Rewrite frontend as single self-contained HTML file — all CSS/JS inline, no external files to fail loading

This commit is contained in:
2026-05-13 17:24:10 +02:00
parent 3432d362e2
commit ddb0f88257
116 changed files with 4240 additions and 921 deletions

View File

@@ -0,0 +1,8 @@
import { TTSProvider, TTSProviderConfig, TTSOptions, TTSResult } from '../../interfaces';
export declare class ElevenLabsTTSProvider implements TTSProvider {
private config;
private axiosInstance;
private lastRequestId;
constructor(config: TTSProviderConfig);
textToSpeech(text: string, outputPath: string, options?: TTSOptions): Promise<TTSResult>;
}

View File

@@ -0,0 +1,79 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElevenLabsTTSProvider = void 0;
const fs_1 = __importDefault(require("fs"));
const child_process_1 = require("child_process");
const axios_1 = __importDefault(require("axios"));
const mediaUtils_1 = require("../../utils/mediaUtils");
class ElevenLabsTTSProvider {
constructor(config) {
this.lastRequestId = null;
this.config = config;
this.axiosInstance = axios_1.default.create({
baseURL: 'https://api.elevenlabs.io/v1',
headers: {
'xi-api-key': config.apiKey,
'Content-Type': 'application/json'
}
});
}
async textToSpeech(text, outputPath, options = {}) {
try {
const voice = options.voice || this.config.voice || 'JBFqnCBsd6RMkjVDRZzb';
const model = options.model || this.config.model || 'eleven_multilingual_v2';
const speedFactor = options.speedFactor || 1.0;
const requestBody = {
text,
model_id: model,
voice_settings: {
stability: 0.5,
similarity_boost: 0.75,
speed: speedFactor,
use_speaker_boost: true
}
};
if (this.lastRequestId) {
requestBody.previous_request_ids = [this.lastRequestId];
}
const tempOutputPath = outputPath.replace(/\.\w+$/, '_temp$&');
const response = await this.axiosInstance.post(`/text-to-speech/${voice}`, requestBody, {
params: { output_format: 'mp3_44100_128' },
responseType: 'arraybuffer'
});
this.lastRequestId = response.headers['request-id'] || null;
const audioBuffer = Buffer.from(response.data);
fs_1.default.writeFileSync(tempOutputPath, audioBuffer);
const cost = text.length;
if (speedFactor !== 1.0) {
(0, child_process_1.execSync)(`ffmpeg -v error -i "${tempOutputPath}" -filter:a "atempo=${speedFactor}" -c:a libmp3lame -q:a 2 "${outputPath}" -y`);
fs_1.default.unlinkSync(tempOutputPath);
}
else {
fs_1.default.renameSync(tempOutputPath, outputPath);
}
const audioDuration = (0, mediaUtils_1.getAudioDuration)(outputPath);
return {
duration: audioDuration,
cost: cost
};
}
catch (error) {
if (error.response) {
console.error(`ElevenLabs TTS error (${error.response.status}):`, Buffer.from(error.response.data).toString());
}
else {
console.error('ElevenLabs TTS error:', error.message);
}
(0, child_process_1.execSync)(`ffmpeg -v error -f lavfi -i anullsrc=r=24000:cl=mono -t 1 -q:a 9 -acodec libmp3lame "${outputPath}" -y`);
return {
duration: 1,
cost: 0
};
}
}
}
exports.ElevenLabsTTSProvider = ElevenLabsTTSProvider;
//# sourceMappingURL=elevenLabsTTSProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"elevenLabsTTSProvider.js","sourceRoot":"","sources":["../../../src/providers/tts/elevenLabsTTSProvider.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,iDAAyC;AACzC,kDAA6C;AAE7C,uDAA0D;AAE1D,MAAa,qBAAqB;IAKhC,YAAY,MAAyB;QAF7B,kBAAa,GAAkB,IAAI,CAAC;QAG1C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,eAAK,CAAC,MAAM,CAAC;YAChC,OAAO,EAAE,8BAA8B;YACvC,OAAO,EAAE;gBACP,YAAY,EAAE,MAAM,CAAC,MAAM;gBAC3B,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,IAAY,EACZ,UAAkB,EAClB,UAAsB,EAAE;QAExB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,sBAAsB,CAAC;YAC3E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,wBAAwB,CAAC;YAC7E,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,GAAG,CAAC;YAE/C,MAAM,WAAW,GAAQ;gBACvB,IAAI;gBACJ,QAAQ,EAAE,KAAK;gBACf,cAAc,EAAE;oBACd,SAAS,EAAE,GAAG;oBACd,gBAAgB,EAAE,IAAI;oBACtB,KAAK,EAAE,WAAW;oBAClB,iBAAiB,EAAE,IAAI;iBACxB;aACF,CAAC;YAEF,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,WAAW,CAAC,oBAAoB,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC1D,CAAC;YAED,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAE/D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAC5C,mBAAmB,KAAK,EAAE,EAC1B,WAAW,EACX;gBACE,MAAM,EAAE,EAAE,aAAa,EAAE,eAAe,EAAE;gBAC1C,YAAY,EAAE,aAAa;aAC5B,CACF,CAAC;YAEF,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC;YAE5D,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC/C,YAAE,CAAC,aAAa,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;YAE9C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC;YAEzB,IAAI,WAAW,KAAK,GAAG,EAAE,CAAC;gBACxB,IAAA,wBAAQ,EAAC,uBAAuB,cAAc,uBAAuB,WAAW,6BAA6B,UAAU,MAAM,CAAC,CAAC;gBAC/H,YAAE,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,YAAE,CAAC,UAAU,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;YAC5C,CAAC;YAED,MAAM,aAAa,GAAG,IAAA,6BAAgB,EAAC,UAAU,CAAC,CAAC;YAEnD,OAAO;gBACL,QAAQ,EAAE,aAAa;gBACvB,IAAI,EAAE,IAAI;aACX,CAAC;QACJ,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,OAAO,CAAC,KAAK,CAAC,yBAAyB,KAAK,CAAC,QAAQ,CAAC,MAAM,IAAI,EAC9D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YACjD,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACxD,CAAC;YACD,IAAA,wBAAQ,EAAC,wFAAwF,UAAU,MAAM,CAAC,CAAC;YACnH,OAAO;gBACL,QAAQ,EAAE,CAAC;gBACX,IAAI,EAAE,CAAC;aACR,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAtFD,sDAsFC"}

