UNPKG

@posthog/ai

Version:
981 lines (933 loc) 33.1 kB
import 'buffer'; import * as uuid from 'uuid'; const getModelParams = params => { if (!params) { return {}; } const modelParams = {}; const paramKeys = ['temperature', 'max_tokens', 'max_completion_tokens', 'top_p', 'frequency_penalty', 'presence_penalty', 'n', 'stop', 'stream', 'streaming']; for (const key of paramKeys) { if (key in params && params[key] !== undefined) { modelParams[key] = params[key]; } } return modelParams; }; const withPrivacyMode = (client, privacyMode, input) => { return client.privacy_mode || privacyMode ? null : input; }; var decamelize = function (str, sep) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } sep = typeof sep === 'undefined' ? '_' : sep; return str .replace(/([a-z\d])([A-Z])/g, '$1' + sep + '$2') .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + sep + '$2') .toLowerCase(); }; var camelcase = {exports: {}}; const UPPERCASE = /[\p{Lu}]/u; const LOWERCASE = /[\p{Ll}]/u; const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu; const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u; const SEPARATORS = /[_.\- ]+/; const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source); const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu'); const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu'); const preserveCamelCase = (string, toLowerCase, toUpperCase) => { let isLastCharLower = false; let isLastCharUpper = false; let isLastLastCharUpper = false; for (let i = 0; i < string.length; i++) { const character = string[i]; if (isLastCharLower && UPPERCASE.test(character)) { string = string.slice(0, i) + '-' + string.slice(i); isLastCharLower = false; isLastLastCharUpper = isLastCharUpper; isLastCharUpper = true; i++; } else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) { string = string.slice(0, i - 1) + '-' + string.slice(i - 1); isLastLastCharUpper = isLastCharUpper; isLastCharUpper = false; isLastCharLower = true; } else { isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character; isLastLastCharUpper = isLastCharUpper; isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character; } } return string; }; const preserveConsecutiveUppercase = (input, toLowerCase) => { LEADING_CAPITAL.lastIndex = 0; return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1)); }; const postProcess = (input, toUpperCase) => { SEPARATORS_AND_IDENTIFIER.lastIndex = 0; NUMBERS_AND_IDENTIFIER.lastIndex = 0; return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier)) .replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m)); }; const camelCase = (input, options) => { if (!(typeof input === 'string' || Array.isArray(input))) { throw new TypeError('Expected the input to be `string | string[]`'); } options = { pascalCase: false, preserveConsecutiveUppercase: false, ...options }; if (Array.isArray(input)) { input = input.map(x => x.trim()) .filter(x => x.length) .join('-'); } else { input = input.trim(); } if (input.length === 0) { return ''; } const toLowerCase = options.locale === false ? string => string.toLowerCase() : string => string.toLocaleLowerCase(options.locale); const toUpperCase = options.locale === false ? string => string.toUpperCase() : string => string.toLocaleUpperCase(options.locale); if (input.length === 1) { return options.pascalCase ? toUpperCase(input) : toLowerCase(input); } const hasUpperCase = input !== toLowerCase(input); if (hasUpperCase) { input = preserveCamelCase(input, toLowerCase, toUpperCase); } input = input.replace(LEADING_SEPARATORS, ''); if (options.preserveConsecutiveUppercase) { input = preserveConsecutiveUppercase(input, toLowerCase); } else { input = toLowerCase(input); } if (options.pascalCase) { input = toUpperCase(input.charAt(0)) + input.slice(1); } return postProcess(input, toUpperCase); }; camelcase.exports = camelCase; // TODO: Remove this for the next major release camelcase.exports.default = camelCase; function keyToJson(key, map) { return map?.[key] || decamelize(key); } function mapKeys(fields, mapper, map) { const mapped = {}; for (const key in fields) { if (Object.hasOwn(fields, key)) { mapped[mapper(key, map)] = fields[key]; } } return mapped; } function shallowCopy(obj) { return Array.isArray(obj) ? [...obj] : { ...obj }; } function replaceSecrets(root, secretsMap) { const result = shallowCopy(root); for (const [path, secretId] of Object.entries(secretsMap)) { const [last, ...partsReverse] = path.split(".").reverse(); // eslint-disable-next-line @typescript-eslint/no-explicit-any let current = result; for (const part of partsReverse.reverse()) { if (current[part] === undefined) { break; } current[part] = shallowCopy(current[part]); current = current[part]; } if (current[last] !== undefined) { current[last] = { lc: 1, type: "secret", id: [secretId], }; } } return result; } /** * Get a unique name for the module, rather than parent class implementations. * Should not be subclassed, subclass lc_name above instead. */ function get_lc_unique_name( // eslint-disable-next-line @typescript-eslint/no-use-before-define serializableClass) { // "super" here would refer to the parent class of Serializable, // when we want the parent class of the module actually calling this method. const parentClass = Object.getPrototypeOf(serializableClass); const lcNameIsSubclassed = typeof serializableClass.lc_name === "function" && (typeof parentClass.lc_name !== "function" || serializableClass.lc_name() !== parentClass.lc_name()); if (lcNameIsSubclassed) { return serializableClass.lc_name(); } else { return serializableClass.name; } } class Serializable { /** * The name of the serializable. Override to provide an alias or * to preserve the serialized module name in minified environments. * * Implemented as a static method to support loading logic. */ static lc_name() { return this.name; } /** * The final serialized identifier for the module. */ get lc_id() { return [ ...this.lc_namespace, get_lc_unique_name(this.constructor), ]; } /** * A map of secrets, which will be omitted from serialization. * Keys are paths to the secret in constructor args, e.g. "foo.bar.baz". * Values are the secret ids, which will be used when deserializing. */ get lc_secrets() { return undefined; } /** * A map of additional attributes to merge with constructor args. * Keys are the attribute names, e.g. "foo". * Values are the attribute values, which will be serialized. * These attributes need to be accepted by the constructor as arguments. */ get lc_attributes() { return undefined; } /** * A map of aliases for constructor args. * Keys are the attribute names, e.g. "foo". * Values are the alias that will replace the key in serialization. * This is used to eg. make argument names match Python. */ get lc_aliases() { return undefined; } constructor(kwargs, ..._args) { Object.defineProperty(this, "lc_serializable", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "lc_kwargs", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.lc_kwargs = kwargs || {}; } toJSON() { if (!this.lc_serializable) { return this.toJSONNotImplemented(); } if ( // eslint-disable-next-line no-instanceof/no-instanceof this.lc_kwargs instanceof Serializable || typeof this.lc_kwargs !== "object" || Array.isArray(this.lc_kwargs)) { // We do not support serialization of classes with arg not a POJO // I'm aware the check above isn't as strict as it could be return this.toJSONNotImplemented(); } const aliases = {}; const secrets = {}; const kwargs = Object.keys(this.lc_kwargs).reduce((acc, key) => { acc[key] = key in this ? this[key] : this.lc_kwargs[key]; return acc; }, {}); // get secrets, attributes and aliases from all superclasses for ( // eslint-disable-next-line @typescript-eslint/no-this-alias let current = Object.getPrototypeOf(this); current; current = Object.getPrototypeOf(current)) { Object.assign(aliases, Reflect.get(current, "lc_aliases", this)); Object.assign(secrets, Reflect.get(current, "lc_secrets", this)); Object.assign(kwargs, Reflect.get(current, "lc_attributes", this)); } // include all secrets used, even if not in kwargs, // will be replaced with sentinel value in replaceSecrets Object.keys(secrets).forEach((keyPath) => { // eslint-disable-next-line @typescript-eslint/no-this-alias, @typescript-eslint/no-explicit-any let read = this; // eslint-disable-next-line @typescript-eslint/no-explicit-any let write = kwargs; const [last, ...partsReverse] = keyPath.split(".").reverse(); for (const key of partsReverse.reverse()) { if (!(key in read) || read[key] === undefined) return; if (!(key in write) || write[key] === undefined) { if (typeof read[key] === "object" && read[key] != null) { write[key] = {}; } else if (Array.isArray(read[key])) { write[key] = []; } } read = read[key]; write = write[key]; } if (last in read && read[last] !== undefined) { write[last] = write[last] || read[last]; } }); return { lc: 1, type: "constructor", id: this.lc_id, kwargs: mapKeys(Object.keys(secrets).length ? replaceSecrets(kwargs, secrets) : kwargs, keyToJson, aliases), }; } toJSONNotImplemented() { return { lc: 1, type: "not_implemented", id: this.lc_id, }; } } // Supabase Edge Function provides a `Deno` global object // without `version` property const isDeno = () => typeof Deno !== "undefined"; function getEnvironmentVariable(name) { // Certain Deno setups will throw an error if you try to access environment variables // https://github.com/langchain-ai/langchainjs/issues/1412 try { if (typeof process !== "undefined") { // eslint-disable-next-line no-process-env return process.env?.[name]; } else if (isDeno()) { return Deno?.env.get(name); } else { return undefined; } } catch (e) { return undefined; } } /** * Abstract class that provides a set of optional methods that can be * overridden in derived classes to handle various events during the * execution of a LangChain application. */ class BaseCallbackHandlerMethodsClass { } /** * Abstract base class for creating callback handlers in the LangChain * framework. It provides a set of optional methods that can be overridden * in derived classes to handle various events during the execution of a * LangChain application. */ class BaseCallbackHandler extends BaseCallbackHandlerMethodsClass { get lc_namespace() { return ["langchain_core", "callbacks", this.name]; } get lc_secrets() { return undefined; } get lc_attributes() { return undefined; } get lc_aliases() { return undefined; } /** * The name of the serializable. Override to provide an alias or * to preserve the serialized module name in minified environments. * * Implemented as a static method to support loading logic. */ static lc_name() { return this.name; } /** * The final serialized identifier for the module. */ get lc_id() { return [ ...this.lc_namespace, get_lc_unique_name(this.constructor), ]; } constructor(input) { super(); Object.defineProperty(this, "lc_serializable", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "lc_kwargs", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "ignoreLLM", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "ignoreChain", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "ignoreAgent", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "ignoreRetriever", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "ignoreCustomEvent", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "raiseError", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "awaitHandlers", { enumerable: true, configurable: true, writable: true, value: getEnvironmentVariable("LANGCHAIN_CALLBACKS_BACKGROUND") === "false" }); this.lc_kwargs = input || {}; if (input) { this.ignoreLLM = input.ignoreLLM ?? this.ignoreLLM; this.ignoreChain = input.ignoreChain ?? this.ignoreChain; this.ignoreAgent = input.ignoreAgent ?? this.ignoreAgent; this.ignoreRetriever = input.ignoreRetriever ?? this.ignoreRetriever; this.ignoreCustomEvent = input.ignoreCustomEvent ?? this.ignoreCustomEvent; this.raiseError = input.raiseError ?? this.raiseError; this.awaitHandlers = this.raiseError || (input._awaitHandler ?? this.awaitHandlers); } } copy() { return new this.constructor(this); } toJSON() { return Serializable.prototype.toJSON.call(this); } toJSONNotImplemented() { return Serializable.prototype.toJSONNotImplemented.call(this); } static fromMethods(methods) { class Handler extends BaseCallbackHandler { constructor() { super(); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: uuid.v4() }); Object.assign(this, methods); } } return new Handler(); } } /** A run may either be a Span or a Generation */ /** Storage for run metadata */ class LangChainCallbackHandler extends BaseCallbackHandler { name = 'PosthogCallbackHandler'; runs = {}; parentTree = {}; constructor(options) { if (!options.client) { throw new Error('PostHog client is required'); } super(); this.client = options.client; this.distinctId = options.distinctId; this.traceId = options.traceId; this.properties = options.properties || {}; this.privacyMode = options.privacyMode || false; this.groups = options.groups || {}; this.debug = options.debug || false; } // ===== CALLBACK METHODS ===== handleChainStart(chain, inputs, runId, parentRunId, tags, metadata, runType, runName) { this._logDebugEvent('on_chain_start', runId, parentRunId, { inputs, tags }); this._setParentOfRun(runId, parentRunId); this._setTraceOrSpanMetadata(chain, inputs, runId, parentRunId, metadata, tags, runName); } handleChainEnd(outputs, runId, parentRunId, tags, // eslint-disable-next-line @typescript-eslint/no-unused-vars kwargs) { this._logDebugEvent('on_chain_end', runId, parentRunId, { outputs, tags }); this._popRunAndCaptureTraceOrSpan(runId, parentRunId, outputs); } handleChainError(error, runId, parentRunId, tags, // eslint-disable-next-line @typescript-eslint/no-unused-vars kwargs) { this._logDebugEvent('on_chain_error', runId, parentRunId, { error, tags }); this._popRunAndCaptureTraceOrSpan(runId, parentRunId, error); } handleChatModelStart(serialized, messages, runId, parentRunId, extraParams, tags, metadata, runName) { this._logDebugEvent('on_chat_model_start', runId, parentRunId, { messages, tags }); this._setParentOfRun(runId, parentRunId); // Flatten the two-dimensional messages and convert each message to a plain object const input = messages.flat().map(m => this._convertMessageToDict(m)); this._setLLMMetadata(serialized, runId, input, metadata, extraParams, runName); } handleLLMStart(serialized, prompts, runId, parentRunId, extraParams, tags, metadata, runName) { this._logDebugEvent('on_llm_start', runId, parentRunId, { prompts, tags }); this._setParentOfRun(runId, parentRunId); this._setLLMMetadata(serialized, runId, prompts, metadata, extraParams, runName); } handleLLMEnd(output, runId, parentRunId, tags, // eslint-disable-next-line @typescript-eslint/no-unused-vars extraParams) { this._logDebugEvent('on_llm_end', runId, parentRunId, { output, tags }); this._popRunAndCaptureGeneration(runId, parentRunId, output); } handleLLMError(err, runId, parentRunId, tags, // eslint-disable-next-line @typescript-eslint/no-unused-vars extraParams) { this._logDebugEvent('on_llm_error', runId, parentRunId, { err, tags }); this._popRunAndCaptureGeneration(runId, parentRunId, err); } handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName) { this._logDebugEvent('on_tool_start', runId, parentRunId, { input, tags }); this._setParentOfRun(runId, parentRunId); this._setTraceOrSpanMetadata(tool, input, runId, parentRunId, metadata, tags, runName); } handleToolEnd(output, runId, parentRunId, tags) { this._logDebugEvent('on_tool_end', runId, parentRunId, { output, tags }); this._popRunAndCaptureTraceOrSpan(runId, parentRunId, output); } handleToolError(err, runId, parentRunId, tags) { this._logDebugEvent('on_tool_error', runId, parentRunId, { err, tags }); this._popRunAndCaptureTraceOrSpan(runId, parentRunId, err); } handleRetrieverStart(retriever, query, runId, parentRunId, tags, metadata, name) { this._logDebugEvent('on_retriever_start', runId, parentRunId, { query, tags }); this._setParentOfRun(runId, parentRunId); this._setTraceOrSpanMetadata(retriever, query, runId, parentRunId, metadata, tags, name); } handleRetrieverEnd(documents, runId, parentRunId, tags) { this._logDebugEvent('on_retriever_end', runId, parentRunId, { documents, tags }); this._popRunAndCaptureTraceOrSpan(runId, parentRunId, documents); } handleRetrieverError(err, runId, parentRunId, tags) { this._logDebugEvent('on_retriever_error', runId, parentRunId, { err, tags }); this._popRunAndCaptureTraceOrSpan(runId, parentRunId, err); } handleAgentAction(action, runId, parentRunId, tags) { this._logDebugEvent('on_agent_action', runId, parentRunId, { action, tags }); this._setParentOfRun(runId, parentRunId); this._setTraceOrSpanMetadata(null, action, runId, parentRunId); } handleAgentEnd(action, runId, parentRunId, tags) { this._logDebugEvent('on_agent_finish', runId, parentRunId, { action, tags }); this._popRunAndCaptureTraceOrSpan(runId, parentRunId, action); } // ===== PRIVATE HELPERS ===== _setParentOfRun(runId, parentRunId) { if (parentRunId) { this.parentTree[runId] = parentRunId; } } _popParentOfRun(runId) { delete this.parentTree[runId]; } _findRootRun(runId) { let id = runId; while (this.parentTree[id]) { id = this.parentTree[id]; } return id; } _setTraceOrSpanMetadata(serialized, input, runId, parentRunId, ...args) { // Use default names if not provided: if this is a top-level run, we mark it as a trace, otherwise as a span. const defaultName = parentRunId ? 'span' : 'trace'; const runName = this._getLangchainRunName(serialized, ...args) || defaultName; this.runs[runId] = { name: runName, input, startTime: Date.now() }; } _setLLMMetadata(serialized, runId, messages, metadata, extraParams, runName) { const runNameFound = this._getLangchainRunName(serialized, { extraParams, runName }) || 'generation'; const generation = { name: runNameFound, input: messages, startTime: Date.now() }; if (extraParams) { generation.modelParams = getModelParams(extraParams.invocation_params); } if (metadata) { if (metadata.ls_model_name) { generation.model = metadata.ls_model_name; } if (metadata.ls_provider) { generation.provider = metadata.ls_provider; } } if (serialized && 'kwargs' in serialized && serialized.kwargs.openai_api_base) { generation.baseUrl = serialized.kwargs.openai_api_base; } this.runs[runId] = generation; } _popRunMetadata(runId) { const endTime = Date.now(); const run = this.runs[runId]; if (!run) { console.warn(`No run metadata found for run ${runId}`); return undefined; } run.endTime = endTime; delete this.runs[runId]; return run; } _getTraceId(runId) { return this.traceId ? String(this.traceId) : this._findRootRun(runId); } _getParentRunId(traceId, runId, parentRunId) { // Replace the parent-run if not found in our stored parent tree. if (parentRunId && !this.parentTree[parentRunId]) { return traceId; } return parentRunId; } _popRunAndCaptureTraceOrSpan(runId, parentRunId, outputs) { const traceId = this._getTraceId(runId); this._popParentOfRun(runId); const run = this._popRunMetadata(runId); if (!run) { return; } if ('modelParams' in run) { console.warn(`Run ${runId} is a generation, but attempted to be captured as a trace/span.`); return; } const actualParentRunId = this._getParentRunId(traceId, runId, parentRunId); this._captureTraceOrSpan(traceId, runId, run, outputs, actualParentRunId); } _captureTraceOrSpan(traceId, runId, run, outputs, parentRunId) { const eventName = parentRunId ? '$ai_span' : '$ai_trace'; const latency = run.endTime ? (run.endTime - run.startTime) / 1000 : 0; const eventProperties = { $ai_trace_id: traceId, $ai_input_state: withPrivacyMode(this.client, this.privacyMode, run.input), $ai_latency: latency, $ai_span_name: run.name, $ai_span_id: runId }; if (parentRunId) { eventProperties['$ai_parent_id'] = parentRunId; } Object.assign(eventProperties, this.properties); if (!this.distinctId) { eventProperties['$process_person_profile'] = false; } if (outputs instanceof Error) { eventProperties['$ai_error'] = outputs.toString(); eventProperties['$ai_is_error'] = true; } else if (outputs !== undefined) { eventProperties['$ai_output_state'] = withPrivacyMode(this.client, this.privacyMode, outputs); } this.client.capture({ distinctId: this.distinctId ? this.distinctId.toString() : runId, event: eventName, properties: eventProperties, groups: this.groups }); } _popRunAndCaptureGeneration(runId, parentRunId, response) { const traceId = this._getTraceId(runId); this._popParentOfRun(runId); const run = this._popRunMetadata(runId); if (!run || typeof run !== 'object' || !('modelParams' in run)) { console.warn(`Run ${runId} is not a generation, but attempted to be captured as such.`); return; } const actualParentRunId = this._getParentRunId(traceId, runId, parentRunId); this._captureGeneration(traceId, runId, run, response, actualParentRunId); } _captureGeneration(traceId, runId, run, output, parentRunId) { const latency = run.endTime ? (run.endTime - run.startTime) / 1000 : 0; const eventProperties = { $ai_trace_id: traceId, $ai_span_id: runId, $ai_span_name: run.name, $ai_parent_id: parentRunId, $ai_provider: run.provider, $ai_model: run.model, $ai_model_parameters: run.modelParams, $ai_input: withPrivacyMode(this.client, this.privacyMode, run.input), $ai_http_status: 200, $ai_latency: latency, $ai_base_url: run.baseUrl }; if (run.tools) { eventProperties['$ai_tools'] = withPrivacyMode(this.client, this.privacyMode, run.tools); } if (output instanceof Error) { eventProperties['$ai_http_status'] = output.status || 500; eventProperties['$ai_error'] = output.toString(); eventProperties['$ai_is_error'] = true; } else { // Handle token usage const [inputTokens, outputTokens, additionalTokenData] = this.parseUsage(output); eventProperties['$ai_input_tokens'] = inputTokens; eventProperties['$ai_output_tokens'] = outputTokens; // Add additional token data to properties if (additionalTokenData.cacheReadInputTokens) { eventProperties['$ai_cache_read_tokens'] = additionalTokenData.cacheReadInputTokens; } if (additionalTokenData.reasoningTokens) { eventProperties['$ai_reasoning_tokens'] = additionalTokenData.reasoningTokens; } // Handle generations/completions let completions; if (output.generations && Array.isArray(output.generations)) { const lastGeneration = output.generations[output.generations.length - 1]; if (Array.isArray(lastGeneration)) { completions = lastGeneration.map(gen => { return { role: 'assistant', content: gen.text }; }); } } if (completions) { eventProperties['$ai_output_choices'] = withPrivacyMode(this.client, this.privacyMode, completions); } } Object.assign(eventProperties, this.properties); if (!this.distinctId) { eventProperties['$process_person_profile'] = false; } this.client.capture({ distinctId: this.distinctId ? this.distinctId.toString() : traceId, event: '$ai_generation', properties: eventProperties, groups: this.groups }); } _logDebugEvent(eventName, runId, parentRunId, extra) { if (this.debug) { console.log(`Event: ${eventName}, runId: ${runId}, parentRunId: ${parentRunId}, extra:`, extra); } } _getLangchainRunName(serialized, ...args) { if (args && args.length > 0) { for (const arg of args) { if (arg && typeof arg === 'object' && 'name' in arg) { return arg.name; } else if (arg && typeof arg === 'object' && 'runName' in arg) { return arg.runName; } } } if (serialized && serialized.name) { return serialized.name; } if (serialized && serialized.id) { return Array.isArray(serialized.id) ? serialized.id[serialized.id.length - 1] : serialized.id; } return undefined; } _convertMessageToDict(message) { let messageDict = {}; // Check the _getType() method or type property instead of instanceof const messageType = message._getType?.() || message.type; switch (messageType) { case 'human': messageDict = { role: 'user', content: message.content }; break; case 'ai': messageDict = { role: 'assistant', content: message.content }; break; case 'system': messageDict = { role: 'system', content: message.content }; break; case 'tool': messageDict = { role: 'tool', content: message.content }; break; case 'function': messageDict = { role: 'function', content: message.content }; break; default: messageDict = { role: messageType || 'unknown', content: String(message.content) }; } if (message.additional_kwargs) { messageDict = { ...messageDict, ...message.additional_kwargs }; } return messageDict; } _parseUsageModel(usage) { const conversionList = [['promptTokens', 'input'], ['completionTokens', 'output'], ['input_tokens', 'input'], ['output_tokens', 'output'], ['prompt_token_count', 'input'], ['candidates_token_count', 'output'], ['inputTokenCount', 'input'], ['outputTokenCount', 'output'], ['input_token_count', 'input'], ['generated_token_count', 'output']]; const parsedUsage = conversionList.reduce((acc, [modelKey, typeKey]) => { const value = usage[modelKey]; if (value != null) { const finalCount = Array.isArray(value) ? value.reduce((sum, tokenCount) => sum + tokenCount, 0) : value; acc[typeKey] = finalCount; } return acc; }, { input: 0, output: 0 }); // Extract additional token details like cached tokens and reasoning tokens const additionalTokenData = {}; // Check for cached tokens in various formats if (usage.prompt_tokens_details?.cached_tokens != null) { additionalTokenData.cacheReadInputTokens = usage.prompt_tokens_details.cached_tokens; } else if (usage.input_token_details?.cache_read != null) { additionalTokenData.cacheReadInputTokens = usage.input_token_details.cache_read; } else if (usage.cachedPromptTokens != null) { additionalTokenData.cacheReadInputTokens = usage.cachedPromptTokens; } // Check for reasoning tokens in various formats if (usage.completion_tokens_details?.reasoning_tokens != null) { additionalTokenData.reasoningTokens = usage.completion_tokens_details.reasoning_tokens; } else if (usage.output_token_details?.reasoning != null) { additionalTokenData.reasoningTokens = usage.output_token_details.reasoning; } else if (usage.reasoningTokens != null) { additionalTokenData.reasoningTokens = usage.reasoningTokens; } return [parsedUsage.input, parsedUsage.output, additionalTokenData]; } parseUsage(response) { let llmUsage = [0, 0, {}]; const llmUsageKeys = ['token_usage', 'usage', 'tokenUsage']; if (response.llmOutput != null) { const key = llmUsageKeys.find(k => response.llmOutput?.[k] != null); if (key) { llmUsage = this._parseUsageModel(response.llmOutput[key]); } } // If top-level usage info was not found, try checking the generations. if (llmUsage[0] === 0 && llmUsage[1] === 0 && response.generations) { for (const generation of response.generations) { for (const genChunk of generation) { // Check other paths for usage information if (genChunk.generationInfo?.usage_metadata) { llmUsage = this._parseUsageModel(genChunk.generationInfo.usage_metadata); return llmUsage; } const messageChunk = genChunk.generationInfo ?? {}; const responseMetadata = messageChunk.response_metadata ?? {}; const chunkUsage = responseMetadata['usage'] ?? responseMetadata['amazon-bedrock-invocationMetrics'] ?? messageChunk.usage_metadata; if (chunkUsage) { llmUsage = this._parseUsageModel(chunkUsage); return llmUsage; } } } } return llmUsage; } } export { LangChainCallbackHandler }; //# sourceMappingURL=index.mjs.map