UNPKG

@naktibalda/jorel

Version:

The easiest way to use LLMs, including streams, images, documents, tools and various agent scenarios.

447 lines (446 loc) 20.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JorEl = void 0; const documents_1 = require("../documents"); const providers_1 = require("../providers"); const shared_1 = require("../shared"); const tools_1 = require("../tools"); const jorel_core_1 = require("./jorel.core"); const jorel_team_1 = require("./jorel.team"); /** * Jor-El: Singular interface for managing multiple LLM providers and models */ class JorEl { /** * System message use for all requests by default (unless specified per request) */ systemMessage; /** * Agent related functionality */ team; /** * Core store for managing providers * @internal */ _core; /** * Public methods for managing models */ models = { list: () => this._core.modelManager.listModels(), register: (params) => { this._core.providerManager.getProvider(params.provider); // Ensure provider exists return this._core.modelManager.registerModel(params); }, unregister: (model) => this._core.modelManager.unregisterModel(model), getDefault: () => this._core.modelManager.getDefaultModel(), setDefault: (model) => this._core.modelManager.registerModel({ model, provider: "", setAsDefault: true, }), embeddings: { register: (params) => this._core.modelManager.registerEmbeddingModel(params), unregister: (model) => this._core.modelManager.unregisterEmbeddingModel(model), getDefault: () => this._core.modelManager.getDefaultEmbeddingModel(), setDefault: (model) => this._core.modelManager.setDefaultEmbeddingModel(model), list: () => this._core.modelManager.listEmbeddingModels(), }, }; /** * Public methods for managing providers */ providers = { list: () => this._core.providerManager.listProviders(), registerCustom: (provider, coreProvider) => this._core.providerManager.registerProvider(provider, coreProvider), registerAnthropic: (config) => { this._core.providerManager.registerProvider("anthropic", new providers_1.AnthropicProvider(config)); const defaultModels = config?.bedrock ? providers_1.defaultAnthropicBedrockModels : providers_1.defaultAnthropicModels; for (const model of defaultModels) { this.models.register({ model, provider: "anthropic" }); } }, registerGoogleGenAi: (config) => { this._core.providerManager.registerProvider("google-gen-ai", new providers_1.GoogleGenerativeAIProvider(config)); for (const model of providers_1.defaultGoogleGenAiModels) { this.models.register({ model, provider: "google-gen-ai" }); } }, registerGrok: (config) => { this._core.providerManager.registerProvider("grok", new providers_1.GrokProvider(config)); for (const model of providers_1.defaultGrokModels) { this.models.register({ model, provider: "grok" }); } }, registerGroq: (config) => { this._core.providerManager.registerProvider("groq", new providers_1.GroqProvider(config)); for (const model of providers_1.defaultGroqModels) { this.models.register({ model, provider: "groq" }); } }, registerMistral: (config) => { this._core.providerManager.registerProvider("mistral", new providers_1.MistralProvider(config)); for (const model of providers_1.defaultMistralAiModels) { this.models.register({ model, provider: "mistral" }); } for (const { model, dimensions } of providers_1.defaultMistralAiEmbeddingModels) { this.models.embeddings.register({ model, dimensions, provider: "mistral" }); } }, registerOllama: (config) => { this._core.providerManager.registerProvider("ollama", new providers_1.OllamaProvider(config)); }, registerOpenAi: (config) => { this._core.providerManager.registerProvider("openai", new providers_1.OpenAIProvider(config)); for (const model of providers_1.defaultOpenAiModels) { this.models.register({ model, provider: "openai" }); } for (const { model, dimensions } of providers_1.defaultOpenAiEmbeddingModels) { this.models.embeddings.register({ model, dimensions, provider: "openai" }); } }, registerOpenRouter: (config) => { this._core.providerManager.registerProvider("open-router", new providers_1.OpenRouterProvider(config)); for (const model of providers_1.defaultOpenRouterModels) { this.models.register({ model, provider: "open-router" }); } }, registerGoogleVertexAi: (config) => { this._core.providerManager.registerProvider("google-vertex-ai", new providers_1.GoogleVertexAiProvider(config)); for (const model of providers_1.defaultVertexAiModels) { this.models.register({ model, provider: "google-vertex-ai" }); } }, anthropic: { addModel: (model) => this.models.register({ model, provider: "anthropic" }), getClient: () => this._core.providerManager.getProvider("anthropic").client, }, googleGenAi: { addModel: (model) => this.models.register({ model, provider: "google-gen-ai" }), getClient: () => this._core.providerManager.getProvider("google-gen-ai").client, }, grok: { addModel: (model) => this.models.register({ model, provider: "grok" }), getClient: () => this._core.providerManager.getProvider("grok").client, }, groq: { addModel: (model) => this.models.register({ model, provider: "groq" }), getClient: () => this._core.providerManager.getProvider("groq").client, }, mistral: { addModel: (model) => this.models.register({ model, provider: "mistral" }), getClient: () => this._core.providerManager.getProvider("mistral").client, }, openAi: { addModel: (model) => this.models.register({ model, provider: "openai" }), getClient: () => this._core.providerManager.getProvider("openai").client, }, openRouter: { addModel: (model) => this.models.register({ model, provider: "open-router" }), getClient: () => this._core.providerManager.getProvider("open-router").client, }, vertexAi: { addModel: (model) => this.models.register({ model, provider: "google-vertex-ai" }), getClient: () => this._core.providerManager.getProvider("google-vertex-ai").client, }, }; /** * Create a new Jor-El instance. * * @param config - The configuration for the Jor-El instance. * @param config.anthropic - Anthropic configuration (optional). * @param config.googleGenAi - Google Generative AI configuration (optional). * @param config.grok - Grok configuration (optional). * @param config.groq - Groq configuration (optional). * @param config.vertexAi - Google Vertex AI configuration (optional). * @param config.ollama - Ollama configuration (optional). * @param config.openAI - OpenAI configuration (optional). * @param config.openRouter - OpenRouter configuration (optional). * @param config.systemMessage - System message to include in all requests (optional). * @param config.documentSystemMessage - System message to include in all requests with documents (optional). * @param config.temperature - Default temperature for all requests (optional). */ constructor(config = {}) { this.systemMessage = config.systemMessage ?? "You are a helpful assistant."; this._documentSystemMessage = config.documentSystemMessage ? this.validateDocumentSystemMessage(config.documentSystemMessage) : "Here are some documents that you can consider in your response: {{documents}}"; this._core = new jorel_core_1.JorElCoreStore({ temperature: config.temperature === undefined ? 0 : config.temperature, logger: config.logger, logLevel: config.logLevel, }); this.team = new jorel_team_1.JorElAgentManager(this._core); if (config.anthropic) this.providers.registerAnthropic(config.anthropic === true ? undefined : config.anthropic); if (config.googleGenAi) this.providers.registerGoogleGenAi(config.googleGenAi === true ? undefined : config.googleGenAi); if (config.grok) this.providers.registerGrok(config.grok === true ? undefined : config.grok); if (config.groq) this.providers.registerGroq(config.groq === true ? undefined : config.groq); if (config.mistral) this.providers.registerMistral(config.mistral === true ? undefined : config.mistral); if (config.vertexAi) this.providers.registerGoogleVertexAi(config.vertexAi === true ? undefined : config.vertexAi); if (config.ollama) this.providers.registerOllama(config.ollama === true ? undefined : config.ollama); if (config.openAI) this.providers.registerOpenAi(config.openAI === true ? undefined : config.openAI); if (config.openRouter) this.providers.registerOpenRouter(config.openRouter === true ? undefined : config.openRouter); } /** @internal */ _documentSystemMessage; /** * Default document system message for all requests (only used when documents are included) */ get documentSystemMessage() { return this._documentSystemMessage; } /** * Set the default document system message for all requests (only used when documents are included) */ set documentSystemMessage(documentSystemMessage) { this._documentSystemMessage = this.validateDocumentSystemMessage(documentSystemMessage); } /** * Default temperature for all requests */ get temperature() { return this._core.defaultConfig.temperature; } /** * Set the default temperature for all requests */ set temperature(temperature) { this._core.defaultConfig.temperature = temperature; } /** * Logger instance */ get logger() { return this._core.logger; } /** * Set the logger instance */ set logger(logger) { this._core.logger = logger; } /** * Log level */ get logLevel() { return this._core.logger.logLevel; } /** * Set the log level */ set logLevel(logLevel) { this._core.logger.logLevel = logLevel; } /** * Generate a response for a given set of messages. * * @param messages - The messages to generate a response for. * @param config - The configuration for the generation. * @param config.model - Model to use for this generation (optional). * @param config.systemMessage - System message to include in this request (optional). * @param config.temperature - Temperature for this request (optional). * @param config.tools - Tools to use for this request (optional). */ async generate(messages, config = {}) { return this._core.generate(messages, config); } async ask(task, config = {}, includeMeta = false) { // @ts-expect-error ts(2769) - overloads return this.text(task, config, includeMeta); } async text(task, config = {}, includeMeta = false) { const { systemMessage, documents, documentSystemMessage, messageHistory, ...coreConfig } = config; const _messages = await this.generateMessages(task, systemMessage, documents, documentSystemMessage, messageHistory); const _config = { ...coreConfig, tools: config.tools ? config.tools instanceof tools_1.LlmToolKit ? config.tools : new tools_1.LlmToolKit(config.tools) : undefined, }; const { output, messages } = await this._core.generateAndProcessTools(_messages, _config); const response = output.content || ""; const meta = output.meta; return includeMeta ? { response, meta, messages } : response; } async json(task, config = {}, includeMeta = false) { const { systemMessage, documents, documentSystemMessage, messageHistory, ...coreConfig } = config; const _messages = await this.generateMessages(task, systemMessage, documents, documentSystemMessage, messageHistory); const _config = { ...coreConfig, json: config.jsonSchema || true, tools: config.tools ? config.tools instanceof tools_1.LlmToolKit ? config.tools : new tools_1.LlmToolKit(config.tools) : undefined, }; const { output, messages } = await this._core.generateAndProcessTools(_messages, _config); const parsed = output.content ? tools_1.LlmToolKit.deserialize(output.content) : {}; return includeMeta ? { response: parsed, meta: output.meta, messages } : parsed; } /** * Generate a stream of response chunks for a given set of messages. * * @param messages - The messages to generate a response for. * @param config - The configuration for the generation. */ async *generateContentStream(messages, config = {}) { yield* this._core.generateContentStream(messages, config); } /** * Generate a stream of response chunks for a given task. * * @param task - The task to generate a response for (either a string or an array of strings and ImageContent objects). * @param config - Configuration for the specific generation. */ async *stream(task, config = {}) { const stream = this.streamWithMeta(task, config); for await (const chunk of stream) { if (chunk.type === "chunk" && chunk.content) yield chunk.content; } } /** * Generate a stream of response chunks for a given task with metadata. * * @param task - The task to generate a response for (either a string or an array of strings and ImageContent objects). * @param config - Configuration for the specific generation. */ async *streamWithMeta(task, config = {}) { const { systemMessage, documents, documentSystemMessage, messageHistory, ...coreConfig } = config; const messages = await this.generateMessages(task, systemMessage, documents, documentSystemMessage, messageHistory); const _config = { ...coreConfig, tools: config.tools ? config.tools instanceof tools_1.LlmToolKit ? config.tools : new tools_1.LlmToolKit(config.tools) : undefined, }; if (config.tools) { yield* this._core.generateStreamAndProcessTools(messages, _config); } else { const stream = this._core.generateContentStream(messages, _config); for await (const chunk of stream) { if (chunk.type === "chunk") { yield chunk; } if (chunk.type === "response") { yield chunk; if (chunk.role === "assistant") { messages.push({ id: (0, shared_1.generateUniqueId)(), role: "assistant", content: chunk.content, createdAt: Date.now(), }); } else { messages.push({ id: (0, shared_1.generateUniqueId)(), role: "assistant_with_tools", content: chunk.content, toolCalls: chunk.toolCalls, createdAt: Date.now(), }); } yield { type: "messages", messages }; } } } } /** * Create an embedding for a given text. * * @param text - The text to create an embedding for. * @param config - The configuration for the embedding. * @param config.model - The model to use for the embedding (optional). */ async embed(text, config = {}) { return this._core.generateEmbedding(text, config.model); } /** * Generate a system message - optionally with a set of documents. * * @param systemMessage - The system message to use. * @param documents - The documents to include in the system message (optional). * @param documentSystemMessage - The system message to use for documents (optional). */ generateSystemMessage(systemMessage = "", { documents, documentSystemMessage, } = {}) { const _documents = documents instanceof documents_1.LlmDocumentCollection ? documents : new documents_1.LlmDocumentCollection(documents); return (0, providers_1.generateSystemMessage)(systemMessage || this.systemMessage, documentSystemMessage || this._documentSystemMessage, _documents); } /** * Generate a user message. * * @param content - The content to include in the user message. */ generateUserMessage(content) { return (0, providers_1.generateUserMessage)(content); } /** * Helper to generate messages for a given task input. * * @param content - The task input content (either a string or an array of strings and ImageContent objects). * @param systemMessage - The system message to include (optional). * @param documents - The documents to include in the system message (optional). * @param documentSystemMessage - The system message to use for documents (optional). * @param messageHistory - The message history to include (optional). If provided along with a dedicated system * message, the system message inside the messages will be ignored. * @internal */ async generateMessages(content, systemMessage, documents, documentSystemMessage, messageHistory = []) { if (Array.isArray(content)) { if (content.length === 0) { throw new Error("The task input must not be an empty array."); } } else { if (!content) { throw new Error("The task input must not be empty."); } } const _userMessage = await this.generateUserMessage(content); // Empty string overrides default to skip system message if (systemMessage !== "" && (systemMessage || this.systemMessage)) { if (messageHistory && messageHistory.some((m) => m.role === "system")) { this._core.logger.info("JorEl", "Message history contains system messages. These will be ignored."); } const _systemMessage = this.generateSystemMessage(systemMessage, { documents, documentSystemMessage }); return [_systemMessage, ...messageHistory.filter((m) => m.role !== "system"), _userMessage]; } else { if (documents && documents.length > 0) { this.logger.warn("JorEl", "Documents were provided but no system message was included. The documents will not be included in the response."); } } return [...messageHistory, _userMessage]; } /** * Helper to validate the document system message. * * @param documentSystemMessage - The document system message to validate. * @internal */ validateDocumentSystemMessage(documentSystemMessage) { if (!documentSystemMessage) return documentSystemMessage; if (documentSystemMessage.includes("{{documents}}")) return documentSystemMessage; throw new Error('The "documentSystemMessage" must either be empty or include the placeholder "{{documents}}" to insert the document list.'); } } exports.JorEl = JorEl;