UNPKG

@langchain/core

Version:
308 lines (307 loc) 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseLanguageModel = exports.BaseLangChain = exports.calculateMaxTokens = exports.getModelContextSize = exports.getEmbeddingContextSize = exports.getModelNameForTiktoken = void 0; exports.isOpenAITool = isOpenAITool; const base_js_1 = require("../caches/base.cjs"); const prompt_values_js_1 = require("../prompt_values.cjs"); const utils_js_1 = require("../messages/utils.cjs"); const async_caller_js_1 = require("../utils/async_caller.cjs"); const tiktoken_js_1 = require("../utils/tiktoken.cjs"); const base_js_2 = require("../runnables/base.cjs"); // https://www.npmjs.com/package/js-tiktoken const getModelNameForTiktoken = (modelName) => { if (modelName.startsWith("gpt-3.5-turbo-16k")) { return "gpt-3.5-turbo-16k"; } if (modelName.startsWith("gpt-3.5-turbo-")) { return "gpt-3.5-turbo"; } if (modelName.startsWith("gpt-4-32k")) { return "gpt-4-32k"; } if (modelName.startsWith("gpt-4-")) { return "gpt-4"; } if (modelName.startsWith("gpt-4o")) { return "gpt-4o"; } return modelName; }; exports.getModelNameForTiktoken = getModelNameForTiktoken; const getEmbeddingContextSize = (modelName) => { switch (modelName) { case "text-embedding-ada-002": return 8191; default: return 2046; } }; exports.getEmbeddingContextSize = getEmbeddingContextSize; const getModelContextSize = (modelName) => { switch ((0, exports.getModelNameForTiktoken)(modelName)) { case "gpt-3.5-turbo-16k": return 16384; case "gpt-3.5-turbo": return 4096; case "gpt-4-32k": return 32768; case "gpt-4": return 8192; case "text-davinci-003": return 4097; case "text-curie-001": return 2048; case "text-babbage-001": return 2048; case "text-ada-001": return 2048; case "code-davinci-002": return 8000; case "code-cushman-001": return 2048; default: return 4097; } }; exports.getModelContextSize = getModelContextSize; /** * Whether or not the input matches the OpenAI tool definition. * @param {unknown} tool The input to check. * @returns {boolean} Whether the input is an OpenAI tool definition. */ function isOpenAITool(tool) { if (typeof tool !== "object" || !tool) return false; if ("type" in tool && tool.type === "function" && "function" in tool && typeof tool.function === "object" && tool.function && "name" in tool.function && "parameters" in tool.function) { return true; } return false; } const calculateMaxTokens = async ({ prompt, modelName, }) => { let numTokens; try { numTokens = (await (0, tiktoken_js_1.encodingForModel)((0, exports.getModelNameForTiktoken)(modelName))).encode(prompt).length; } catch (error) { console.warn("Failed to calculate number of tokens, falling back to approximate count"); // fallback to approximate calculation if tiktoken is not available // each token is ~4 characters: https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them# numTokens = Math.ceil(prompt.length / 4); } const maxTokens = (0, exports.getModelContextSize)(modelName); return maxTokens - numTokens; }; exports.calculateMaxTokens = calculateMaxTokens; const getVerbosity = () => false; /** * Base class for language models, chains, tools. */ class BaseLangChain extends base_js_2.Runnable { get lc_attributes() { return { callbacks: undefined, verbose: undefined, }; } constructor(params) { super(params); /** * Whether to print out response text. */ Object.defineProperty(this, "verbose", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "callbacks", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "tags", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "metadata", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.verbose = params.verbose ?? getVerbosity(); this.callbacks = params.callbacks; this.tags = params.tags ?? []; this.metadata = params.metadata ?? {}; } } exports.BaseLangChain = BaseLangChain; /** * Base class for language models. */ class BaseLanguageModel extends BaseLangChain { /** * Keys that the language model accepts as call options. */ get callKeys() { return ["stop", "timeout", "signal", "tags", "metadata", "callbacks"]; } constructor({ callbacks, callbackManager, ...params }) { const { cache, ...rest } = params; super({ callbacks: callbacks ?? callbackManager, ...rest, }); /** * The async caller should be used by subclasses to make any async calls, * which will thus benefit from the concurrency and retry logic. */ Object.defineProperty(this, "caller", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "cache", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_encoding", { enumerable: true, configurable: true, writable: true, value: void 0 }); if (typeof cache === "object") { this.cache = cache; } else if (cache) { this.cache = base_js_1.InMemoryCache.global(); } else { this.cache = undefined; } this.caller = new async_caller_js_1.AsyncCaller(params ?? {}); } /** * Get the number of tokens in the content. * @param content The content to get the number of tokens for. * @returns The number of tokens in the content. */ async getNumTokens(content) { // Extract text content from MessageContent let textContent; if (typeof content === "string") { textContent = content; } else { /** * Content is an array of MessageContentComplex * * ToDo(@christian-bromann): This is a temporary fix to get the number of tokens for the content. * We need to find a better way to do this. * @see https://github.com/langchain-ai/langchainjs/pull/8341#pullrequestreview-2933713116 */ textContent = content .map((item) => { if (typeof item === "string") return item; if (item.type === "text" && "text" in item) return item.text; return ""; }) .join(""); } // fallback to approximate calculation if tiktoken is not available let numTokens = Math.ceil(textContent.length / 4); if (!this._encoding) { try { this._encoding = await (0, tiktoken_js_1.encodingForModel)("modelName" in this ? (0, exports.getModelNameForTiktoken)(this.modelName) : "gpt2"); } catch (error) { console.warn("Failed to calculate number of tokens, falling back to approximate count", error); } } if (this._encoding) { try { numTokens = this._encoding.encode(textContent).length; } catch (error) { console.warn("Failed to calculate number of tokens, falling back to approximate count", error); } } return numTokens; } static _convertInputToPromptValue(input) { if (typeof input === "string") { return new prompt_values_js_1.StringPromptValue(input); } else if (Array.isArray(input)) { return new prompt_values_js_1.ChatPromptValue(input.map(utils_js_1.coerceMessageLikeToMessage)); } else { return input; } } /** * Get the identifying parameters of the LLM. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any _identifyingParams() { return {}; } /** * Create a unique cache key for a specific call to a specific language model. * @param callOptions Call options for the model * @returns A unique cache key. */ _getSerializedCacheKeyParametersForCall( // TODO: Fix when we remove the RunnableLambda backwards compatibility shim. { config, ...callOptions }) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const params = { ...this._identifyingParams(), ...callOptions, _type: this._llmType(), _model: this._modelType(), }; const filteredEntries = Object.entries(params).filter(([_, value]) => value !== undefined); const serializedEntries = filteredEntries .map(([key, value]) => `${key}:${JSON.stringify(value)}`) .sort() .join(","); return serializedEntries; } /** * @deprecated * Return a json-like object representing this LLM. */ serialize() { return { ...this._identifyingParams(), _type: this._llmType(), _model: this._modelType(), }; } /** * @deprecated * Load an LLM from a json-like object describing it. */ static async deserialize(_data) { throw new Error("Use .toJSON() instead"); } } exports.BaseLanguageModel = BaseLanguageModel;