UNPKG

@llumiverse/core

Version:

Provide an universal API to LLMs. Support for existing LLMs can be added by writing a driver.

154 lines 5.69 kB
/** * Classes to handle the execution of an interaction in an execution environment. * Base abstract class is then implemented by each environment * (eg: OpenAI, HuggingFace, etc.) */ import { DefaultCompletionStream, FallbackCompletionStream } from "./CompletionStream.js"; import { formatTextPrompt } from "./formatters/index.js"; import { Modalities } from "@llumiverse/common"; import { validateResult } from "./validation.js"; const ConsoleLogger = { debug: console.debug, info: console.info, warn: console.warn, error: console.error, }; const noop = () => void 0; const NoopLogger = { debug: noop, info: noop, warn: noop, error: noop, }; export function createLogger(logger) { if (logger === "console") { return ConsoleLogger; } else if (logger) { return logger; } else { return NoopLogger; } } /** * To be implemented by each driver */ export class AbstractDriver { options; logger; constructor(opts) { this.options = opts; this.logger = createLogger(opts.logger); } async createTrainingPrompt(options) { const prompt = await this.createPrompt(options.segments, { result_schema: options.schema, model: options.model }); return JSON.stringify({ prompt, completion: typeof options.completion === 'string' ? options.completion : JSON.stringify(options.completion) }); } startTraining(_dataset, _options) { throw new Error("Method not implemented."); } cancelTraining(_jobId) { throw new Error("Method not implemented."); } getTrainingJob(_jobId) { throw new Error("Method not implemented."); } validateResult(result, options) { if (!result.tool_use && !result.error && options.result_schema) { try { result.result = validateResult(result.result, options.result_schema); } catch (error) { this.logger?.error({ err: error, data: result.result }, `[${this.provider}] [${options.model}] ${error.code ? '[' + error.code + '] ' : ''}Result validation error: ${error.message}`); result.error = { code: error.code || error.name, message: error.message, data: result.result, }; } } } async execute(segments, options) { const prompt = await this.createPrompt(segments, options); return this._execute(prompt, options).catch((error) => { error.prompt = prompt; throw error; }); } async _execute(prompt, options) { this.logger.debug(`[${this.provider}] Executing prompt on ${options.model}`); try { const start = Date.now(); let result; switch (options.output_modality) { case Modalities.text: result = await this.requestTextCompletion(prompt, options); this.validateResult(result, options); break; case Modalities.image: result = await this.requestImageGeneration(prompt, options); break; default: throw new Error(`Unsupported modality: ${options['output_modality'] ?? "No modality specified"}`); } const execution_time = Date.now() - start; return { ...result, prompt, execution_time }; } catch (error) { error.prompt = prompt; throw error; } } // by default no stream is supported. we block and we return all at once async stream(segments, options) { const prompt = await this.createPrompt(segments, options); const canStream = await this.canStream(options); if (options.output_modality === Modalities.text && canStream) { return new DefaultCompletionStream(this, prompt, options); } else { return new FallbackCompletionStream(this, prompt, options); } } /** * Override this method to provide a custom prompt formatter * @param segments * @param options * @returns */ async formatPrompt(segments, opts) { return formatTextPrompt(segments, opts.result_schema); } async createPrompt(segments, opts) { return await (opts.format ? opts.format(segments, opts.result_schema) : this.formatPrompt(segments, opts)); } /** * Must be overridden if the implementation cannot stream. * Some implementation may be able to stream for certain models but not for others. * You must overwrite and return false if the current model doesn't support streaming. * The default implementation returns true, so it is assumed that the streaming can be done. * If this method returns false then the streaming execution will fallback on a blocking execution streaming the entire response as a single event. * @param options the execution options containing the target model name. * @returns true if the execution can be streamed false otherwise. */ canStream(_options) { return Promise.resolve(true); } /** * Get a list of models that can be trained. * The default is to return an empty array * @returns */ async listTrainableModels() { return []; } async requestImageGeneration(_prompt, _options) { throw new Error("Image generation not implemented."); //Cannot be made abstract, as abstract methods are required in the derived class } } //# sourceMappingURL=Driver.js.map