View File

@@ -0,0 +1,8 @@
import { TTSProvider, TTSProviderConfig, TTSOptions, TTSResult } from '../../interfaces';
export declare class GoogleCloudTTSProvider implements TTSProvider {
private config;
private client;
constructor(config: TTSProviderConfig);
textToSpeech(text: string, outputPath: string, options?: TTSOptions): Promise<TTSResult>;
private extractLanguageCode;
}

View File

@@ -0,0 +1,80 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GoogleCloudTTSProvider = void 0;
const fs_1 = __importDefault(require("fs"));
const child_process_1 = require("child_process");
const text_to_speech_1 = require("@google-cloud/text-to-speech");
const mediaUtils_1 = require("../../utils/mediaUtils");
class GoogleCloudTTSProvider {
constructor(config) {
this.config = config;
const clientConfig = {
apiKey: config.apiKey,
fallback: true
};
if (config.keyFilename) {
clientConfig.keyFilename = config.keyFilename;
}
this.client = new text_to_speech_1.TextToSpeechClient(clientConfig);
}
async textToSpeech(text, outputPath, options = {}) {
try {
const voice = options.voice || this.config.voice || 'en-US-Chirp-HD-F';
const model = options.model || this.config.model || 'chirp-hd';
const speedFactor = options.speedFactor || 1.0;
const request = {
input: { text },
voice: {
languageCode: this.extractLanguageCode(voice),
name: voice
},
audioConfig: {
audioEncoding: 'MP3',
speakingRate: speedFactor
}
};
const [response] = await this.client.synthesizeSpeech(request);
if (!response.audioContent) {
throw new Error('No audio content returned from Google Cloud TTS');
}
const audioBuffer = response.audioContent instanceof Uint8Array
? Buffer.from(response.audioContent)
: Buffer.from(response.audioContent);
const tempOutputPath = outputPath.replace(/\.\w+$/, '_temp$&');
fs_1.default.writeFileSync(tempOutputPath, audioBuffer);
const cost = text.length;
if (speedFactor !== 1.0) {
(0, child_process_1.execSync)(`ffmpeg -v error -i "${tempOutputPath}" -filter:a "atempo=${speedFactor}" -c:a libmp3lame -q:a 2 "${outputPath}" -y`);
fs_1.default.unlinkSync(tempOutputPath);
}
else {
fs_1.default.renameSync(tempOutputPath, outputPath);
}
const audioDuration = (0, mediaUtils_1.getAudioDuration)(outputPath);
return {
duration: audioDuration,
cost: cost
};
}
catch (error) {
console.error('Google Cloud TTS error:', error.message);
(0, child_process_1.execSync)(`ffmpeg -v error -f lavfi -i anullsrc=r=24000:cl=mono -t 1 -q:a 9 -acodec libmp3lame "${outputPath}" -y`);
return {
duration: 1,
cost: 0
};
}
}
extractLanguageCode(voiceName) {
const parts = voiceName.split('-');
if (parts.length >= 2) {
return `${parts[0]}-${parts[1]}`;
}
return 'en-US';
}
}
exports.GoogleCloudTTSProvider = GoogleCloudTTSProvider;
//# sourceMappingURL=googleCloudTTSProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"googleCloudTTSProvider.js","sourceRoot":"","sources":["../../../src/providers/tts/googleCloudTTSProvider.ts"],"names":[],"mappings":"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"}

4
dist/providers/tts/index.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
export * from './ttsProviderFactory';
export * from './openAITTSProvider';
export * from './elevenLabsTTSProvider';
export * from './googleCloudTTSProvider';

21
dist/providers/tts/index.js vendored Normal file
View File

@@ -0,0 +1,21 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./ttsProviderFactory"), exports);
__exportStar(require("./openAITTSProvider"), exports);
__exportStar(require("./elevenLabsTTSProvider"), exports);
__exportStar(require("./googleCloudTTSProvider"), exports);
//# sourceMappingURL=index.js.map

1
dist/providers/tts/index.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/tts/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uDAAqC;AACrC,sDAAoC;AACpC,0DAAwC;AACxC,2DAAyC"}

