UNPKG

@posthog/ai

Version:
1,495 lines (1,477 loc) 63.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var OpenAIOrignal = require('openai'); var uuid = require('uuid'); var ai = require('ai'); var AnthropicOriginal = require('@anthropic-ai/sdk'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var OpenAIOrignal__default = /*#__PURE__*/_interopDefaultLegacy(OpenAIOrignal); var uuid__namespace = /*#__PURE__*/_interopNamespace(uuid); var AnthropicOriginal__default = /*#__PURE__*/_interopDefaultLegacy(AnthropicOriginal); 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 formatResponseAnthropic = response => { // Example approach if "response.content" holds array of text segments, etc. const output = []; for (const choice of response.content ?? []) { if (choice?.text) { output.push({ role: 'assistant', content: choice.text }); } } return output; }; const formatResponseOpenAI = response => { const output = []; for (const choice of response.choices ?? []) { if (choice.message?.content) { output.push({ role: choice.message.role, content: choice.message.content }); } } return output; }; const mergeSystemPrompt = (params, provider) => { if (provider == 'anthropic') { const messages = params.messages || []; if (!params.system) { return messages; } const systemMessage = params.system; return [{ role: 'system', content: systemMessage }, ...messages]; } return params.messages; }; const withPrivacyMode = (client, privacyMode, input) => { return client.privacy_mode || privacyMode ? null : input; }; const sendEventToPosthog = ({ client, distinctId, traceId, model, provider, input, output, latency, baseURL, params, httpStatus = 200, usage = {}, isError = false, error, tools }) => { if (client.capture) { let errorData = {}; if (isError) { errorData = { $ai_is_error: true, $ai_error: error }; } let costOverrideData = {}; if (params.posthogCostOverride) { const inputCostUSD = (params.posthogCostOverride.inputCost ?? 0) * (usage.inputTokens ?? 0); const outputCostUSD = (params.posthogCostOverride.outputCost ?? 0) * (usage.outputTokens ?? 0); costOverrideData = { $ai_input_cost_usd: inputCostUSD, $ai_output_cost_usd: outputCostUSD, $ai_total_cost_usd: inputCostUSD + outputCostUSD }; } const additionalTokenValues = { ...(usage.reasoningTokens ? { $ai_reasoning_tokens: usage.reasoningTokens } : {}), ...(usage.cacheReadInputTokens ? { $ai_cache_read_input_tokens: usage.cacheReadInputTokens } : {}), ...(usage.cacheCreationInputTokens ? { $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens } : {}) }; client.capture({ distinctId: distinctId ?? traceId, event: '$ai_generation', properties: { $ai_provider: params.posthogProviderOverride ?? provider, $ai_model: params.posthogModelOverride ?? model, $ai_model_parameters: getModelParams(params), $ai_input: withPrivacyMode(client, params.posthogPrivacyMode ?? false, input), $ai_output_choices: withPrivacyMode(client, params.posthogPrivacyMode ?? false, output), $ai_http_status: httpStatus, $ai_input_tokens: usage.inputTokens ?? 0, $ai_output_tokens: usage.outputTokens ?? 0, ...additionalTokenValues, $ai_latency: latency, $ai_trace_id: traceId, $ai_base_url: baseURL, ...params.posthogProperties, ...(distinctId ? {} : { $process_person_profile: false }), ...(tools ? { $ai_tools: tools } : {}), ...errorData, ...costOverrideData }, groups: params.posthogGroups }); } }; class PostHogOpenAI extends OpenAIOrignal__default["default"] { constructor(config) { const { posthog, ...openAIConfig } = config; super(openAIConfig); this.phClient = posthog; this.chat = new WrappedChat$1(this, this.phClient); } } class WrappedChat$1 extends OpenAIOrignal__default["default"].Chat { constructor(parentClient, phClient) { super(parentClient); this.completions = new WrappedCompletions$1(parentClient, phClient); } } class WrappedCompletions$1 extends OpenAIOrignal__default["default"].Chat.Completions { constructor(client, phClient) { super(client); this.phClient = phClient; } // --- Implementation Signature create(body, options) { const { posthogDistinctId, posthogTraceId, posthogProperties, // eslint-disable-next-line @typescript-eslint/no-unused-vars posthogPrivacyMode = false, posthogGroups, ...openAIParams } = body; const traceId = posthogTraceId ?? uuid.v4(); const startTime = Date.now(); const parentPromise = super.create(openAIParams, options); if (openAIParams.stream) { return parentPromise.then(value => { if ('tee' in value) { const [stream1, stream2] = value.tee(); (async () => { try { let accumulatedContent = ''; let usage = { inputTokens: 0, outputTokens: 0 }; for await (const chunk of stream1) { const delta = chunk?.choices?.[0]?.delta?.content ?? ''; accumulatedContent += delta; if (chunk.usage) { usage = { inputTokens: chunk.usage.prompt_tokens ?? 0, outputTokens: chunk.usage.completion_tokens ?? 0, reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0, cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0 }; } } const latency = (Date.now() - startTime) / 1000; sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: openAIParams.model, provider: 'openai', input: openAIParams.messages, output: [{ content: accumulatedContent, role: 'assistant' }], latency, baseURL: this.baseURL ?? '', params: body, httpStatus: 200, usage }); } catch (error) { sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: openAIParams.model, provider: 'openai', input: openAIParams.messages, output: [], latency: 0, baseURL: this.baseURL ?? '', params: body, httpStatus: error?.status ? error.status : 500, usage: { inputTokens: 0, outputTokens: 0 }, isError: true, error: JSON.stringify(error) }); } })(); // Return the other stream to the user return stream2; } return value; }); } else { const wrappedPromise = parentPromise.then(result => { if ('choices' in result) { const latency = (Date.now() - startTime) / 1000; sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: openAIParams.model, provider: 'openai', input: openAIParams.messages, output: formatResponseOpenAI(result), latency, baseURL: this.baseURL ?? '', params: body, httpStatus: 200, usage: { inputTokens: result.usage?.prompt_tokens ?? 0, outputTokens: result.usage?.completion_tokens ?? 0, reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0, cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0 } }); } return result; }, error => { sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: openAIParams.model, provider: 'openai', input: openAIParams.messages, output: [], latency: 0, baseURL: this.baseURL ?? '', params: body, httpStatus: error?.status ? error.status : 500, usage: { inputTokens: 0, outputTokens: 0 }, isError: true, error: JSON.stringify(error) }); throw error; }); return wrappedPromise; } } } class PostHogAzureOpenAI extends OpenAIOrignal.AzureOpenAI { constructor(config) { const { posthog, ...openAIConfig } = config; super(openAIConfig); this.phClient = posthog; this.chat = new WrappedChat(this, this.phClient); } } class WrappedChat extends OpenAIOrignal.AzureOpenAI.Chat { constructor(parentClient, phClient) { super(parentClient); this.completions = new WrappedCompletions(parentClient, phClient); } } class WrappedCompletions extends OpenAIOrignal.AzureOpenAI.Chat.Completions { constructor(client, phClient) { super(client); this.phClient = phClient; } // --- Implementation Signature create(body, options) { const { posthogDistinctId, posthogTraceId, posthogProperties, // eslint-disable-next-line @typescript-eslint/no-unused-vars posthogPrivacyMode = false, posthogGroups, ...openAIParams } = body; const traceId = posthogTraceId ?? uuid.v4(); const startTime = Date.now(); const parentPromise = super.create(openAIParams, options); if (openAIParams.stream) { return parentPromise.then(value => { let accumulatedContent = ''; let usage = { inputTokens: 0, outputTokens: 0 }; let model = openAIParams.model; if ('tee' in value) { const [stream1, stream2] = value.tee(); (async () => { try { for await (const chunk of stream1) { const delta = chunk?.choices?.[0]?.delta?.content ?? ''; accumulatedContent += delta; if (chunk.usage) { if (chunk.model != model) { model = chunk.model; } usage = { inputTokens: chunk.usage.prompt_tokens ?? 0, outputTokens: chunk.usage.completion_tokens ?? 0, reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0, cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0 }; } } const latency = (Date.now() - startTime) / 1000; sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model, provider: 'azure', input: openAIParams.messages, output: [{ content: accumulatedContent, role: 'assistant' }], latency, baseURL: this.baseURL ?? '', params: body, httpStatus: 200, usage }); } catch (error) { // error handling sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model, provider: 'azure', input: openAIParams.messages, output: JSON.stringify(error), latency: 0, baseURL: this.baseURL ?? '', params: body, httpStatus: error?.status ? error.status : 500, usage: { inputTokens: 0, outputTokens: 0 }, isError: true, error: JSON.stringify(error) }); } })(); // Return the other stream to the user return stream2; } return value; }); } else { const wrappedPromise = parentPromise.then(result => { if ('choices' in result) { const latency = (Date.now() - startTime) / 1000; let model = openAIParams.model; if (result.model != model) { model = result.model; } sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model, provider: 'azure', input: openAIParams.messages, output: formatResponseOpenAI(result), latency, baseURL: this.baseURL ?? '', params: body, httpStatus: 200, usage: { inputTokens: result.usage?.prompt_tokens ?? 0, outputTokens: result.usage?.completion_tokens ?? 0, reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0, cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0 } }); } return result; }, error => { sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: openAIParams.model, provider: 'azure', input: openAIParams.messages, output: [], latency: 0, baseURL: this.baseURL ?? '', params: body, httpStatus: error?.status ? error.status : 500, usage: { inputTokens: 0, outputTokens: 0 }, isError: true, error: JSON.stringify(error) }); throw error; }); return wrappedPromise; } } } const mapVercelParams = params => { return { temperature: params.temperature, max_tokens: params.maxTokens, top_p: params.topP, frequency_penalty: params.frequencyPenalty, presence_penalty: params.presencePenalty, stop: params.stopSequences, stream: params.stream }; }; const mapVercelPrompt = prompt => { return prompt.map(p => { let content = {}; if (Array.isArray(p.content)) { content = p.content.map(c => { if (c.type === 'text') { return { type: 'text', content: c.text }; } else if (c.type === 'image') { return { type: 'image', content: { // if image is a url use it, or use "none supported" image: c.image instanceof URL ? c.image.toString() : 'raw images not supported', mimeType: c.mimeType } }; } else if (c.type === 'file') { return { type: 'file', content: { file: c.data instanceof URL ? c.data.toString() : 'raw files not supported', mimeType: c.mimeType } }; } else if (c.type === 'tool-call') { return { type: 'tool-call', content: { toolCallId: c.toolCallId, toolName: c.toolName, args: c.args } }; } else if (c.type === 'tool-result') { return { type: 'tool-result', content: { toolCallId: c.toolCallId, toolName: c.toolName, result: c.result, isError: c.isError } }; } return { content: '' }; }); } else { content = { type: 'text', text: p.content }; } return { role: p.role, content }; }); }; const mapVercelOutput = result => { const output = { ...(result.text ? { text: result.text } : {}), ...(result.object ? { object: result.object } : {}), ...(result.reasoning ? { reasoning: result.reasoning } : {}), ...(result.response ? { response: result.response } : {}), ...(result.finishReason ? { finishReason: result.finishReason } : {}), ...(result.usage ? { usage: result.usage } : {}), ...(result.warnings ? { warnings: result.warnings } : {}), ...(result.providerMetadata ? { toolCalls: result.providerMetadata } : {}) }; // if text and no object or reasoning, return text if (output.text && !output.object && !output.reasoning) { return [{ content: output.text, role: 'assistant' }]; } return [{ content: JSON.stringify(output), role: 'assistant' }]; }; const extractProvider = model => { // vercel provider is in the format of provider.endpoint const provider = model.provider.toLowerCase(); const providerName = provider.split('.')[0]; return providerName; }; const createInstrumentationMiddleware = (phClient, model, options) => { const middleware = { wrapGenerate: async ({ doGenerate, params }) => { const startTime = Date.now(); const mergedParams = { ...options, ...mapVercelParams(params) }; try { const result = await doGenerate(); const latency = (Date.now() - startTime) / 1000; const modelId = options.posthogModelOverride ?? (result.response?.modelId ? result.response.modelId : model.modelId); const provider = options.posthogProviderOverride ?? extractProvider(model); const baseURL = ''; // cannot currently get baseURL from vercel const content = mapVercelOutput(result); // let tools = result.toolCalls const providerMetadata = result.providerMetadata; const additionalTokenValues = { ...(providerMetadata?.openai?.reasoningTokens ? { reasoningTokens: providerMetadata.openai.reasoningTokens } : {}), ...(providerMetadata?.openai?.cachedPromptTokens ? { cacheReadInputTokens: providerMetadata.openai.cachedPromptTokens } : {}), ...(providerMetadata?.anthropic ? { cacheReadInputTokens: providerMetadata.anthropic.cacheReadInputTokens, cacheCreationInputTokens: providerMetadata.anthropic.cacheCreationInputTokens } : {}) }; sendEventToPosthog({ client: phClient, distinctId: options.posthogDistinctId, traceId: options.posthogTraceId, model: modelId, provider: provider, input: options.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt), output: [{ content, role: 'assistant' }], latency, baseURL, params: mergedParams, httpStatus: 200, usage: { inputTokens: result.usage.promptTokens, outputTokens: result.usage.completionTokens, ...additionalTokenValues } }); return result; } catch (error) { const modelId = model.modelId; sendEventToPosthog({ client: phClient, distinctId: options.posthogDistinctId, traceId: options.posthogTraceId, model: modelId, provider: model.provider, input: options.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt), output: [], latency: 0, baseURL: '', params: mergedParams, httpStatus: error?.status ? error.status : 500, usage: { inputTokens: 0, outputTokens: 0 }, isError: true, error: JSON.stringify(error) }); throw error; } }, wrapStream: async ({ doStream, params }) => { const startTime = Date.now(); let generatedText = ''; let usage = {}; const mergedParams = { ...options, ...mapVercelParams(params) }; const modelId = options.posthogModelOverride ?? model.modelId; const provider = options.posthogProviderOverride ?? extractProvider(model); const baseURL = ''; // cannot currently get baseURL from vercel try { const { stream, ...rest } = await doStream(); const transformStream = new TransformStream({ transform(chunk, controller) { if (chunk.type === 'text-delta') { generatedText += chunk.textDelta; } if (chunk.type === 'finish') { usage = { inputTokens: chunk.usage?.promptTokens, outputTokens: chunk.usage?.completionTokens }; if (chunk.providerMetadata?.openai?.reasoningTokens) { usage.reasoningTokens = chunk.providerMetadata.openai.reasoningTokens; } if (chunk.providerMetadata?.openai?.cachedPromptTokens) { usage.cacheReadInputTokens = chunk.providerMetadata.openai.cachedPromptTokens; } if (chunk.providerMetadata?.anthropic?.cacheReadInputTokens) { usage.cacheReadInputTokens = chunk.providerMetadata.anthropic.cacheReadInputTokens; } if (chunk.providerMetadata?.anthropic?.cacheCreationInputTokens) { usage.cacheCreationInputTokens = chunk.providerMetadata.anthropic.cacheCreationInputTokens; } } controller.enqueue(chunk); }, flush() { const latency = (Date.now() - startTime) / 1000; sendEventToPosthog({ client: phClient, distinctId: options.posthogDistinctId, traceId: options.posthogTraceId, model: modelId, provider: provider, input: options.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt), output: [{ content: generatedText, role: 'assistant' }], latency, baseURL, params: mergedParams, httpStatus: 200, usage }); } }); return { stream: stream.pipeThrough(transformStream), ...rest }; } catch (error) { sendEventToPosthog({ client: phClient, distinctId: options.posthogDistinctId, traceId: options.posthogTraceId, model: modelId, provider: provider, input: options.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt), output: [], latency: 0, baseURL: '', params: mergedParams, httpStatus: error?.status ? error.status : 500, usage: { inputTokens: 0, outputTokens: 0 }, isError: true, error: JSON.stringify(error) }); throw error; } } }; return middleware; }; const wrapVercelLanguageModel = (model, phClient, options) => { const traceId = options.posthogTraceId ?? uuid.v4(); const middleware = createInstrumentationMiddleware(phClient, model, { ...options, posthogTraceId: traceId, posthogDistinctId: options.posthogDistinctId ?? traceId }); const wrappedModel = ai.experimental_wrapLanguageModel({ model, middleware }); return wrappedModel; }; class PostHogAnthropic extends AnthropicOriginal__default["default"] { constructor(config) { const { posthog, ...anthropicConfig } = config; super(anthropicConfig); this.phClient = posthog; this.messages = new WrappedMessages(this, this.phClient); } } class WrappedMessages extends AnthropicOriginal__default["default"].Messages { constructor(parentClient, phClient) { super(parentClient); this.phClient = phClient; } create(body, options) { const { posthogDistinctId, posthogTraceId, posthogProperties, // eslint-disable-next-line @typescript-eslint/no-unused-vars posthogPrivacyMode = false, posthogGroups, ...anthropicParams } = body; const traceId = posthogTraceId ?? uuid.v4(); const startTime = Date.now(); const parentPromise = super.create(anthropicParams, options); if (anthropicParams.stream) { return parentPromise.then(value => { let accumulatedContent = ''; const usage = { inputTokens: 0, outputTokens: 0, cacheCreationInputTokens: 0, cacheReadInputTokens: 0 }; if ('tee' in value) { const [stream1, stream2] = value.tee(); (async () => { try { for await (const chunk of stream1) { if ('delta' in chunk) { if ('text' in chunk.delta) { const delta = chunk?.delta?.text ?? ''; accumulatedContent += delta; } } if (chunk.type == 'message_start') { usage.inputTokens = chunk.message.usage.input_tokens ?? 0; usage.cacheCreationInputTokens = chunk.message.usage.cache_creation_input_tokens ?? 0; usage.cacheReadInputTokens = chunk.message.usage.cache_read_input_tokens ?? 0; } if ('usage' in chunk) { usage.outputTokens = chunk.usage.output_tokens ?? 0; } } const latency = (Date.now() - startTime) / 1000; sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: anthropicParams.model, provider: 'anthropic', input: mergeSystemPrompt(anthropicParams, 'anthropic'), output: [{ content: accumulatedContent, role: 'assistant' }], latency, baseURL: this.baseURL ?? '', params: body, httpStatus: 200, usage }); } catch (error) { // error handling sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: anthropicParams.model, provider: 'anthropic', input: mergeSystemPrompt(anthropicParams, 'anthropic'), output: [], latency: 0, baseURL: this.baseURL ?? '', params: body, httpStatus: error?.status ? error.status : 500, usage: { inputTokens: 0, outputTokens: 0 }, isError: true, error: JSON.stringify(error) }); } })(); // Return the other stream to the user return stream2; } return value; }); } else { const wrappedPromise = parentPromise.then(result => { if ('content' in result) { const latency = (Date.now() - startTime) / 1000; sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: anthropicParams.model, provider: 'anthropic', input: mergeSystemPrompt(anthropicParams, 'anthropic'), output: formatResponseAnthropic(result), latency, baseURL: this.baseURL ?? '', params: body, httpStatus: 200, usage: { inputTokens: result.usage.input_tokens ?? 0, outputTokens: result.usage.output_tokens ?? 0, cacheCreationInputTokens: result.usage.cache_creation_input_tokens ?? 0, cacheReadInputTokens: result.usage.cache_read_input_tokens ?? 0 } }); } return result; }, error => { sendEventToPosthog({ client: this.phClient, distinctId: posthogDistinctId ?? traceId, traceId, model: anthropicParams.model, provider: 'anthropic', input: mergeSystemPrompt(anthropicParams, 'anthropic'), output: [], latency: 0, baseURL: this.baseURL ?? '', params: body, httpStatus: error?.status ? error.status : 500, usage: { inputTokens: 0, outputTokens: 0 }, isError: true, error: JSON.stringify(error) }); throw error; }); return wrappedPromise; } } } 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__namespace.v4() }); Object.assign(this, methods); } } return new Handler(); } } class LangChainCallbackHandler extends BaseCallbackHandler { constructor(options) { if (!options.client) { throw new Error('PostHog client is required'); } super(); this.name = 'PosthogCallbackHandler'; this.runs = {}; this.parentTree = {}; 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,