View File

@@ -0,0 +1,17 @@
import { TTSProvider, TTSProviderConfig, TTSOptions, TTSResult } from '../../interfaces';
/**
* OpenAI TTS Provider Implementation
*/
export declare class OpenAITTSProvider implements TTSProvider {
private config;
private openai;
constructor(config: TTSProviderConfig);
/**
* Convert text to speech
* @param text - Text to convert to speech
* @param outputPath - Output path for the audio file
* @param options - Additional options
* @returns Duration of the generated audio in seconds and cost
*/
textToSpeech(text: string, outputPath: string, options?: TTSOptions): Promise<TTSResult>;
}

75
dist/providers/tts/openAITTSProvider.js vendored Normal file
View File

@@ -0,0 +1,75 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenAITTSProvider = void 0;
const fs_1 = __importDefault(require("fs"));
const child_process_1 = require("child_process");
const openai_1 = require("openai");
const mediaUtils_1 = require("../../utils/mediaUtils");
/**
* OpenAI TTS Provider Implementation
*/
class OpenAITTSProvider {
constructor(config) {
this.config = config;
this.openai = new openai_1.OpenAI({
apiKey: config.apiKey,
});
}
/**
* Convert text to speech
* @param text - Text to convert to speech
* @param outputPath - Output path for the audio file
* @param options - Additional options
* @returns Duration of the generated audio in seconds and cost
*/
async textToSpeech(text, outputPath, options = {}) {
try {
// Get the options, with defaults from config
const voice = options.voice || this.config.voice;
const model = options.model || this.config.model;
const speedFactor = options.speedFactor || 1.0;
// Generate the initial TTS output
const tempOutputPath = outputPath.replace(/\.\w+$/, '_temp$&');
const mp3 = await this.openai.audio.speech.create({
model: model,
voice: voice,
input: text,
...(options.instructions ? { instructions: options.instructions } : {})
});
// Cost calculation is based on character count
const cost = text.length;
const buffer = Buffer.from(await mp3.arrayBuffer());
fs_1.default.writeFileSync(tempOutputPath, buffer);
// Speed up the audio using FFmpeg if needed
if (speedFactor !== 1.0) {
(0, child_process_1.execSync)(`ffmpeg -v error -i "${tempOutputPath}" -filter:a "atempo=${speedFactor}" -c:a libmp3lame -q:a 2 "${outputPath}" -y`);
// Clean up temporary file
fs_1.default.unlinkSync(tempOutputPath);
}
else {
// Just use the file as is
fs_1.default.renameSync(tempOutputPath, outputPath);
}
// Get actual audio duration for accurate timing
const audioDuration = (0, mediaUtils_1.getAudioDuration)(outputPath);
return {
duration: audioDuration,
cost: cost
};
}
catch (error) {
console.error("Error generating speech:", error);
// Create a silent audio file if TTS fails
(0, child_process_1.execSync)(`ffmpeg -v error -f lavfi -i anullsrc=r=24000:cl=mono -t 1 -q:a 9 -acodec libmp3lame "${outputPath}" -y`);
return {
duration: 1,
cost: 0
};
}
}
}
exports.OpenAITTSProvider = OpenAITTSProvider;
//# sourceMappingURL=openAITTSProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"openAITTSProvider.js","sourceRoot":"","sources":["../../../src/providers/tts/openAITTSProvider.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,iDAAyC;AACzC,mCAAgC;AAEhC,uDAA0D;AAE1D;;GAEG;AACH,MAAa,iBAAiB;IAI5B,YAAY,MAAyB;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,IAAI,eAAM,CAAC;YACvB,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,YAAY,CAChB,IAAY,EACZ,UAAkB,EAClB,UAAsB,EAAE;QAExB,IAAI,CAAC;YACH,6CAA6C;YAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACjD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,GAAG,CAAC;YAE/C,kCAAkC;YAClC,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAE/D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;gBAChD,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,KAAY;gBACnB,KAAK,EAAE,IAAI;gBACX,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxE,CAAC,CAAC;YAEH,+CAA+C;YAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC;YAEzB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;YACpD,YAAE,CAAC,aAAa,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;YAEzC,4CAA4C;YAC5C,IAAI,WAAW,KAAK,GAAG,EAAE,CAAC;gBACxB,IAAA,wBAAQ,EAAC,uBAAuB,cAAc,uBAAuB,WAAW,6BAA6B,UAAU,MAAM,CAAC,CAAC;gBAC/H,0BAA0B;gBAC1B,YAAE,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,0BAA0B;gBAC1B,YAAE,CAAC,UAAU,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;YAC5C,CAAC;YAED,gDAAgD;YAChD,MAAM,aAAa,GAAG,IAAA,6BAAgB,EAAC,UAAU,CAAC,CAAC;YAEnD,OAAO;gBACL,QAAQ,EAAE,aAAa;gBACvB,IAAI,EAAE,IAAI;aACX,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;YACjD,0CAA0C;YAC1C,IAAA,wBAAQ,EAAC,wFAAwF,UAAU,MAAM,CAAC,CAAC;YACnH,OAAO;gBACL,QAAQ,EAAE,CAAC;gBACX,IAAI,EAAE,CAAC;aACR,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAxED,8CAwEC"}

View File

@@ -0,0 +1,8 @@
import { TTSProvider } from '../../interfaces';
import { Config } from '../../config/config';
/**
* Factory for creating TTS providers
*/
export declare class TTSProviderFactory {
static getProvider(config: Config): TTSProvider;
}

View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TTSProviderFactory = void 0;
const openAITTSProvider_1 = require("./openAITTSProvider");
const elevenLabsTTSProvider_1 = require("./elevenLabsTTSProvider");
const googleCloudTTSProvider_1 = require("./googleCloudTTSProvider");
/**
* Factory for creating TTS providers
*/
class TTSProviderFactory {
static getProvider(config) {
const providerName = config.ttsProvider;
const providerConfig = config.ttsProviders[providerName];
if (!providerConfig) {
throw new Error(`TTS provider "${providerName}" not configured.`);
}
switch (providerName) {
case 'openai':
return new openAITTSProvider_1.OpenAITTSProvider(providerConfig);
case 'elevenlabs':
return new elevenLabsTTSProvider_1.ElevenLabsTTSProvider(providerConfig);
case 'google':
return new googleCloudTTSProvider_1.GoogleCloudTTSProvider(providerConfig);
// Add other providers here
default:
throw new Error(`TTS provider "${providerName}" not implemented.`);
}
}
}
exports.TTSProviderFactory = TTSProviderFactory;
//# sourceMappingURL=ttsProviderFactory.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ttsProviderFactory.js","sourceRoot":"","sources":["../../../src/providers/tts/ttsProviderFactory.ts"],"names":[],"mappings":";;;AAEA,2DAAwD;AACxD,mEAAgE;AAChE,qEAAkE;AAElE;;GAEG;AACH,MAAa,kBAAkB;IAC7B,MAAM,CAAC,WAAW,CAAC,MAAc;QAC/B,MAAM,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC;QACxC,MAAM,cAAc,GAAG,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QAEzD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,iBAAiB,YAAY,mBAAmB,CAAC,CAAC;QACpE,CAAC;QAED,QAAQ,YAAY,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,OAAO,IAAI,qCAAiB,CAAC,cAAc,CAAC,CAAC;YAC/C,KAAK,YAAY;gBACf,OAAO,IAAI,6CAAqB,CAAC,cAAc,CAAC,CAAC;YACnD,KAAK,QAAQ;gBACX,OAAO,IAAI,+CAAsB,CAAC,cAAc,CAAC,CAAC;YACpD,2BAA2B;YAC3B;gBACE,MAAM,IAAI,KAAK,CAAC,iBAAiB,YAAY,oBAAoB,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;CACF;AArBD,gDAqBC"}

View File

@@ -0,0 +1,33 @@
import { VisionProvider, VisionProviderConfig, VisionResult, BatchContext } from '../../interfaces';
/**
* Google Gemini Vision Provider Implementation
*/
export declare class GeminiVisionProvider implements VisionProvider {
private config;
private genAI;
private model;
constructor(config: VisionProviderConfig);
/**
* Describe a single image
* @param imagePath - Path to the image file
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
describeImage(imagePath: string, prompt: string): Promise<VisionResult>;
/**
* Compare two images and describe the differences
* @param image1Path - Path to the first image
* @param image2Path - Path to the second image
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
compareImages(image1Path: string, image2Path: string, prompt: string): Promise<VisionResult>;
/**
* Describe a batch of images
* @param imagePaths - Array of paths to the images
* @param lastBatchContext - Context from the previous batch
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
describeBatch(imagePaths: string[], lastBatchContext: BatchContext, prompt: string): Promise<VisionResult>;
}

View File

@@ -0,0 +1,162 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeminiVisionProvider = void 0;
const fs_1 = __importDefault(require("fs"));
const generative_ai_1 = require("@google/generative-ai");
/**
* Google Gemini Vision Provider Implementation
*/
class GeminiVisionProvider {
constructor(config) {
this.config = config;
this.genAI = new generative_ai_1.GoogleGenerativeAI(config.apiKey);
this.model = this.genAI.getGenerativeModel({ model: config.model });
}
/**
* Describe a single image
* @param imagePath - Path to the image file
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async describeImage(imagePath, prompt) {
try {
const imageData = fs_1.default.readFileSync(imagePath);
const mimeType = 'image/jpeg'; // Assuming JPEG, could be detected based on file extension
// Create a file part for the image
const imagePart = {
inlineData: {
data: imageData.toString('base64'),
mimeType
}
};
// Generate content using Gemini
const result = await this.model.generateContent([prompt, imagePart]);
const response = await result.response;
const text = response.text();
// Gemini doesn't provide token usage information in the same way as OpenAI
// We'll estimate based on prompt length and response length
const inputTokens = Math.ceil(prompt.length / 4) + 1000; // rough estimate for image
const outputTokens = Math.ceil(text.length / 4);
return {
description: text,
usage: {
inputTokens,
outputTokens,
totalTokens: inputTokens + outputTokens
}
};
}
catch (error) {
console.error("Error describing image with Gemini:", error);
return {
description: "Unable to describe this image.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
/**
* Compare two images and describe the differences
* @param image1Path - Path to the first image
* @param image2Path - Path to the second image
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async compareImages(image1Path, image2Path, prompt) {
try {
const image1Data = fs_1.default.readFileSync(image1Path);
const image2Data = fs_1.default.readFileSync(image2Path);
const mimeType = 'image/jpeg'; // Assuming JPEG, could be detected based on file extension
// Create file parts for both images
const image1Part = {
inlineData: {
data: image1Data.toString('base64'),
mimeType
}
};
const image2Part = {
inlineData: {
data: image2Data.toString('base64'),
mimeType
}
};
// Generate content using Gemini with both images
const result = await this.model.generateContent([prompt, image1Part, image2Part]);
const response = await result.response;
const text = response.text();
// Estimate token usage
const inputTokens = Math.ceil(prompt.length / 4) + 2000; // rough estimate for two images
const outputTokens = Math.ceil(text.length / 4);
return {
description: text,
usage: {
inputTokens,
outputTokens,
totalTokens: inputTokens + outputTokens
}
};
}
catch (error) {
console.error("Error comparing images with Gemini:", error);
return {
description: "Unable to describe the differences between these images.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
/**
* Describe a batch of images
* @param imagePaths - Array of paths to the images
* @param lastBatchContext - Context from the previous batch
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async describeBatch(imagePaths, lastBatchContext, prompt) {
try {
// Create a prompt that includes context from the last batch if available
let contextualPrompt = prompt;
if (lastBatchContext && lastBatchContext.lastDescription) {
contextualPrompt = `Previous batch summary: ${lastBatchContext.lastDescription}\n\n${prompt}`;
}
// Create content parts array starting with the prompt
const contentParts = [contextualPrompt];
// Add all images to the content parts
for (const imagePath of imagePaths) {
const imageData = fs_1.default.readFileSync(imagePath);
const mimeType = 'image/jpeg'; // Assuming JPEG, could be detected based on file extension
contentParts.push({
inlineData: {
data: imageData.toString('base64'),
mimeType
}
});
}
// Generate content using Gemini with all images
const result = await this.model.generateContent(contentParts);
const response = await result.response;
const text = response.text();
// Estimate token usage
const inputTokens = Math.ceil(contextualPrompt.length / 4) + (1000 * imagePaths.length); // rough estimate
const outputTokens = Math.ceil(text.length / 4);
return {
description: text,
usage: {
inputTokens,
outputTokens,
totalTokens: inputTokens + outputTokens
}
};
}
catch (error) {
console.error("Error describing batch of images with Gemini:", error);
return {
description: "Unable to describe this batch of images.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
}
exports.GeminiVisionProvider = GeminiVisionProvider;
//# sourceMappingURL=geminiVisionProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"geminiVisionProvider.js","sourceRoot":"","sources":["../../../src/providers/vision/geminiVisionProvider.ts"],"names":[],"mappings":"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"}

5
dist/providers/vision/index.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
export * from './visionProviderFactory';
export * from './openAIVisionProvider';
export * from './geminiVisionProvider';
export * from './ollamaVisionProvider';
export * from './openRouterVisionProvider';

22
dist/providers/vision/index.js vendored Normal file
View File

@@ -0,0 +1,22 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./visionProviderFactory"), exports);
__exportStar(require("./openAIVisionProvider"), exports);
__exportStar(require("./geminiVisionProvider"), exports);
__exportStar(require("./ollamaVisionProvider"), exports);
__exportStar(require("./openRouterVisionProvider"), exports);
//# sourceMappingURL=index.js.map

1
dist/providers/vision/index.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/vision/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,yDAAuC;AACvC,yDAAuC;AACvC,yDAAuC;AACvC,6DAA2C"}

View File

@@ -0,0 +1,33 @@
import { VisionProvider, VisionProviderConfig, VisionResult, BatchContext } from '../../interfaces';
/**
* Ollama Vision Provider Implementation
* See: https://github.com/ollama/ollama/blob/main/docs/api.md
*/
export declare class OllamaVisionProvider implements VisionProvider {
private config;
private axiosInstance;
constructor(config: VisionProviderConfig);
/**
* Describe a single image
* @param imagePath - Path to the image file
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
describeImage(imagePath: string, prompt: string): Promise<VisionResult>;
/**
* Compare two images and describe differences
* @param image1Path - Path to the first image
* @param image2Path - Path to the second image
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
compareImages(image1Path: string, image2Path: string, prompt: string): Promise<VisionResult>;
/**
* Describe a batch of images
* @param imagePaths - Array of paths to the images
* @param lastBatchContext - Context from the previous batch (optional)
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
describeBatch(imagePaths: string[], lastBatchContext: BatchContext, prompt: string): Promise<VisionResult>;
}

View File

@@ -0,0 +1,141 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OllamaVisionProvider = void 0;
const fs_1 = __importDefault(require("fs"));
const axios_1 = __importDefault(require("axios"));
/**
* Ollama Vision Provider Implementation
* See: https://github.com/ollama/ollama/blob/main/docs/api.md
*/
class OllamaVisionProvider {
constructor(config) {
this.config = config;
this.axiosInstance = axios_1.default.create({
baseURL: config.baseUrl || "http://localhost:11434",
headers: { "Content-Type": "application/json" }
});
}
/**
* Describe a single image
* @param imagePath - Path to the image file
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async describeImage(imagePath, prompt) {
try {
const imageData = fs_1.default.readFileSync(imagePath);
const base64Image = imageData.toString('base64');
const response = await this.axiosInstance.post('/api/generate', {
model: this.config.model,
prompt: prompt,
images: [base64Image],
stream: false,
options: {
max_tokens: this.config.maxTokens || 300,
temperature: 0.1
}
});
const combinedText = response.data.response || "";
return {
description: combinedText.trim(),
usage: {
inputTokens: 0,
outputTokens: 0,
totalTokens: 0
}
};
}
catch (error) {
console.error("Ollama describeImage error:", error);
return {
description: "Unable to describe this image.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
/**
* Compare two images and describe differences
* @param image1Path - Path to the first image
* @param image2Path - Path to the second image
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async compareImages(image1Path, image2Path, prompt) {
try {
const image1Data = fs_1.default.readFileSync(image1Path).toString('base64');
const image2Data = fs_1.default.readFileSync(image2Path).toString('base64');
const response = await this.axiosInstance.post('/api/generate', {
model: this.config.model,
prompt: prompt,
images: [image1Data, image2Data],
stream: false,
options: {
max_tokens: this.config.maxTokens || 300,
temperature: 0.2
}
});
const combinedText = response.data.response || "";
return {
description: combinedText.trim(),
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
catch (error) {
console.error("Ollama compareImages error:", error);
return {
description: "Unable to describe the differences.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
/**
* Describe a batch of images
* @param imagePaths - Array of paths to the images
* @param lastBatchContext - Context from the previous batch (optional)
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async describeBatch(imagePaths, lastBatchContext, prompt) {
try {
let userPrompt = prompt;
// If there's context, prepend it. This helps maintain a storyline across batches.
if (lastBatchContext && lastBatchContext.lastDescription) {
userPrompt = `Previous batch summary: ${lastBatchContext.lastDescription}\n\n${prompt}`;
}
// Convert images to base64
const imagesBase64 = imagePaths.map(fp => {
const imageData = fs_1.default.readFileSync(fp);
return imageData.toString('base64');
});
const response = await this.axiosInstance.post('/api/generate', {
model: this.config.model,
prompt: userPrompt,
images: imagesBase64,
stream: false,
options: {
max_tokens: this.config.maxTokens || 300,
temperature: 0.2
}
}, {
timeout: 120000 // Timeout in milliseconds, e.g., 5000 ms = 5 seconds
});
const combinedText = response.data.response || "";
return {
description: combinedText.trim(),
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
catch (error) {
console.error("Ollama describeBatch error:", error);
return {
description: "Unable to describe this batch of images.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
}
exports.OllamaVisionProvider = OllamaVisionProvider;
//# sourceMappingURL=ollamaVisionProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ollamaVisionProvider.js","sourceRoot":"","sources":["../../../src/providers/vision/ollamaVisionProvider.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,32 @@
import { VisionProvider, VisionProviderConfig, VisionResult, BatchContext } from '../../interfaces';
/**
* OpenAI Vision Provider Implementation
*/
export declare class OpenAIVisionProvider implements VisionProvider {
private config;
private openai;
constructor(config: VisionProviderConfig);
/**
* Describe a single image
* @param imagePath - Path to the image file
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
describeImage(imagePath: string, prompt: string): Promise<VisionResult>;
/**
* Compare two images and describe the differences
* @param image1Path - Path to the first image
* @param image2Path - Path to the second image
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
compareImages(image1Path: string, image2Path: string, prompt: string): Promise<VisionResult>;
/**
* Describe a batch of images
* @param imagePaths - Array of paths to the images
* @param lastBatchContext - Context from the previous batch
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
describeBatch(imagePaths: string[], lastBatchContext: BatchContext, prompt: string): Promise<VisionResult>;
}

View File

@@ -0,0 +1,182 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenAIVisionProvider = void 0;
const fs_1 = __importDefault(require("fs"));
const openai_1 = require("openai");
/**
* OpenAI Vision Provider Implementation
*/
class OpenAIVisionProvider {
constructor(config) {
this.config = config;
this.openai = new openai_1.OpenAI({
apiKey: config.apiKey,
});
}
/**
* Describe a single image
* @param imagePath - Path to the image file
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async describeImage(imagePath, prompt) {
try {
const imageData = fs_1.default.readFileSync(imagePath);
const base64Image = imageData.toString('base64');
const response = await this.openai.chat.completions.create({
model: this.config.model,
temperature: 0.1,
messages: [
{
role: "user",
content: [
{ type: "text", text: prompt },
{
type: "image_url",
image_url: {
url: `data:image/jpeg;base64,${base64Image}`
}
}
]
}
],
max_completion_tokens: this.config.maxTokens || 300
});
return {
description: response.choices[0].message.content?.trim() || "No description generated.",
usage: {
inputTokens: response.usage?.prompt_tokens || 0,
outputTokens: response.usage?.completion_tokens || 0,
totalTokens: response.usage?.total_tokens || 0
}
};
}
catch (error) {
console.error("Error describing image:", error);
return {
description: "Unable to describe this image.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
/**
* Compare two images and describe the differences
* @param image1Path - Path to the first image
* @param image2Path - Path to the second image
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async compareImages(image1Path, image2Path, prompt) {
try {
const image1Data = fs_1.default.readFileSync(image1Path);
const image2Data = fs_1.default.readFileSync(image2Path);
const base64Image1 = image1Data.toString('base64');
const base64Image2 = image2Data.toString('base64');
const response = await this.openai.chat.completions.create({
model: this.config.model,
messages: [
{
role: "user",
content: [
{ type: "text", text: prompt },
{
type: "image_url",
image_url: {
url: `data:image/jpeg;base64,${base64Image1}`
}
},
{
type: "image_url",
image_url: {
url: `data:image/jpeg;base64,${base64Image2}`
}
}
]
}
],
max_completion_tokens: this.config.maxTokens || 300
});
return {
description: response.choices[0].message.content?.trim() || "No description generated.",
usage: {
inputTokens: response.usage?.prompt_tokens || 0,
outputTokens: response.usage?.completion_tokens || 0,
totalTokens: response.usage?.total_tokens || 0
}
};
}
catch (error) {
console.error("Error comparing images:", error);
return {
description: "Unable to describe the differences between these images.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
/**
* Describe a batch of images
* @param imagePaths - Array of paths to the images
* @param lastBatchContext - Context from the previous batch
* @param prompt - Prompt for the AI
* @returns Description and usage stats
*/
async describeBatch(imagePaths, lastBatchContext, prompt) {
try {
// Convert images to base64
const imagesBase64 = imagePaths.map(fp => {
const imageData = fs_1.default.readFileSync(fp);
return imageData.toString('base64');
});
// Build the messages array for the chat completion
const messages = [
{
role: "user",
content: [
{ type: "text", text: prompt }
]
}
];
// If we have some text context from the last batch, inject that as well
if (lastBatchContext && lastBatchContext.lastDescription) {
messages.unshift({
role: "system",
content: `Previous batch summary: ${lastBatchContext.lastDescription}`
});
}
// Append each image in the new batch
imagesBase64.forEach(base64 => {
messages[messages.length - 1].content.push({
type: "image_url",
image_url: {
url: `data:image/jpeg;base64,${base64}`
}
});
});
const response = await this.openai.chat.completions.create({
model: this.config.model,
messages,
max_completion_tokens: this.config.maxTokens || 300
});
return {
description: response.choices[0].message.content?.trim() || "No description generated.",
usage: {
inputTokens: response.usage?.prompt_tokens || 0,
outputTokens: response.usage?.completion_tokens || 0,
totalTokens: response.usage?.total_tokens || 0
}
};
}
catch (error) {
console.error("Error describing batch of images:", error);
return {
description: "Unable to describe this batch of images.",
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
}
exports.OpenAIVisionProvider = OpenAIVisionProvider;
//# sourceMappingURL=openAIVisionProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"openAIVisionProvider.js","sourceRoot":"","sources":["../../../src/providers/vision/openAIVisionProvider.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,9 @@
import { VisionProvider, VisionProviderConfig, VisionResult, BatchContext } from '../../interfaces';
export declare class OpenRouterVisionProvider implements VisionProvider {
private config;
private axiosInstance;
constructor(config: VisionProviderConfig);
describeImage(imagePath: string, prompt: string): Promise<VisionResult>;
compareImages(image1Path: string, image2Path: string, prompt: string): Promise<VisionResult>;
describeBatch(imagePaths: string[], lastBatchContext: BatchContext, prompt: string): Promise<VisionResult>;
}

View File

@@ -0,0 +1,161 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenRouterVisionProvider = void 0;
const fs_1 = __importDefault(require("fs"));
const axios_1 = __importDefault(require("axios"));
class OpenRouterVisionProvider {
constructor(config) {
this.config = config;
this.axiosInstance = axios_1.default.create({
baseURL: config.baseUrl || 'https://openrouter.ai/api/v1',
headers: {
'Authorization': `Bearer ${config.apiKey}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://github.com/anomalyco/aidio-description',
'X-Title': 'Aidio Description Generator'
}
});
}
async describeImage(imagePath, prompt) {
try {
const imageData = fs_1.default.readFileSync(imagePath);
const base64Image = imageData.toString('base64');
const response = await this.axiosInstance.post('/chat/completions', {
model: this.config.model,
temperature: 0.1,
messages: [
{
role: 'user',
content: [
{ type: 'text', text: prompt },
{
type: 'image_url',
image_url: {
url: `data:image/jpeg;base64,${base64Image}`
}
}
]
}
],
max_tokens: this.config.maxTokens || 300
});
const data = response.data;
return {
description: data.choices?.[0]?.message?.content?.trim() || 'No description generated.',
usage: {
inputTokens: data.usage?.prompt_tokens || 0,
outputTokens: data.usage?.completion_tokens || 0,
totalTokens: data.usage?.total_tokens || 0
}
};
}
catch (error) {
console.error('OpenRouter describeImage error:', error.response?.data || error.message);
return {
description: 'Unable to describe this image.',
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
async compareImages(image1Path, image2Path, prompt) {
try {
const image1Data = fs_1.default.readFileSync(image1Path);
const image2Data = fs_1.default.readFileSync(image2Path);
const base64Image1 = image1Data.toString('base64');
const base64Image2 = image2Data.toString('base64');
const response = await this.axiosInstance.post('/chat/completions', {
model: this.config.model,
temperature: 0.1,
messages: [
{
role: 'user',
content: [
{ type: 'text', text: prompt },
{
type: 'image_url',
image_url: { url: `data:image/jpeg;base64,${base64Image1}` }
},
{
type: 'image_url',
image_url: { url: `data:image/jpeg;base64,${base64Image2}` }
}
]
}
],
max_tokens: this.config.maxTokens || 300
});
const data = response.data;
return {
description: data.choices?.[0]?.message?.content?.trim() || 'No description generated.',
usage: {
inputTokens: data.usage?.prompt_tokens || 0,
outputTokens: data.usage?.completion_tokens || 0,
totalTokens: data.usage?.total_tokens || 0
}
};
}
catch (error) {
console.error('OpenRouter compareImages error:', error.response?.data || error.message);
return {
description: 'Unable to describe the differences between these images.',
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
async describeBatch(imagePaths, lastBatchContext, prompt) {
try {
const imagesBase64 = imagePaths.map(fp => {
const imageData = fs_1.default.readFileSync(fp);
return imageData.toString('base64');
});
const messages = [
{
role: 'user',
content: [
{ type: 'text', text: prompt }
]
}
];
if (lastBatchContext && lastBatchContext.lastDescription) {
messages.unshift({
role: 'system',
content: `Previous batch summary: ${lastBatchContext.lastDescription}`
});
}
imagesBase64.forEach(base64 => {
messages[messages.length - 1].content.push({
type: 'image_url',
image_url: {
url: `data:image/jpeg;base64,${base64}`
}
});
});
const response = await this.axiosInstance.post('/chat/completions', {
model: this.config.model,
messages,
max_tokens: this.config.maxTokens || 300
});
const data = response.data;
return {
description: data.choices?.[0]?.message?.content?.trim() || 'No description generated.',
usage: {
inputTokens: data.usage?.prompt_tokens || 0,
outputTokens: data.usage?.completion_tokens || 0,
totalTokens: data.usage?.total_tokens || 0
}
};
}
catch (error) {
console.error('OpenRouter describeBatch error:', error.response?.data || error.message);
return {
description: 'Unable to describe this batch of images.',
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
};
}
}
}
exports.OpenRouterVisionProvider = OpenRouterVisionProvider;
//# sourceMappingURL=openRouterVisionProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"openRouterVisionProvider.js","sourceRoot":"","sources":["../../../src/providers/vision/openRouterVisionProvider.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,8 @@
import { VisionProvider } from '../../interfaces';
import { Config } from '../../config/config';
/**
* Factory for creating vision AI providers
*/
export declare class VisionProviderFactory {
static getProvider(config: Config): VisionProvider;
}

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VisionProviderFactory = void 0;
const openAIVisionProvider_1 = require("./openAIVisionProvider");
const geminiVisionProvider_1 = require("./geminiVisionProvider");
const ollamaVisionProvider_1 = require("./ollamaVisionProvider");
const openRouterVisionProvider_1 = require("./openRouterVisionProvider");
/**
* Factory for creating vision AI providers
*/
class VisionProviderFactory {
static getProvider(config) {
const providerName = config.visionProvider;
const providerConfig = config.visionProviders[providerName];
if (!providerConfig) {
throw new Error(`Vision provider "${providerName}" not configured.`);
}
switch (providerName) {
case 'openai':
return new openAIVisionProvider_1.OpenAIVisionProvider(providerConfig);
case 'gemini':
return new geminiVisionProvider_1.GeminiVisionProvider(providerConfig);
case "ollama":
return new ollamaVisionProvider_1.OllamaVisionProvider(providerConfig);
case 'openrouter':
return new openRouterVisionProvider_1.OpenRouterVisionProvider(providerConfig);
// Add other providers here
default:
throw new Error(`Vision provider "${providerName}" not implemented.`);
}
}
}
exports.VisionProviderFactory = VisionProviderFactory;
//# sourceMappingURL=visionProviderFactory.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"visionProviderFactory.js","sourceRoot":"","sources":["../../../src/providers/vision/visionProviderFactory.ts"],"names":[],"mappings":";;;AAEA,iEAA8D;AAC9D,iEAA8D;AAC9D,iEAA8D;AAC9D,yEAAsE;AAEtE;;GAEG;AACH,MAAa,qBAAqB;IAChC,MAAM,CAAC,WAAW,CAAC,MAAc;QAC/B,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC;QAC3C,MAAM,cAAc,GAAG,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QAE5D,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,oBAAoB,YAAY,mBAAmB,CAAC,CAAC;QACvE,CAAC;QAED,QAAQ,YAAY,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,OAAO,IAAI,2CAAoB,CAAC,cAAc,CAAC,CAAC;YAClD,KAAK,QAAQ;gBACX,OAAO,IAAI,2CAAoB,CAAC,cAAc,CAAC,CAAC;YAClD,KAAK,QAAQ;gBACX,OAAO,IAAI,2CAAoB,CAAC,cAAc,CAAC,CAAC;YAClD,KAAK,YAAY;gBACf,OAAO,IAAI,mDAAwB,CAAC,cAAc,CAAC,CAAC;YACtD,2BAA2B;YAC3B;gBACE,MAAM,IAAI,KAAK,CAAC,oBAAoB,YAAY,oBAAoB,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;CACF;AAvBD,sDAuBC"}