@n8n/n8n-nodes-langchain 1.98.1 → 1.99.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +0 -2
  2. package/dist/known/nodes.json +4 -0
  3. package/dist/methods/defined.json +3 -0
  4. package/dist/methods/referenced.json +3 -0
  5. package/dist/nodes/ModelSelector/ModelSelector.node.js +175 -0
  6. package/dist/nodes/ModelSelector/ModelSelector.node.js.map +1 -0
  7. package/dist/nodes/ModelSelector/helpers.js +85 -0
  8. package/dist/nodes/ModelSelector/helpers.js.map +1 -0
  9. package/dist/nodes/agents/Agent/V1/AgentV1.node.js +2 -1
  10. package/dist/nodes/agents/Agent/V1/AgentV1.node.js.map +1 -1
  11. package/dist/nodes/agents/Agent/V2/AgentV2.node.js +2 -1
  12. package/dist/nodes/agents/Agent/V2/AgentV2.node.js.map +1 -1
  13. package/dist/nodes/agents/Agent/agents/utils.js.map +1 -1
  14. package/dist/nodes/embeddings/EmbeddingsGoogleGemini/EmbeddingsGoogleGemini.node.js +4 -3
  15. package/dist/nodes/embeddings/EmbeddingsGoogleGemini/EmbeddingsGoogleGemini.node.js.map +1 -1
  16. package/dist/nodes/embeddings/EmbeddingsMistralCloud/EmbeddingsMistralCloud.node.js +1 -1
  17. package/dist/nodes/embeddings/EmbeddingsMistralCloud/EmbeddingsMistralCloud.node.js.map +1 -1
  18. package/dist/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.js +1 -1
  19. package/dist/nodes/llms/LMChatAnthropic/LmChatAnthropic.node.js.map +1 -1
  20. package/dist/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.js +1 -1
  21. package/dist/nodes/llms/LMChatOpenAi/LmChatOpenAi.node.js.map +1 -1
  22. package/dist/nodes/llms/LMOpenAi/LmOpenAi.node.js +1 -1
  23. package/dist/nodes/llms/LMOpenAi/LmOpenAi.node.js.map +1 -1
  24. package/dist/nodes/llms/LmChatDeepSeek/LmChatDeepSeek.node.js +1 -1
  25. package/dist/nodes/llms/LmChatDeepSeek/LmChatDeepSeek.node.js.map +1 -1
  26. package/dist/nodes/llms/LmChatGoogleGemini/LmChatGoogleGemini.node.js +1 -1
  27. package/dist/nodes/llms/LmChatGoogleGemini/LmChatGoogleGemini.node.js.map +1 -1
  28. package/dist/nodes/llms/LmChatGroq/LmChatGroq.node.js +1 -1
  29. package/dist/nodes/llms/LmChatGroq/LmChatGroq.node.js.map +1 -1
  30. package/dist/nodes/llms/LmChatMistralCloud/LmChatMistralCloud.node.js +1 -1
  31. package/dist/nodes/llms/LmChatMistralCloud/LmChatMistralCloud.node.js.map +1 -1
  32. package/dist/nodes/llms/LmChatOpenRouter/LmChatOpenRouter.node.js +1 -1
  33. package/dist/nodes/llms/LmChatOpenRouter/LmChatOpenRouter.node.js.map +1 -1
  34. package/dist/nodes/llms/LmChatXAiGrok/LmChatXAiGrok.node.js +1 -1
  35. package/dist/nodes/llms/LmChatXAiGrok/LmChatXAiGrok.node.js.map +1 -1
  36. package/dist/nodes/llms/N8nLlmTracing.js +37 -13
  37. package/dist/nodes/llms/N8nLlmTracing.js.map +1 -1
  38. package/dist/nodes/llms/N8nNonEstimatingTracing.js +167 -0
  39. package/dist/nodes/llms/N8nNonEstimatingTracing.js.map +1 -0
  40. package/dist/nodes/mcp/McpTrigger/McpServer.js +18 -0
  41. package/dist/nodes/mcp/McpTrigger/McpServer.js.map +1 -1
  42. package/dist/nodes/mcp/McpTrigger/McpTrigger.node.js +20 -6
  43. package/dist/nodes/mcp/McpTrigger/McpTrigger.node.js.map +1 -1
  44. package/dist/nodes/text_splitters/TextSplitterTokenSplitter/TextSplitterTokenSplitter.node.js +2 -5
  45. package/dist/nodes/text_splitters/TextSplitterTokenSplitter/TextSplitterTokenSplitter.node.js.map +1 -1
  46. package/dist/nodes/text_splitters/TextSplitterTokenSplitter/TokenTextSplitter.js +59 -0
  47. package/dist/nodes/text_splitters/TextSplitterTokenSplitter/TokenTextSplitter.js.map +1 -0
  48. package/dist/nodes/vector_store/VectorStoreMongoDBAtlas/VectorStoreMongoDBAtlas.node.js +20 -8
  49. package/dist/nodes/vector_store/VectorStoreMongoDBAtlas/VectorStoreMongoDBAtlas.node.js.map +1 -1
  50. package/dist/types/nodes.json +6 -5
  51. package/dist/types/types.js +15 -0
  52. package/dist/types/types.js.map +1 -1
  53. package/dist/utils/N8nTool.js.map +1 -1
  54. package/dist/utils/output_parsers/N8nStructuredOutputParser.js +7 -0
  55. package/dist/utils/output_parsers/N8nStructuredOutputParser.js.map +1 -1
  56. package/dist/utils/tokenizer/cl100k_base.json +1 -0
  57. package/dist/utils/tokenizer/o200k_base.json +1 -0
  58. package/dist/utils/tokenizer/tiktoken.js +64 -0
  59. package/dist/utils/tokenizer/tiktoken.js.map +1 -0
  60. package/package.json +26 -24
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../nodes/llms/LmChatOpenRouter/LmChatOpenRouter.node.ts"],"sourcesContent":["/* eslint-disable n8n-nodes-base/node-dirname-against-convention */\n\nimport { ChatOpenAI, type ClientOptions } from '@langchain/openai';\nimport {\n\tNodeConnectionTypes,\n\ttype INodeType,\n\ttype INodeTypeDescription,\n\ttype ISupplyDataFunctions,\n\ttype SupplyData,\n} from 'n8n-workflow';\n\nimport { getHttpProxyAgent } from '@utils/httpProxyAgent';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport { openAiFailedAttemptHandler } from '../../vendors/OpenAi/helpers/error-handling';\nimport { makeN8nLlmFailedAttemptHandler } from '../n8nLlmFailedAttemptHandler';\nimport { N8nLlmTracing } from '../N8nLlmTracing';\n\nexport class LmChatOpenRouter implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'OpenRouter Chat Model',\n\t\tname: 'lmChatOpenRouter',\n\t\ticon: { light: 'file:openrouter.svg', dark: 'file:openrouter.dark.svg' },\n\t\tgroup: ['transform'],\n\t\tversion: [1],\n\t\tdescription: 'For advanced usage with an AI chain',\n\t\tdefaults: {\n\t\t\tname: 'OpenRouter Chat Model',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Language Models', 'Root Nodes'],\n\t\t\t\t'Language Models': ['Chat Models (Recommended)'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatopenrouter/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node\n\t\tinputs: [],\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong\n\t\toutputs: [NodeConnectionTypes.AiLanguageModel],\n\t\toutputNames: ['Model'],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\tname: 'openRouterApi',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\trequestDefaults: {\n\t\t\tignoreHttpStatusErrors: true,\n\t\t\tbaseURL: '={{ $credentials?.url }}',\n\t\t},\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'If using JSON response format, you must include word \"json\" in the prompt in your chain or agent. Also, make sure to select latest models released post November 2023.',\n\t\t\t\tname: 'notice',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'/options.responseFormat': ['json_object'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'options',\n\t\t\t\tdescription:\n\t\t\t\t\t'The model which will generate the completion. <a href=\"https://openrouter.ai/docs/models\">Learn more</a>.',\n\t\t\t\ttypeOptions: {\n\t\t\t\t\tloadOptions: {\n\t\t\t\t\t\trouting: {\n\t\t\t\t\t\t\trequest: {\n\t\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t\turl: '/models',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\t\tpostReceive: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'rootProperty',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tproperty: 'data',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'setKeyValue',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tname: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t\tvalue: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'sort',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tkey: 'name',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trouting: {\n\t\t\t\t\tsend: {\n\t\t\t\t\t\ttype: 'body',\n\t\t\t\t\t\tproperty: 'model',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdefault: 'openai/gpt-4.1-mini',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdescription: 'Additional options to add',\n\t\t\t\ttype: 'collection',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Frequency Penalty',\n\t\t\t\t\t\tname: 'frequencyPenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Maximum Number of Tokens',\n\t\t\t\t\t\tname: 'maxTokens',\n\t\t\t\t\t\tdefault: -1,\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 32,768).',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\ttypeOptions: {\n\t\t\t\t\t\t\tmaxValue: 32768,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Response Format',\n\t\t\t\t\t\tname: 'responseFormat',\n\t\t\t\t\t\tdefault: 'text',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Text',\n\t\t\t\t\t\t\t\tvalue: 'text',\n\t\t\t\t\t\t\t\tdescription: 'Regular text response',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'JSON',\n\t\t\t\t\t\t\t\tvalue: 'json_object',\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t'Enables JSON mode, which should guarantee the message the model generates is valid JSON',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Presence Penalty',\n\t\t\t\t\t\tname: 'presencePenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Sampling Temperature',\n\t\t\t\t\t\tname: 'temperature',\n\t\t\t\t\t\tdefault: 0.7,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Timeout',\n\t\t\t\t\t\tname: 'timeout',\n\t\t\t\t\t\tdefault: 360000,\n\t\t\t\t\t\tdescription: 'Maximum amount of time a request is allowed to take in milliseconds',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Max Retries',\n\t\t\t\t\t\tname: 'maxRetries',\n\t\t\t\t\t\tdefault: 2,\n\t\t\t\t\t\tdescription: 'Maximum number of retries to attempt',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Top P',\n\t\t\t\t\t\tname: 'topP',\n\t\t\t\t\t\tdefault: 1,\n\t\t\t\t\t\ttypeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst credentials = await this.getCredentials<OpenAICompatibleCredential>('openRouterApi');\n\n\t\tconst modelName = this.getNodeParameter('model', itemIndex) as string;\n\n\t\tconst options = this.getNodeParameter('options', itemIndex, {}) as {\n\t\t\tfrequencyPenalty?: number;\n\t\t\tmaxTokens?: number;\n\t\t\tmaxRetries: number;\n\t\t\ttimeout: number;\n\t\t\tpresencePenalty?: number;\n\t\t\ttemperature?: number;\n\t\t\ttopP?: number;\n\t\t\tresponseFormat?: 'text' | 'json_object';\n\t\t};\n\n\t\tconst configuration: ClientOptions = {\n\t\t\tbaseURL: credentials.url,\n\t\t\thttpAgent: getHttpProxyAgent(),\n\t\t};\n\n\t\tconst model = new ChatOpenAI({\n\t\t\topenAIApiKey: credentials.apiKey,\n\t\t\tmodelName,\n\t\t\t...options,\n\t\t\ttimeout: options.timeout ?? 60000,\n\t\t\tmaxRetries: options.maxRetries ?? 2,\n\t\t\tconfiguration,\n\t\t\tcallbacks: [new N8nLlmTracing(this)],\n\t\t\tmodelKwargs: options.responseFormat\n\t\t\t\t? {\n\t\t\t\t\t\tresponse_format: { type: options.responseFormat },\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t\tonFailedAttempt: makeN8nLlmFailedAttemptHandler(this, openAiFailedAttemptHandler),\n\t\t});\n\n\t\treturn {\n\t\t\tresponse: model,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../nodes/llms/LmChatOpenRouter/LmChatOpenRouter.node.ts"],"sourcesContent":["/* eslint-disable n8n-nodes-base/node-dirname-against-convention */\n\nimport { ChatOpenAI, type ClientOptions } from '@langchain/openai';\nimport {\n\tNodeConnectionTypes,\n\ttype INodeType,\n\ttype INodeTypeDescription,\n\ttype ISupplyDataFunctions,\n\ttype SupplyData,\n} from 'n8n-workflow';\n\nimport { getHttpProxyAgent } from '@utils/httpProxyAgent';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport type { OpenAICompatibleCredential } from '../../../types/types';\nimport { openAiFailedAttemptHandler } from '../../vendors/OpenAi/helpers/error-handling';\nimport { makeN8nLlmFailedAttemptHandler } from '../n8nLlmFailedAttemptHandler';\nimport { N8nLlmTracing } from '../N8nLlmTracing';\n\nexport class LmChatOpenRouter implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'OpenRouter Chat Model',\n\t\tname: 'lmChatOpenRouter',\n\t\ticon: { light: 'file:openrouter.svg', dark: 'file:openrouter.dark.svg' },\n\t\tgroup: ['transform'],\n\t\tversion: [1],\n\t\tdescription: 'For advanced usage with an AI chain',\n\t\tdefaults: {\n\t\t\tname: 'OpenRouter Chat Model',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Language Models', 'Root Nodes'],\n\t\t\t\t'Language Models': ['Chat Models (Recommended)'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatopenrouter/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node\n\t\tinputs: [],\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong\n\t\toutputs: [NodeConnectionTypes.AiLanguageModel],\n\t\toutputNames: ['Model'],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\tname: 'openRouterApi',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\trequestDefaults: {\n\t\t\tignoreHttpStatusErrors: true,\n\t\t\tbaseURL: '={{ $credentials?.url }}',\n\t\t},\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'If using JSON response format, you must include word \"json\" in the prompt in your chain or agent. Also, make sure to select latest models released post November 2023.',\n\t\t\t\tname: 'notice',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'/options.responseFormat': ['json_object'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'options',\n\t\t\t\tdescription:\n\t\t\t\t\t'The model which will generate the completion. <a href=\"https://openrouter.ai/docs/models\">Learn more</a>.',\n\t\t\t\ttypeOptions: {\n\t\t\t\t\tloadOptions: {\n\t\t\t\t\t\trouting: {\n\t\t\t\t\t\t\trequest: {\n\t\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t\turl: '/models',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\t\tpostReceive: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'rootProperty',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tproperty: 'data',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'setKeyValue',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tname: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t\tvalue: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'sort',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tkey: 'name',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trouting: {\n\t\t\t\t\tsend: {\n\t\t\t\t\t\ttype: 'body',\n\t\t\t\t\t\tproperty: 'model',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdefault: 'openai/gpt-4.1-mini',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdescription: 'Additional options to add',\n\t\t\t\ttype: 'collection',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Frequency Penalty',\n\t\t\t\t\t\tname: 'frequencyPenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Maximum Number of Tokens',\n\t\t\t\t\t\tname: 'maxTokens',\n\t\t\t\t\t\tdefault: -1,\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 32,768).',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\ttypeOptions: {\n\t\t\t\t\t\t\tmaxValue: 32768,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Response Format',\n\t\t\t\t\t\tname: 'responseFormat',\n\t\t\t\t\t\tdefault: 'text',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Text',\n\t\t\t\t\t\t\t\tvalue: 'text',\n\t\t\t\t\t\t\t\tdescription: 'Regular text response',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'JSON',\n\t\t\t\t\t\t\t\tvalue: 'json_object',\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t'Enables JSON mode, which should guarantee the message the model generates is valid JSON',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Presence Penalty',\n\t\t\t\t\t\tname: 'presencePenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Sampling Temperature',\n\t\t\t\t\t\tname: 'temperature',\n\t\t\t\t\t\tdefault: 0.7,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Timeout',\n\t\t\t\t\t\tname: 'timeout',\n\t\t\t\t\t\tdefault: 360000,\n\t\t\t\t\t\tdescription: 'Maximum amount of time a request is allowed to take in milliseconds',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Max Retries',\n\t\t\t\t\t\tname: 'maxRetries',\n\t\t\t\t\t\tdefault: 2,\n\t\t\t\t\t\tdescription: 'Maximum number of retries to attempt',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Top P',\n\t\t\t\t\t\tname: 'topP',\n\t\t\t\t\t\tdefault: 1,\n\t\t\t\t\t\ttypeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst credentials = await this.getCredentials<OpenAICompatibleCredential>('openRouterApi');\n\n\t\tconst modelName = this.getNodeParameter('model', itemIndex) as string;\n\n\t\tconst options = this.getNodeParameter('options', itemIndex, {}) as {\n\t\t\tfrequencyPenalty?: number;\n\t\t\tmaxTokens?: number;\n\t\t\tmaxRetries: number;\n\t\t\ttimeout: number;\n\t\t\tpresencePenalty?: number;\n\t\t\ttemperature?: number;\n\t\t\ttopP?: number;\n\t\t\tresponseFormat?: 'text' | 'json_object';\n\t\t};\n\n\t\tconst configuration: ClientOptions = {\n\t\t\tbaseURL: credentials.url,\n\t\t\thttpAgent: getHttpProxyAgent(),\n\t\t};\n\n\t\tconst model = new ChatOpenAI({\n\t\t\topenAIApiKey: credentials.apiKey,\n\t\t\tmodel: modelName,\n\t\t\t...options,\n\t\t\ttimeout: options.timeout ?? 60000,\n\t\t\tmaxRetries: options.maxRetries ?? 2,\n\t\t\tconfiguration,\n\t\t\tcallbacks: [new N8nLlmTracing(this)],\n\t\t\tmodelKwargs: options.responseFormat\n\t\t\t\t? {\n\t\t\t\t\t\tresponse_format: { type: options.responseFormat },\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t\tonFailedAttempt: makeN8nLlmFailedAttemptHandler(this, openAiFailedAttemptHandler),\n\t\t});\n\n\t\treturn {\n\t\t\tresponse: model,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -226,7 +226,7 @@ class LmChatXAiGrok {
226
226
  };
227
227
  const model = new import_openai.ChatOpenAI({
228
228
  openAIApiKey: credentials.apiKey,
229
- modelName,
229
+ model: modelName,
230
230
  ...options,
231
231
  timeout: options.timeout ?? 6e4,
232
232
  maxRetries: options.maxRetries ?? 2,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../nodes/llms/LmChatXAiGrok/LmChatXAiGrok.node.ts"],"sourcesContent":["/* eslint-disable n8n-nodes-base/node-dirname-against-convention */\n\nimport { ChatOpenAI, type ClientOptions } from '@langchain/openai';\nimport {\n\tNodeConnectionTypes,\n\ttype INodeType,\n\ttype INodeTypeDescription,\n\ttype ISupplyDataFunctions,\n\ttype SupplyData,\n} from 'n8n-workflow';\n\nimport { getHttpProxyAgent } from '@utils/httpProxyAgent';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport { openAiFailedAttemptHandler } from '../../vendors/OpenAi/helpers/error-handling';\nimport { makeN8nLlmFailedAttemptHandler } from '../n8nLlmFailedAttemptHandler';\nimport { N8nLlmTracing } from '../N8nLlmTracing';\n\nexport class LmChatXAiGrok implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'xAI Grok Chat Model',\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-name-miscased\n\t\tname: 'lmChatXAiGrok',\n\t\ticon: { light: 'file:logo.dark.svg', dark: 'file:logo.svg' },\n\t\tgroup: ['transform'],\n\t\tversion: [1],\n\t\tdescription: 'For advanced usage with an AI chain',\n\t\tdefaults: {\n\t\t\tname: 'xAI Grok Chat Model',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Language Models', 'Root Nodes'],\n\t\t\t\t'Language Models': ['Chat Models (Recommended)'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatxaigrok/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node\n\t\tinputs: [],\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong\n\t\toutputs: [NodeConnectionTypes.AiLanguageModel],\n\t\toutputNames: ['Model'],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\tname: 'xAiApi',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\trequestDefaults: {\n\t\t\tignoreHttpStatusErrors: true,\n\t\t\tbaseURL: '={{ $credentials?.url }}',\n\t\t},\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'If using JSON response format, you must include word \"json\" in the prompt in your chain or agent. Also, make sure to select latest models released post November 2023.',\n\t\t\t\tname: 'notice',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'/options.responseFormat': ['json_object'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'options',\n\t\t\t\tdescription:\n\t\t\t\t\t'The model which will generate the completion. <a href=\"https://docs.x.ai/docs/models\">Learn more</a>.',\n\t\t\t\ttypeOptions: {\n\t\t\t\t\tloadOptions: {\n\t\t\t\t\t\trouting: {\n\t\t\t\t\t\t\trequest: {\n\t\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t\turl: '/models',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\t\tpostReceive: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'rootProperty',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tproperty: 'data',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'setKeyValue',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tname: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t\tvalue: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'sort',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tkey: 'name',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trouting: {\n\t\t\t\t\tsend: {\n\t\t\t\t\t\ttype: 'body',\n\t\t\t\t\t\tproperty: 'model',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdefault: 'grok-2-vision-1212',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdescription: 'Additional options to add',\n\t\t\t\ttype: 'collection',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Frequency Penalty',\n\t\t\t\t\t\tname: 'frequencyPenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Maximum Number of Tokens',\n\t\t\t\t\t\tname: 'maxTokens',\n\t\t\t\t\t\tdefault: -1,\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 32,768).',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\ttypeOptions: {\n\t\t\t\t\t\t\tmaxValue: 32768,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Response Format',\n\t\t\t\t\t\tname: 'responseFormat',\n\t\t\t\t\t\tdefault: 'text',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Text',\n\t\t\t\t\t\t\t\tvalue: 'text',\n\t\t\t\t\t\t\t\tdescription: 'Regular text response',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'JSON',\n\t\t\t\t\t\t\t\tvalue: 'json_object',\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t'Enables JSON mode, which should guarantee the message the model generates is valid JSON',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Presence Penalty',\n\t\t\t\t\t\tname: 'presencePenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Sampling Temperature',\n\t\t\t\t\t\tname: 'temperature',\n\t\t\t\t\t\tdefault: 0.7,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Timeout',\n\t\t\t\t\t\tname: 'timeout',\n\t\t\t\t\t\tdefault: 360000,\n\t\t\t\t\t\tdescription: 'Maximum amount of time a request is allowed to take in milliseconds',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Max Retries',\n\t\t\t\t\t\tname: 'maxRetries',\n\t\t\t\t\t\tdefault: 2,\n\t\t\t\t\t\tdescription: 'Maximum number of retries to attempt',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Top P',\n\t\t\t\t\t\tname: 'topP',\n\t\t\t\t\t\tdefault: 1,\n\t\t\t\t\t\ttypeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst credentials = await this.getCredentials<OpenAICompatibleCredential>('xAiApi');\n\n\t\tconst modelName = this.getNodeParameter('model', itemIndex) as string;\n\n\t\tconst options = this.getNodeParameter('options', itemIndex, {}) as {\n\t\t\tfrequencyPenalty?: number;\n\t\t\tmaxTokens?: number;\n\t\t\tmaxRetries: number;\n\t\t\ttimeout: number;\n\t\t\tpresencePenalty?: number;\n\t\t\ttemperature?: number;\n\t\t\ttopP?: number;\n\t\t\tresponseFormat?: 'text' | 'json_object';\n\t\t};\n\n\t\tconst configuration: ClientOptions = {\n\t\t\tbaseURL: credentials.url,\n\t\t\thttpAgent: getHttpProxyAgent(),\n\t\t};\n\n\t\tconst model = new ChatOpenAI({\n\t\t\topenAIApiKey: credentials.apiKey,\n\t\t\tmodelName,\n\t\t\t...options,\n\t\t\ttimeout: options.timeout ?? 60000,\n\t\t\tmaxRetries: options.maxRetries ?? 2,\n\t\t\tconfiguration,\n\t\t\tcallbacks: [new N8nLlmTracing(this)],\n\t\t\tmodelKwargs: {\n\t\t\t\tstream_options: undefined,\n\t\t\t\t...(options.responseFormat\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tresponse_format: { type: options.responseFormat },\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined),\n\t\t\t},\n\t\t\tonFailedAttempt: makeN8nLlmFailedAttemptHandler(this, openAiFailedAttemptHandler),\n\t\t});\n\n\t\treturn {\n\t\t\tresponse: model,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../nodes/llms/LmChatXAiGrok/LmChatXAiGrok.node.ts"],"sourcesContent":["/* eslint-disable n8n-nodes-base/node-dirname-against-convention */\n\nimport { ChatOpenAI, type ClientOptions } from '@langchain/openai';\nimport {\n\tNodeConnectionTypes,\n\ttype INodeType,\n\ttype INodeTypeDescription,\n\ttype ISupplyDataFunctions,\n\ttype SupplyData,\n} from 'n8n-workflow';\n\nimport { getHttpProxyAgent } from '@utils/httpProxyAgent';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\nimport type { OpenAICompatibleCredential } from '../../../types/types';\nimport { openAiFailedAttemptHandler } from '../../vendors/OpenAi/helpers/error-handling';\nimport { makeN8nLlmFailedAttemptHandler } from '../n8nLlmFailedAttemptHandler';\nimport { N8nLlmTracing } from '../N8nLlmTracing';\n\nexport class LmChatXAiGrok implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'xAI Grok Chat Model',\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-name-miscased\n\t\tname: 'lmChatXAiGrok',\n\t\ticon: { light: 'file:logo.dark.svg', dark: 'file:logo.svg' },\n\t\tgroup: ['transform'],\n\t\tversion: [1],\n\t\tdescription: 'For advanced usage with an AI chain',\n\t\tdefaults: {\n\t\t\tname: 'xAI Grok Chat Model',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Language Models', 'Root Nodes'],\n\t\t\t\t'Language Models': ['Chat Models (Recommended)'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatxaigrok/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node\n\t\tinputs: [],\n\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong\n\t\toutputs: [NodeConnectionTypes.AiLanguageModel],\n\t\toutputNames: ['Model'],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\tname: 'xAiApi',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\trequestDefaults: {\n\t\t\tignoreHttpStatusErrors: true,\n\t\t\tbaseURL: '={{ $credentials?.url }}',\n\t\t},\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiChain, NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName:\n\t\t\t\t\t'If using JSON response format, you must include word \"json\" in the prompt in your chain or agent. Also, make sure to select latest models released post November 2023.',\n\t\t\t\tname: 'notice',\n\t\t\t\ttype: 'notice',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\t'/options.responseFormat': ['json_object'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Model',\n\t\t\t\tname: 'model',\n\t\t\t\ttype: 'options',\n\t\t\t\tdescription:\n\t\t\t\t\t'The model which will generate the completion. <a href=\"https://docs.x.ai/docs/models\">Learn more</a>.',\n\t\t\t\ttypeOptions: {\n\t\t\t\t\tloadOptions: {\n\t\t\t\t\t\trouting: {\n\t\t\t\t\t\t\trequest: {\n\t\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t\turl: '/models',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\toutput: {\n\t\t\t\t\t\t\t\tpostReceive: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'rootProperty',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tproperty: 'data',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'setKeyValue',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tname: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t\tvalue: '={{$responseItem.id}}',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: 'sort',\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tkey: 'name',\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trouting: {\n\t\t\t\t\tsend: {\n\t\t\t\t\t\ttype: 'body',\n\t\t\t\t\t\tproperty: 'model',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdefault: 'grok-2-vision-1212',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdescription: 'Additional options to add',\n\t\t\t\ttype: 'collection',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Frequency Penalty',\n\t\t\t\t\t\tname: 'frequencyPenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Maximum Number of Tokens',\n\t\t\t\t\t\tname: 'maxTokens',\n\t\t\t\t\t\tdefault: -1,\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'The maximum number of tokens to generate in the completion. Most models have a context length of 2048 tokens (except for the newest models, which support 32,768).',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\ttypeOptions: {\n\t\t\t\t\t\t\tmaxValue: 32768,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Response Format',\n\t\t\t\t\t\tname: 'responseFormat',\n\t\t\t\t\t\tdefault: 'text',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Text',\n\t\t\t\t\t\t\t\tvalue: 'text',\n\t\t\t\t\t\t\t\tdescription: 'Regular text response',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'JSON',\n\t\t\t\t\t\t\t\tvalue: 'json_object',\n\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t'Enables JSON mode, which should guarantee the message the model generates is valid JSON',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Presence Penalty',\n\t\t\t\t\t\tname: 'presencePenalty',\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: -2, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics\",\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Sampling Temperature',\n\t\t\t\t\t\tname: 'temperature',\n\t\t\t\t\t\tdefault: 0.7,\n\t\t\t\t\t\ttypeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Timeout',\n\t\t\t\t\t\tname: 'timeout',\n\t\t\t\t\t\tdefault: 360000,\n\t\t\t\t\t\tdescription: 'Maximum amount of time a request is allowed to take in milliseconds',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Max Retries',\n\t\t\t\t\t\tname: 'maxRetries',\n\t\t\t\t\t\tdefault: 2,\n\t\t\t\t\t\tdescription: 'Maximum number of retries to attempt',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Top P',\n\t\t\t\t\t\tname: 'topP',\n\t\t\t\t\t\tdefault: 1,\n\t\t\t\t\t\ttypeOptions: { maxValue: 1, minValue: 0, numberPrecision: 1 },\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered. We generally recommend altering this or temperature but not both.',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst credentials = await this.getCredentials<OpenAICompatibleCredential>('xAiApi');\n\n\t\tconst modelName = this.getNodeParameter('model', itemIndex) as string;\n\n\t\tconst options = this.getNodeParameter('options', itemIndex, {}) as {\n\t\t\tfrequencyPenalty?: number;\n\t\t\tmaxTokens?: number;\n\t\t\tmaxRetries: number;\n\t\t\ttimeout: number;\n\t\t\tpresencePenalty?: number;\n\t\t\ttemperature?: number;\n\t\t\ttopP?: number;\n\t\t\tresponseFormat?: 'text' | 'json_object';\n\t\t};\n\n\t\tconst configuration: ClientOptions = {\n\t\t\tbaseURL: credentials.url,\n\t\t\thttpAgent: getHttpProxyAgent(),\n\t\t};\n\n\t\tconst model = new ChatOpenAI({\n\t\t\topenAIApiKey: credentials.apiKey,\n\t\t\tmodel: modelName,\n\t\t\t...options,\n\t\t\ttimeout: options.timeout ?? 60000,\n\t\t\tmaxRetries: options.maxRetries ?? 2,\n\t\t\tconfiguration,\n\t\t\tcallbacks: [new N8nLlmTracing(this)],\n\t\t\tmodelKwargs: {\n\t\t\t\tstream_options: undefined,\n\t\t\t\t...(options.responseFormat\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tresponse_format: { type: options.responseFormat },\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined),\n\t\t\t},\n\t\t\tonFailedAttempt: makeN8nLlmFailedAttemptHandler(this, openAiFailedAttemptHandler),\n\t\t});\n\n\t\treturn {\n\t\t\tresponse: model,\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __typeError = (msg) => {
9
+ throw TypeError(msg);
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -26,6 +29,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
29
  mod
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
33
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
34
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
35
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
29
36
  var N8nLlmTracing_exports = {};
30
37
  __export(N8nLlmTracing_exports, {
31
38
  N8nLlmTracing: () => N8nLlmTracing
@@ -33,10 +40,11 @@ __export(N8nLlmTracing_exports, {
33
40
  module.exports = __toCommonJS(N8nLlmTracing_exports);
34
41
  var import_base = require("@langchain/core/callbacks/base");
35
42
  var import_base2 = require("@langchain/core/language_models/base");
36
- var import_tiktoken = require("@langchain/core/utils/tiktoken");
37
43
  var import_pick = __toESM(require("lodash/pick"));
38
44
  var import_n8n_workflow = require("n8n-workflow");
39
45
  var import_helpers = require("../../utils/helpers");
46
+ var import_tiktoken = require("../../utils/tokenizer/tiktoken");
47
+ var _parentRunIndex;
40
48
  const TIKTOKEN_ESTIMATE_MODEL = "gpt-4o";
41
49
  class N8nLlmTracing extends import_base.BaseCallbackHandler {
42
50
  constructor(executionFunctions, options) {
@@ -49,6 +57,7 @@ class N8nLlmTracing extends import_base.BaseCallbackHandler {
49
57
  this.connectionType = import_n8n_workflow.NodeConnectionTypes.AiLanguageModel;
50
58
  this.promptTokensEstimate = 0;
51
59
  this.completionTokensEstimate = 0;
60
+ __privateAdd(this, _parentRunIndex);
52
61
  /**
53
62
  * A map to associate LLM run IDs to run details.
54
63
  * Key: Unique identifier for each LLM run (run ID)
@@ -114,9 +123,14 @@ class N8nLlmTracing extends import_base.BaseCallbackHandler {
114
123
  if (typeof message?.toJSON === "function") return message.toJSON();
115
124
  return message;
116
125
  });
117
- this.executionFunctions.addOutputData(this.connectionType, runDetails.index, [
118
- [{ json: { ...response } }]
119
- ]);
126
+ const sourceNodeRunIndex = __privateGet(this, _parentRunIndex) !== void 0 ? __privateGet(this, _parentRunIndex) + runDetails.index : void 0;
127
+ this.executionFunctions.addOutputData(
128
+ this.connectionType,
129
+ runDetails.index,
130
+ [[{ json: { ...response } }]],
131
+ void 0,
132
+ sourceNodeRunIndex
133
+ );
120
134
  (0, import_helpers.logAiEvent)(this.executionFunctions, "ai-llm-generated-output", {
121
135
  messages: parsedMessages,
122
136
  options: runDetails.options,
@@ -125,18 +139,23 @@ class N8nLlmTracing extends import_base.BaseCallbackHandler {
125
139
  }
126
140
  async handleLLMStart(llm, prompts, runId) {
127
141
  const estimatedTokens = await this.estimateTokensFromStringList(prompts);
142
+ const sourceNodeRunIndex = __privateGet(this, _parentRunIndex) !== void 0 ? __privateGet(this, _parentRunIndex) + this.executionFunctions.getNextRunIndex() : void 0;
128
143
  const options = llm.type === "constructor" ? llm.kwargs : llm;
129
- const { index } = this.executionFunctions.addInputData(this.connectionType, [
144
+ const { index } = this.executionFunctions.addInputData(
145
+ this.connectionType,
130
146
  [
131
- {
132
- json: {
133
- messages: prompts,
134
- estimatedTokens,
135
- options
147
+ [
148
+ {
149
+ json: {
150
+ messages: prompts,
151
+ estimatedTokens,
152
+ options
153
+ }
136
154
  }
137
- }
138
- ]
139
- ]);
155
+ ]
156
+ ],
157
+ sourceNodeRunIndex
158
+ );
140
159
  this.runsMap[runId] = {
141
160
  index,
142
161
  options,
@@ -174,7 +193,12 @@ class N8nLlmTracing extends import_base.BaseCallbackHandler {
174
193
  parentRunId
175
194
  });
176
195
  }
196
+ // Used to associate subsequent runs with the correct parent run in subnodes of subnodes
197
+ setParentRunIndex(runIndex) {
198
+ __privateSet(this, _parentRunIndex, runIndex);
199
+ }
177
200
  }
201
+ _parentRunIndex = new WeakMap();
178
202
  // Annotate the CommonJS export names for ESM import in node:
179
203
  0 && (module.exports = {
180
204
  N8nLlmTracing
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../nodes/llms/N8nLlmTracing.ts"],"sourcesContent":["import { BaseCallbackHandler } from '@langchain/core/callbacks/base';\nimport type { SerializedFields } from '@langchain/core/dist/load/map_keys';\nimport { getModelNameForTiktoken } from '@langchain/core/language_models/base';\nimport type {\n\tSerialized,\n\tSerializedNotImplemented,\n\tSerializedSecret,\n} from '@langchain/core/load/serializable';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { LLMResult } from '@langchain/core/outputs';\nimport { encodingForModel } from '@langchain/core/utils/tiktoken';\nimport pick from 'lodash/pick';\nimport type { IDataObject, ISupplyDataFunctions, JsonObject } from 'n8n-workflow';\nimport { NodeConnectionTypes, NodeError, NodeOperationError } from 'n8n-workflow';\n\nimport { logAiEvent } from '@utils/helpers';\n\ntype TokensUsageParser = (llmOutput: LLMResult['llmOutput']) => {\n\tcompletionTokens: number;\n\tpromptTokens: number;\n\ttotalTokens: number;\n};\n\ntype RunDetail = {\n\tindex: number;\n\tmessages: BaseMessage[] | string[] | string;\n\toptions: SerializedSecret | SerializedNotImplemented | SerializedFields;\n};\n\nconst TIKTOKEN_ESTIMATE_MODEL = 'gpt-4o';\nexport class N8nLlmTracing extends BaseCallbackHandler {\n\tname = 'N8nLlmTracing';\n\n\t// This flag makes sure that LangChain will wait for the handlers to finish before continuing\n\t// This is crucial for the handleLLMError handler to work correctly (it should be called before the error is propagated to the root node)\n\tawaitHandlers = true;\n\n\tconnectionType = NodeConnectionTypes.AiLanguageModel;\n\n\tpromptTokensEstimate = 0;\n\n\tcompletionTokensEstimate = 0;\n\n\t/**\n\t * A map to associate LLM run IDs to run details.\n\t * Key: Unique identifier for each LLM run (run ID)\n\t * Value: RunDetails object\n\t *\n\t */\n\trunsMap: Record<string, RunDetail> = {};\n\n\toptions = {\n\t\t// Default(OpenAI format) parser\n\t\ttokensUsageParser: (llmOutput: LLMResult['llmOutput']) => {\n\t\t\tconst completionTokens = (llmOutput?.tokenUsage?.completionTokens as number) ?? 0;\n\t\t\tconst promptTokens = (llmOutput?.tokenUsage?.promptTokens as number) ?? 0;\n\n\t\t\treturn {\n\t\t\t\tcompletionTokens,\n\t\t\t\tpromptTokens,\n\t\t\t\ttotalTokens: completionTokens + promptTokens,\n\t\t\t};\n\t\t},\n\t\terrorDescriptionMapper: (error: NodeError) => error.description,\n\t};\n\n\tconstructor(\n\t\tprivate executionFunctions: ISupplyDataFunctions,\n\t\toptions?: {\n\t\t\ttokensUsageParser?: TokensUsageParser;\n\t\t\terrorDescriptionMapper?: (error: NodeError) => string;\n\t\t},\n\t) {\n\t\tsuper();\n\t\tthis.options = { ...this.options, ...options };\n\t}\n\n\tasync estimateTokensFromGeneration(generations: LLMResult['generations']) {\n\t\tconst messages = generations.flatMap((gen) => gen.map((g) => g.text));\n\t\treturn await this.estimateTokensFromStringList(messages);\n\t}\n\n\tasync estimateTokensFromStringList(list: string[]) {\n\t\tconst embeddingModel = getModelNameForTiktoken(TIKTOKEN_ESTIMATE_MODEL);\n\t\tconst encoder = await encodingForModel(embeddingModel);\n\n\t\tconst encodedListLength = await Promise.all(\n\t\t\tlist.map(async (text) => encoder.encode(text).length),\n\t\t);\n\n\t\treturn encodedListLength.reduce((acc, curr) => acc + curr, 0);\n\t}\n\n\tasync handleLLMEnd(output: LLMResult, runId: string) {\n\t\t// The fallback should never happen since handleLLMStart should always set the run details\n\t\t// but just in case, we set the index to the length of the runsMap\n\t\tconst runDetails = this.runsMap[runId] ?? { index: Object.keys(this.runsMap).length };\n\n\t\toutput.generations = output.generations.map((gen) =>\n\t\t\tgen.map((g) => pick(g, ['text', 'generationInfo'])),\n\t\t);\n\n\t\tconst tokenUsageEstimate = {\n\t\t\tcompletionTokens: 0,\n\t\t\tpromptTokens: 0,\n\t\t\ttotalTokens: 0,\n\t\t};\n\t\tconst tokenUsage = this.options.tokensUsageParser(output.llmOutput);\n\n\t\tif (output.generations.length > 0) {\n\t\t\ttokenUsageEstimate.completionTokens = await this.estimateTokensFromGeneration(\n\t\t\t\toutput.generations,\n\t\t\t);\n\n\t\t\ttokenUsageEstimate.promptTokens = this.promptTokensEstimate;\n\t\t\ttokenUsageEstimate.totalTokens =\n\t\t\t\ttokenUsageEstimate.completionTokens + this.promptTokensEstimate;\n\t\t}\n\t\tconst response: {\n\t\t\tresponse: { generations: LLMResult['generations'] };\n\t\t\ttokenUsageEstimate?: typeof tokenUsageEstimate;\n\t\t\ttokenUsage?: typeof tokenUsage;\n\t\t} = {\n\t\t\tresponse: { generations: output.generations },\n\t\t};\n\n\t\t// If the LLM response contains actual tokens usage, otherwise fallback to the estimate\n\t\tif (tokenUsage.completionTokens > 0) {\n\t\t\tresponse.tokenUsage = tokenUsage;\n\t\t} else {\n\t\t\tresponse.tokenUsageEstimate = tokenUsageEstimate;\n\t\t}\n\n\t\tconst parsedMessages =\n\t\t\ttypeof runDetails.messages === 'string'\n\t\t\t\t? runDetails.messages\n\t\t\t\t: runDetails.messages.map((message) => {\n\t\t\t\t\t\tif (typeof message === 'string') return message;\n\t\t\t\t\t\tif (typeof message?.toJSON === 'function') return message.toJSON();\n\n\t\t\t\t\t\treturn message;\n\t\t\t\t\t});\n\n\t\tthis.executionFunctions.addOutputData(this.connectionType, runDetails.index, [\n\t\t\t[{ json: { ...response } }],\n\t\t]);\n\n\t\tlogAiEvent(this.executionFunctions, 'ai-llm-generated-output', {\n\t\t\tmessages: parsedMessages,\n\t\t\toptions: runDetails.options,\n\t\t\tresponse,\n\t\t});\n\t}\n\n\tasync handleLLMStart(llm: Serialized, prompts: string[], runId: string) {\n\t\tconst estimatedTokens = await this.estimateTokensFromStringList(prompts);\n\n\t\tconst options = llm.type === 'constructor' ? llm.kwargs : llm;\n\t\tconst { index } = this.executionFunctions.addInputData(this.connectionType, [\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tjson: {\n\t\t\t\t\t\tmessages: prompts,\n\t\t\t\t\t\testimatedTokens,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t]);\n\n\t\t// Save the run details for later use when processing `handleLLMEnd` event\n\t\tthis.runsMap[runId] = {\n\t\t\tindex,\n\t\t\toptions,\n\t\t\tmessages: prompts,\n\t\t};\n\t\tthis.promptTokensEstimate = estimatedTokens;\n\t}\n\n\tasync handleLLMError(\n\t\terror: IDataObject | Error,\n\t\trunId: string,\n\t\tparentRunId?: string | undefined,\n\t) {\n\t\tconst runDetails = this.runsMap[runId] ?? { index: Object.keys(this.runsMap).length };\n\n\t\t// Filter out non-x- headers to avoid leaking sensitive information in logs\n\t\tif (typeof error === 'object' && error?.hasOwnProperty('headers')) {\n\t\t\tconst errorWithHeaders = error as { headers: Record<string, unknown> };\n\n\t\t\tObject.keys(errorWithHeaders.headers).forEach((key) => {\n\t\t\t\tif (!key.startsWith('x-')) {\n\t\t\t\t\tdelete errorWithHeaders.headers[key];\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tif (error instanceof NodeError) {\n\t\t\tif (this.options.errorDescriptionMapper) {\n\t\t\t\terror.description = this.options.errorDescriptionMapper(error);\n\t\t\t}\n\n\t\t\tthis.executionFunctions.addOutputData(this.connectionType, runDetails.index, error);\n\t\t} else {\n\t\t\t// If the error is not a NodeError, we wrap it in a NodeOperationError\n\t\t\tthis.executionFunctions.addOutputData(\n\t\t\t\tthis.connectionType,\n\t\t\t\trunDetails.index,\n\t\t\t\tnew NodeOperationError(this.executionFunctions.getNode(), error as JsonObject, {\n\t\t\t\t\tfunctionality: 'configuration-node',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tlogAiEvent(this.executionFunctions, 'ai-llm-errored', {\n\t\t\terror: Object.keys(error).length === 0 ? error.toString() : error,\n\t\t\trunId,\n\t\t\tparentRunId,\n\t\t});\n\t}\n}\n"],"mappings":"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","names":["import_base","pick"]}
1
+ {"version":3,"sources":["../../../nodes/llms/N8nLlmTracing.ts"],"sourcesContent":["import { BaseCallbackHandler } from '@langchain/core/callbacks/base';\nimport type { SerializedFields } from '@langchain/core/dist/load/map_keys';\nimport { getModelNameForTiktoken } from '@langchain/core/language_models/base';\nimport type {\n\tSerialized,\n\tSerializedNotImplemented,\n\tSerializedSecret,\n} from '@langchain/core/load/serializable';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { LLMResult } from '@langchain/core/outputs';\nimport pick from 'lodash/pick';\nimport type { IDataObject, ISupplyDataFunctions, JsonObject } from 'n8n-workflow';\nimport { NodeConnectionTypes, NodeError, NodeOperationError } from 'n8n-workflow';\n\nimport { logAiEvent } from '@utils/helpers';\nimport { encodingForModel } from '@utils/tokenizer/tiktoken';\n\ntype TokensUsageParser = (llmOutput: LLMResult['llmOutput']) => {\n\tcompletionTokens: number;\n\tpromptTokens: number;\n\ttotalTokens: number;\n};\n\ntype RunDetail = {\n\tindex: number;\n\tmessages: BaseMessage[] | string[] | string;\n\toptions: SerializedSecret | SerializedNotImplemented | SerializedFields;\n};\n\nconst TIKTOKEN_ESTIMATE_MODEL = 'gpt-4o';\nexport class N8nLlmTracing extends BaseCallbackHandler {\n\tname = 'N8nLlmTracing';\n\n\t// This flag makes sure that LangChain will wait for the handlers to finish before continuing\n\t// This is crucial for the handleLLMError handler to work correctly (it should be called before the error is propagated to the root node)\n\tawaitHandlers = true;\n\n\tconnectionType = NodeConnectionTypes.AiLanguageModel;\n\n\tpromptTokensEstimate = 0;\n\n\tcompletionTokensEstimate = 0;\n\n\t#parentRunIndex?: number;\n\n\t/**\n\t * A map to associate LLM run IDs to run details.\n\t * Key: Unique identifier for each LLM run (run ID)\n\t * Value: RunDetails object\n\t *\n\t */\n\trunsMap: Record<string, RunDetail> = {};\n\n\toptions = {\n\t\t// Default(OpenAI format) parser\n\t\ttokensUsageParser: (llmOutput: LLMResult['llmOutput']) => {\n\t\t\tconst completionTokens = (llmOutput?.tokenUsage?.completionTokens as number) ?? 0;\n\t\t\tconst promptTokens = (llmOutput?.tokenUsage?.promptTokens as number) ?? 0;\n\n\t\t\treturn {\n\t\t\t\tcompletionTokens,\n\t\t\t\tpromptTokens,\n\t\t\t\ttotalTokens: completionTokens + promptTokens,\n\t\t\t};\n\t\t},\n\t\terrorDescriptionMapper: (error: NodeError) => error.description,\n\t};\n\n\tconstructor(\n\t\tprivate executionFunctions: ISupplyDataFunctions,\n\t\toptions?: {\n\t\t\ttokensUsageParser?: TokensUsageParser;\n\t\t\terrorDescriptionMapper?: (error: NodeError) => string;\n\t\t},\n\t) {\n\t\tsuper();\n\t\tthis.options = { ...this.options, ...options };\n\t}\n\n\tasync estimateTokensFromGeneration(generations: LLMResult['generations']) {\n\t\tconst messages = generations.flatMap((gen) => gen.map((g) => g.text));\n\t\treturn await this.estimateTokensFromStringList(messages);\n\t}\n\n\tasync estimateTokensFromStringList(list: string[]) {\n\t\tconst embeddingModel = getModelNameForTiktoken(TIKTOKEN_ESTIMATE_MODEL);\n\t\tconst encoder = await encodingForModel(embeddingModel);\n\n\t\tconst encodedListLength = await Promise.all(\n\t\t\tlist.map(async (text) => encoder.encode(text).length),\n\t\t);\n\n\t\treturn encodedListLength.reduce((acc, curr) => acc + curr, 0);\n\t}\n\n\tasync handleLLMEnd(output: LLMResult, runId: string) {\n\t\t// The fallback should never happen since handleLLMStart should always set the run details\n\t\t// but just in case, we set the index to the length of the runsMap\n\t\tconst runDetails = this.runsMap[runId] ?? { index: Object.keys(this.runsMap).length };\n\n\t\toutput.generations = output.generations.map((gen) =>\n\t\t\tgen.map((g) => pick(g, ['text', 'generationInfo'])),\n\t\t);\n\n\t\tconst tokenUsageEstimate = {\n\t\t\tcompletionTokens: 0,\n\t\t\tpromptTokens: 0,\n\t\t\ttotalTokens: 0,\n\t\t};\n\t\tconst tokenUsage = this.options.tokensUsageParser(output.llmOutput);\n\n\t\tif (output.generations.length > 0) {\n\t\t\ttokenUsageEstimate.completionTokens = await this.estimateTokensFromGeneration(\n\t\t\t\toutput.generations,\n\t\t\t);\n\n\t\t\ttokenUsageEstimate.promptTokens = this.promptTokensEstimate;\n\t\t\ttokenUsageEstimate.totalTokens =\n\t\t\t\ttokenUsageEstimate.completionTokens + this.promptTokensEstimate;\n\t\t}\n\t\tconst response: {\n\t\t\tresponse: { generations: LLMResult['generations'] };\n\t\t\ttokenUsageEstimate?: typeof tokenUsageEstimate;\n\t\t\ttokenUsage?: typeof tokenUsage;\n\t\t} = {\n\t\t\tresponse: { generations: output.generations },\n\t\t};\n\n\t\t// If the LLM response contains actual tokens usage, otherwise fallback to the estimate\n\t\tif (tokenUsage.completionTokens > 0) {\n\t\t\tresponse.tokenUsage = tokenUsage;\n\t\t} else {\n\t\t\tresponse.tokenUsageEstimate = tokenUsageEstimate;\n\t\t}\n\n\t\tconst parsedMessages =\n\t\t\ttypeof runDetails.messages === 'string'\n\t\t\t\t? runDetails.messages\n\t\t\t\t: runDetails.messages.map((message) => {\n\t\t\t\t\t\tif (typeof message === 'string') return message;\n\t\t\t\t\t\tif (typeof message?.toJSON === 'function') return message.toJSON();\n\n\t\t\t\t\t\treturn message;\n\t\t\t\t\t});\n\n\t\tconst sourceNodeRunIndex =\n\t\t\tthis.#parentRunIndex !== undefined ? this.#parentRunIndex + runDetails.index : undefined;\n\n\t\tthis.executionFunctions.addOutputData(\n\t\t\tthis.connectionType,\n\t\t\trunDetails.index,\n\t\t\t[[{ json: { ...response } }]],\n\t\t\tundefined,\n\t\t\tsourceNodeRunIndex,\n\t\t);\n\n\t\tlogAiEvent(this.executionFunctions, 'ai-llm-generated-output', {\n\t\t\tmessages: parsedMessages,\n\t\t\toptions: runDetails.options,\n\t\t\tresponse,\n\t\t});\n\t}\n\n\tasync handleLLMStart(llm: Serialized, prompts: string[], runId: string) {\n\t\tconst estimatedTokens = await this.estimateTokensFromStringList(prompts);\n\t\tconst sourceNodeRunIndex =\n\t\t\tthis.#parentRunIndex !== undefined\n\t\t\t\t? this.#parentRunIndex + this.executionFunctions.getNextRunIndex()\n\t\t\t\t: undefined;\n\n\t\tconst options = llm.type === 'constructor' ? llm.kwargs : llm;\n\t\tconst { index } = this.executionFunctions.addInputData(\n\t\t\tthis.connectionType,\n\t\t\t[\n\t\t\t\t[\n\t\t\t\t\t{\n\t\t\t\t\t\tjson: {\n\t\t\t\t\t\t\tmessages: prompts,\n\t\t\t\t\t\t\testimatedTokens,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t],\n\t\t\tsourceNodeRunIndex,\n\t\t);\n\n\t\t// Save the run details for later use when processing `handleLLMEnd` event\n\t\tthis.runsMap[runId] = {\n\t\t\tindex,\n\t\t\toptions,\n\t\t\tmessages: prompts,\n\t\t};\n\t\tthis.promptTokensEstimate = estimatedTokens;\n\t}\n\n\tasync handleLLMError(\n\t\terror: IDataObject | Error,\n\t\trunId: string,\n\t\tparentRunId?: string | undefined,\n\t) {\n\t\tconst runDetails = this.runsMap[runId] ?? { index: Object.keys(this.runsMap).length };\n\n\t\t// Filter out non-x- headers to avoid leaking sensitive information in logs\n\t\tif (typeof error === 'object' && error?.hasOwnProperty('headers')) {\n\t\t\tconst errorWithHeaders = error as { headers: Record<string, unknown> };\n\n\t\t\tObject.keys(errorWithHeaders.headers).forEach((key) => {\n\t\t\t\tif (!key.startsWith('x-')) {\n\t\t\t\t\tdelete errorWithHeaders.headers[key];\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tif (error instanceof NodeError) {\n\t\t\tif (this.options.errorDescriptionMapper) {\n\t\t\t\terror.description = this.options.errorDescriptionMapper(error);\n\t\t\t}\n\n\t\t\tthis.executionFunctions.addOutputData(this.connectionType, runDetails.index, error);\n\t\t} else {\n\t\t\t// If the error is not a NodeError, we wrap it in a NodeOperationError\n\t\t\tthis.executionFunctions.addOutputData(\n\t\t\t\tthis.connectionType,\n\t\t\t\trunDetails.index,\n\t\t\t\tnew NodeOperationError(this.executionFunctions.getNode(), error as JsonObject, {\n\t\t\t\t\tfunctionality: 'configuration-node',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tlogAiEvent(this.executionFunctions, 'ai-llm-errored', {\n\t\t\terror: Object.keys(error).length === 0 ? error.toString() : error,\n\t\t\trunId,\n\t\t\tparentRunId,\n\t\t});\n\t}\n\n\t// Used to associate subsequent runs with the correct parent run in subnodes of subnodes\n\tsetParentRunIndex(runIndex: number) {\n\t\tthis.#parentRunIndex = runIndex;\n\t}\n}\n"],"mappings":"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","names":["import_base","pick"]}
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __typeError = (msg) => {
9
+ throw TypeError(msg);
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
33
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
34
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
35
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
36
+ var N8nNonEstimatingTracing_exports = {};
37
+ __export(N8nNonEstimatingTracing_exports, {
38
+ N8nNonEstimatingTracing: () => N8nNonEstimatingTracing
39
+ });
40
+ module.exports = __toCommonJS(N8nNonEstimatingTracing_exports);
41
+ var import_base = require("@langchain/core/callbacks/base");
42
+ var import_pick = __toESM(require("lodash/pick"));
43
+ var import_n8n_workflow = require("n8n-workflow");
44
+ var import_helpers = require("../../utils/helpers");
45
+ var _parentRunIndex;
46
+ class N8nNonEstimatingTracing extends import_base.BaseCallbackHandler {
47
+ constructor(executionFunctions, options) {
48
+ super();
49
+ this.executionFunctions = executionFunctions;
50
+ this.name = "N8nNonEstimatingTracing";
51
+ // This flag makes sure that LangChain will wait for the handlers to finish before continuing
52
+ // This is crucial for the handleLLMError handler to work correctly (it should be called before the error is propagated to the root node)
53
+ this.awaitHandlers = true;
54
+ this.connectionType = import_n8n_workflow.NodeConnectionTypes.AiLanguageModel;
55
+ __privateAdd(this, _parentRunIndex);
56
+ /**
57
+ * A map to associate LLM run IDs to run details.
58
+ * Key: Unique identifier for each LLM run (run ID)
59
+ * Value: RunDetails object
60
+ *
61
+ */
62
+ this.runsMap = {};
63
+ this.options = {
64
+ // Default(OpenAI format) parser
65
+ errorDescriptionMapper: (error) => error.description
66
+ };
67
+ this.options = { ...this.options, ...options };
68
+ }
69
+ async handleLLMEnd(output, runId) {
70
+ const runDetails = this.runsMap[runId] ?? { index: Object.keys(this.runsMap).length };
71
+ output.generations = output.generations.map(
72
+ (gen) => gen.map((g) => (0, import_pick.default)(g, ["text", "generationInfo"]))
73
+ );
74
+ const tokenUsageEstimate = {
75
+ completionTokens: 0,
76
+ promptTokens: 0,
77
+ totalTokens: 0
78
+ };
79
+ const response = {
80
+ response: { generations: output.generations }
81
+ };
82
+ response.tokenUsageEstimate = tokenUsageEstimate;
83
+ const parsedMessages = typeof runDetails.messages === "string" ? runDetails.messages : runDetails.messages.map((message) => {
84
+ if (typeof message === "string") return message;
85
+ if (typeof message?.toJSON === "function") return message.toJSON();
86
+ return message;
87
+ });
88
+ const sourceNodeRunIndex = __privateGet(this, _parentRunIndex) !== void 0 ? __privateGet(this, _parentRunIndex) + runDetails.index : void 0;
89
+ this.executionFunctions.addOutputData(
90
+ this.connectionType,
91
+ runDetails.index,
92
+ [[{ json: { ...response } }]],
93
+ void 0,
94
+ sourceNodeRunIndex
95
+ );
96
+ (0, import_helpers.logAiEvent)(this.executionFunctions, "ai-llm-generated-output", {
97
+ messages: parsedMessages,
98
+ options: runDetails.options,
99
+ response
100
+ });
101
+ }
102
+ async handleLLMStart(llm, prompts, runId) {
103
+ const estimatedTokens = 0;
104
+ const sourceNodeRunIndex = __privateGet(this, _parentRunIndex) !== void 0 ? __privateGet(this, _parentRunIndex) + this.executionFunctions.getNextRunIndex() : void 0;
105
+ const options = llm.type === "constructor" ? llm.kwargs : llm;
106
+ const { index } = this.executionFunctions.addInputData(
107
+ this.connectionType,
108
+ [
109
+ [
110
+ {
111
+ json: {
112
+ messages: prompts,
113
+ estimatedTokens,
114
+ options
115
+ }
116
+ }
117
+ ]
118
+ ],
119
+ sourceNodeRunIndex
120
+ );
121
+ this.runsMap[runId] = {
122
+ index,
123
+ options,
124
+ messages: prompts
125
+ };
126
+ }
127
+ async handleLLMError(error, runId, parentRunId) {
128
+ const runDetails = this.runsMap[runId] ?? { index: Object.keys(this.runsMap).length };
129
+ if (typeof error === "object" && error?.hasOwnProperty("headers")) {
130
+ const errorWithHeaders = error;
131
+ Object.keys(errorWithHeaders.headers).forEach((key) => {
132
+ if (!key.startsWith("x-")) {
133
+ delete errorWithHeaders.headers[key];
134
+ }
135
+ });
136
+ }
137
+ if (error instanceof import_n8n_workflow.NodeError) {
138
+ if (this.options.errorDescriptionMapper) {
139
+ error.description = this.options.errorDescriptionMapper(error);
140
+ }
141
+ this.executionFunctions.addOutputData(this.connectionType, runDetails.index, error);
142
+ } else {
143
+ this.executionFunctions.addOutputData(
144
+ this.connectionType,
145
+ runDetails.index,
146
+ new import_n8n_workflow.NodeOperationError(this.executionFunctions.getNode(), error, {
147
+ functionality: "configuration-node"
148
+ })
149
+ );
150
+ }
151
+ (0, import_helpers.logAiEvent)(this.executionFunctions, "ai-llm-errored", {
152
+ error: Object.keys(error).length === 0 ? error.toString() : error,
153
+ runId,
154
+ parentRunId
155
+ });
156
+ }
157
+ // Used to associate subsequent runs with the correct parent run in subnodes of subnodes
158
+ setParentRunIndex(runIndex) {
159
+ __privateSet(this, _parentRunIndex, runIndex);
160
+ }
161
+ }
162
+ _parentRunIndex = new WeakMap();
163
+ // Annotate the CommonJS export names for ESM import in node:
164
+ 0 && (module.exports = {
165
+ N8nNonEstimatingTracing
166
+ });
167
+ //# sourceMappingURL=N8nNonEstimatingTracing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../nodes/llms/N8nNonEstimatingTracing.ts"],"sourcesContent":["import { BaseCallbackHandler } from '@langchain/core/callbacks/base';\nimport type { SerializedFields } from '@langchain/core/dist/load/map_keys';\nimport type {\n\tSerialized,\n\tSerializedNotImplemented,\n\tSerializedSecret,\n} from '@langchain/core/load/serializable';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { LLMResult } from '@langchain/core/outputs';\nimport pick from 'lodash/pick';\nimport type { IDataObject, ISupplyDataFunctions, JsonObject } from 'n8n-workflow';\nimport { NodeConnectionTypes, NodeError, NodeOperationError } from 'n8n-workflow';\n\nimport { logAiEvent } from '@utils/helpers';\n\ntype RunDetail = {\n\tindex: number;\n\tmessages: BaseMessage[] | string[] | string;\n\toptions: SerializedSecret | SerializedNotImplemented | SerializedFields;\n};\n\nexport class N8nNonEstimatingTracing extends BaseCallbackHandler {\n\tname = 'N8nNonEstimatingTracing';\n\n\t// This flag makes sure that LangChain will wait for the handlers to finish before continuing\n\t// This is crucial for the handleLLMError handler to work correctly (it should be called before the error is propagated to the root node)\n\tawaitHandlers = true;\n\n\tconnectionType = NodeConnectionTypes.AiLanguageModel;\n\n\t#parentRunIndex?: number;\n\n\t/**\n\t * A map to associate LLM run IDs to run details.\n\t * Key: Unique identifier for each LLM run (run ID)\n\t * Value: RunDetails object\n\t *\n\t */\n\trunsMap: Record<string, RunDetail> = {};\n\n\toptions = {\n\t\t// Default(OpenAI format) parser\n\t\terrorDescriptionMapper: (error: NodeError) => error.description,\n\t};\n\n\tconstructor(\n\t\tprivate executionFunctions: ISupplyDataFunctions,\n\t\toptions?: {\n\t\t\terrorDescriptionMapper?: (error: NodeError) => string;\n\t\t},\n\t) {\n\t\tsuper();\n\t\tthis.options = { ...this.options, ...options };\n\t}\n\n\tasync handleLLMEnd(output: LLMResult, runId: string) {\n\t\t// The fallback should never happen since handleLLMStart should always set the run details\n\t\t// but just in case, we set the index to the length of the runsMap\n\t\tconst runDetails = this.runsMap[runId] ?? { index: Object.keys(this.runsMap).length };\n\n\t\toutput.generations = output.generations.map((gen) =>\n\t\t\tgen.map((g) => pick(g, ['text', 'generationInfo'])),\n\t\t);\n\n\t\tconst tokenUsageEstimate = {\n\t\t\tcompletionTokens: 0,\n\t\t\tpromptTokens: 0,\n\t\t\ttotalTokens: 0,\n\t\t};\n\t\tconst response: {\n\t\t\tresponse: { generations: LLMResult['generations'] };\n\t\t\ttokenUsageEstimate?: typeof tokenUsageEstimate;\n\t\t} = {\n\t\t\tresponse: { generations: output.generations },\n\t\t};\n\n\t\tresponse.tokenUsageEstimate = tokenUsageEstimate;\n\n\t\tconst parsedMessages =\n\t\t\ttypeof runDetails.messages === 'string'\n\t\t\t\t? runDetails.messages\n\t\t\t\t: runDetails.messages.map((message) => {\n\t\t\t\t\t\tif (typeof message === 'string') return message;\n\t\t\t\t\t\tif (typeof message?.toJSON === 'function') return message.toJSON();\n\n\t\t\t\t\t\treturn message;\n\t\t\t\t\t});\n\n\t\tconst sourceNodeRunIndex =\n\t\t\tthis.#parentRunIndex !== undefined ? this.#parentRunIndex + runDetails.index : undefined;\n\n\t\tthis.executionFunctions.addOutputData(\n\t\t\tthis.connectionType,\n\t\t\trunDetails.index,\n\t\t\t[[{ json: { ...response } }]],\n\t\t\tundefined,\n\t\t\tsourceNodeRunIndex,\n\t\t);\n\n\t\tlogAiEvent(this.executionFunctions, 'ai-llm-generated-output', {\n\t\t\tmessages: parsedMessages,\n\t\t\toptions: runDetails.options,\n\t\t\tresponse,\n\t\t});\n\t}\n\n\tasync handleLLMStart(llm: Serialized, prompts: string[], runId: string) {\n\t\tconst estimatedTokens = 0;\n\t\tconst sourceNodeRunIndex =\n\t\t\tthis.#parentRunIndex !== undefined\n\t\t\t\t? this.#parentRunIndex + this.executionFunctions.getNextRunIndex()\n\t\t\t\t: undefined;\n\n\t\tconst options = llm.type === 'constructor' ? llm.kwargs : llm;\n\t\tconst { index } = this.executionFunctions.addInputData(\n\t\t\tthis.connectionType,\n\t\t\t[\n\t\t\t\t[\n\t\t\t\t\t{\n\t\t\t\t\t\tjson: {\n\t\t\t\t\t\t\tmessages: prompts,\n\t\t\t\t\t\t\testimatedTokens,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t],\n\t\t\tsourceNodeRunIndex,\n\t\t);\n\n\t\t// Save the run details for later use when processing `handleLLMEnd` event\n\t\tthis.runsMap[runId] = {\n\t\t\tindex,\n\t\t\toptions,\n\t\t\tmessages: prompts,\n\t\t};\n\t}\n\n\tasync handleLLMError(\n\t\terror: IDataObject | Error,\n\t\trunId: string,\n\t\tparentRunId?: string | undefined,\n\t) {\n\t\tconst runDetails = this.runsMap[runId] ?? { index: Object.keys(this.runsMap).length };\n\n\t\t// Filter out non-x- headers to avoid leaking sensitive information in logs\n\t\tif (typeof error === 'object' && error?.hasOwnProperty('headers')) {\n\t\t\tconst errorWithHeaders = error as { headers: Record<string, unknown> };\n\n\t\t\tObject.keys(errorWithHeaders.headers).forEach((key) => {\n\t\t\t\tif (!key.startsWith('x-')) {\n\t\t\t\t\tdelete errorWithHeaders.headers[key];\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tif (error instanceof NodeError) {\n\t\t\tif (this.options.errorDescriptionMapper) {\n\t\t\t\terror.description = this.options.errorDescriptionMapper(error);\n\t\t\t}\n\n\t\t\tthis.executionFunctions.addOutputData(this.connectionType, runDetails.index, error);\n\t\t} else {\n\t\t\t// If the error is not a NodeError, we wrap it in a NodeOperationError\n\t\t\tthis.executionFunctions.addOutputData(\n\t\t\t\tthis.connectionType,\n\t\t\t\trunDetails.index,\n\t\t\t\tnew NodeOperationError(this.executionFunctions.getNode(), error as JsonObject, {\n\t\t\t\t\tfunctionality: 'configuration-node',\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tlogAiEvent(this.executionFunctions, 'ai-llm-errored', {\n\t\t\terror: Object.keys(error).length === 0 ? error.toString() : error,\n\t\t\trunId,\n\t\t\tparentRunId,\n\t\t});\n\t}\n\n\t// Used to associate subsequent runs with the correct parent run in subnodes of subnodes\n\tsetParentRunIndex(runIndex: number) {\n\t\tthis.#parentRunIndex = runIndex;\n\t}\n}\n"],"mappings":"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","names":["pick"]}
@@ -163,6 +163,24 @@ const _McpServerManager = class _McpServerManager {
163
163
  }
164
164
  return wasToolCall(req.rawBody.toString());
165
165
  }
166
+ async handleDeleteRequest(req, resp) {
167
+ const sessionId = this.getSessionId(req);
168
+ if (!sessionId) {
169
+ resp.status(400).send("No sessionId provided");
170
+ return;
171
+ }
172
+ const transport = this.getTransport(sessionId);
173
+ if (transport) {
174
+ if (transport instanceof import_FlushingTransport.FlushingStreamableHTTPTransport) {
175
+ await transport.handleRequest(req, resp);
176
+ return;
177
+ } else {
178
+ resp.status(405).send("Method Not Allowed");
179
+ return;
180
+ }
181
+ }
182
+ resp.status(404).send("Session not found");
183
+ }
166
184
  setUpHandlers(server) {
167
185
  server.setRequestHandler(
168
186
  import_types.ListToolsRequestSchema,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../nodes/mcp/McpTrigger/McpServer.ts"],"sourcesContent":["import type { Tool } from '@langchain/core/tools';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type {\n\tJSONRPCMessage,\n\tServerRequest,\n\tServerNotification,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n\tJSONRPCMessageSchema,\n\tListToolsRequestSchema,\n\tCallToolRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { randomUUID } from 'crypto';\nimport type * as express from 'express';\nimport type { IncomingMessage } from 'http';\nimport { jsonParse, OperationalError, type Logger } from 'n8n-workflow';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\n\nimport { FlushingSSEServerTransport, FlushingStreamableHTTPTransport } from './FlushingTransport';\nimport type { CompressionResponse } from './FlushingTransport';\n\n/**\n * Parses the JSONRPC message and checks whether the method used was a tool\n * call. This is necessary in order to not have executions for listing tools\n * and other commands sent by the MCP client\n */\nfunction wasToolCall(body: string) {\n\ttry {\n\t\tconst message: unknown = JSON.parse(body);\n\t\tconst parsedMessage: JSONRPCMessage = JSONRPCMessageSchema.parse(message);\n\t\treturn (\n\t\t\t'method' in parsedMessage &&\n\t\t\t'id' in parsedMessage &&\n\t\t\tparsedMessage?.method === CallToolRequestSchema.shape.method.value\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Extracts the request ID from a JSONRPC message (for example for tool calls).\n * Returns undefined if the message doesn't have an ID (for example on a tool list request)\n *\n */\nfunction getRequestId(message: unknown): string | undefined {\n\ttry {\n\t\tconst parsedMessage: JSONRPCMessage = JSONRPCMessageSchema.parse(message);\n\t\treturn 'id' in parsedMessage ? String(parsedMessage.id) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * This singleton is shared across the instance, making sure it is the one\n * keeping account of MCP servers.\n * It needs to stay in memory to keep track of the long-lived connections.\n * It requires a logger at first creation to set everything up.\n */\nexport class McpServerManager {\n\tstatic #instance: McpServerManager;\n\n\tservers: { [sessionId: string]: Server } = {};\n\n\ttransports: {\n\t\t[sessionId: string]: FlushingSSEServerTransport | FlushingStreamableHTTPTransport;\n\t} = {};\n\n\tprivate tools: { [sessionId: string]: Tool[] } = {};\n\n\tprivate resolveFunctions: { [callId: string]: CallableFunction } = {};\n\n\tlogger: Logger;\n\n\tprivate constructor(logger: Logger) {\n\t\tthis.logger = logger;\n\t\tthis.logger.debug('MCP Server created');\n\t}\n\n\tstatic instance(logger: Logger): McpServerManager {\n\t\tif (!McpServerManager.#instance) {\n\t\t\tMcpServerManager.#instance = new McpServerManager(logger);\n\t\t\tlogger.debug('Created singleton MCP manager');\n\t\t}\n\n\t\treturn McpServerManager.#instance;\n\t}\n\n\tasync createServerWithSSETransport(\n\t\tserverName: string,\n\t\tpostUrl: string,\n\t\tresp: CompressionResponse,\n\t): Promise<void> {\n\t\tconst server = new Server(\n\t\t\t{\n\t\t\t\tname: serverName,\n\t\t\t\tversion: '0.1.0',\n\t\t\t},\n\t\t\t{\n\t\t\t\tcapabilities: {\n\t\t\t\t\ttools: {},\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst transport = new FlushingSSEServerTransport(postUrl, resp);\n\n\t\tthis.setUpHandlers(server);\n\n\t\tconst sessionId = transport.sessionId;\n\t\tthis.transports[sessionId] = transport;\n\t\tthis.servers[sessionId] = server;\n\n\t\tresp.on('close', async () => {\n\t\t\tthis.logger.debug(`Deleting transport for ${sessionId}`);\n\t\t\tdelete this.tools[sessionId];\n\t\t\tdelete this.transports[sessionId];\n\t\t\tdelete this.servers[sessionId];\n\t\t});\n\t\tawait server.connect(transport);\n\n\t\t// Make sure we flush the compression middleware, so that it's not waiting for more content to be added to the buffer\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\t}\n\n\tgetSessionId(req: express.Request): string | undefined {\n\t\t// Session ID can be passed either as a query parameter (SSE transport)\n\t\t// or in the header (StreamableHTTP transport).\n\t\treturn (req.query.sessionId ?? req.headers['mcp-session-id']) as string | undefined;\n\t}\n\n\tgetTransport(\n\t\tsessionId: string,\n\t): FlushingSSEServerTransport | FlushingStreamableHTTPTransport | undefined {\n\t\treturn this.transports[sessionId];\n\t}\n\n\tasync createServerWithStreamableHTTPTransport(\n\t\tserverName: string,\n\t\tresp: CompressionResponse,\n\t\treq?: express.Request,\n\t): Promise<void> {\n\t\tconst server = new Server(\n\t\t\t{\n\t\t\t\tname: serverName,\n\t\t\t\tversion: '0.1.0',\n\t\t\t},\n\t\t\t{\n\t\t\t\tcapabilities: {\n\t\t\t\t\ttools: {},\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst transport = new FlushingStreamableHTTPTransport(\n\t\t\t{\n\t\t\t\tsessionIdGenerator: () => randomUUID(),\n\t\t\t\tonsessioninitialized: (sessionId) => {\n\t\t\t\t\tthis.logger.debug(`New session initialized: ${sessionId}`);\n\t\t\t\t\ttransport.onclose = () => {\n\t\t\t\t\t\tthis.logger.debug(`Deleting transport for ${sessionId}`);\n\t\t\t\t\t\tdelete this.tools[sessionId];\n\t\t\t\t\t\tdelete this.transports[sessionId];\n\t\t\t\t\t\tdelete this.servers[sessionId];\n\t\t\t\t\t};\n\t\t\t\t\tthis.transports[sessionId] = transport;\n\t\t\t\t\tthis.servers[sessionId] = server;\n\t\t\t\t},\n\t\t\t},\n\t\t\tresp,\n\t\t);\n\n\t\tthis.setUpHandlers(server);\n\n\t\tawait server.connect(transport);\n\n\t\tawait transport.handleRequest(req as IncomingMessage, resp, req?.body);\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\t}\n\n\tasync handlePostMessage(req: express.Request, resp: CompressionResponse, connectedTools: Tool[]) {\n\t\t// Session ID can be passed either as a query parameter (SSE transport)\n\t\t// or in the header (StreamableHTTP transport).\n\t\tconst sessionId = this.getSessionId(req);\n\t\tconst transport = this.getTransport(sessionId as string);\n\t\tif (sessionId && transport) {\n\t\t\t// We need to add a promise here because the `handlePostMessage` will send something to the\n\t\t\t// MCP Server, that will run in a different context. This means that the return will happen\n\t\t\t// almost immediately, and will lead to marking the sub-node as \"running\" in the final execution\n\t\t\tconst message = jsonParse(req.rawBody.toString());\n\t\t\tconst messageId = getRequestId(message);\n\t\t\t// Use session & message ID if available, otherwise fall back to sessionId\n\t\t\tconst callId = messageId ? `${sessionId}_${messageId}` : sessionId;\n\t\t\tthis.tools[sessionId] = connectedTools;\n\n\t\t\ttry {\n\t\t\t\tawait new Promise(async (resolve) => {\n\t\t\t\t\tthis.resolveFunctions[callId] = resolve;\n\t\t\t\t\tawait transport.handleRequest(req, resp, message as IncomingMessage);\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tdelete this.resolveFunctions[callId];\n\t\t\t}\n\t\t} else {\n\t\t\tthis.logger.warn(`No transport found for session ${sessionId}`);\n\t\t\tresp.status(401).send('No transport found for sessionId');\n\t\t}\n\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\n\t\treturn wasToolCall(req.rawBody.toString());\n\t}\n\n\tsetUpHandlers(server: Server) {\n\t\tserver.setRequestHandler(\n\t\t\tListToolsRequestSchema,\n\t\t\tasync (_, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {\n\t\t\t\tif (!extra.sessionId) {\n\t\t\t\t\tthrow new OperationalError('Require a sessionId for the listing of tools');\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\ttools: this.tools[extra.sessionId].map((tool) => {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\t\t// Allow additional properties on tool call input\n\t\t\t\t\t\t\tinputSchema: zodToJsonSchema(tool.schema, { removeAdditionalStrategy: 'strict' }),\n\t\t\t\t\t\t};\n\t\t\t\t\t}),\n\t\t\t\t};\n\t\t\t},\n\t\t);\n\n\t\tserver.setRequestHandler(\n\t\t\tCallToolRequestSchema,\n\t\t\tasync (request, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {\n\t\t\t\tif (!request.params?.name || !request.params?.arguments) {\n\t\t\t\t\tthrow new OperationalError('Require a name and arguments for the tool call');\n\t\t\t\t}\n\t\t\t\tif (!extra.sessionId) {\n\t\t\t\t\tthrow new OperationalError('Require a sessionId for the tool call');\n\t\t\t\t}\n\n\t\t\t\tconst callId = extra.requestId ? `${extra.sessionId}_${extra.requestId}` : extra.sessionId;\n\n\t\t\t\tconst requestedTool: Tool | undefined = this.tools[extra.sessionId].find(\n\t\t\t\t\t(tool) => tool.name === request.params.name,\n\t\t\t\t);\n\t\t\t\tif (!requestedTool) {\n\t\t\t\t\tthrow new OperationalError('Tool not found');\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await requestedTool.invoke(request.params.arguments);\n\t\t\t\t\tif (this.resolveFunctions[callId]) {\n\t\t\t\t\t\tthis.resolveFunctions[callId]();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.logger.warn(`No resolve function found for ${callId}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.logger.debug(`Got request for ${requestedTool.name}, and executed it.`);\n\n\t\t\t\t\tif (typeof result === 'object') {\n\t\t\t\t\t\treturn { content: [{ type: 'text', text: JSON.stringify(result) }] };\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof result === 'string') {\n\t\t\t\t\t\treturn { content: [{ type: 'text', text: result }] };\n\t\t\t\t\t}\n\t\t\t\t\treturn { content: [{ type: 'text', text: String(result) }] };\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.logger.error(`Error while executing Tool ${requestedTool.name}: ${error}`);\n\t\t\t\t\treturn { isError: true, content: [{ type: 'text', text: `Error: ${error.message}` }] };\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tserver.onclose = () => {\n\t\t\tthis.logger.debug('Closing MCP Server');\n\t\t};\n\t\tserver.onerror = (error: unknown) => {\n\t\t\tthis.logger.error(`MCP Error: ${error}`);\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../nodes/mcp/McpTrigger/McpServer.ts"],"sourcesContent":["import type { Tool } from '@langchain/core/tools';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type {\n\tJSONRPCMessage,\n\tServerRequest,\n\tServerNotification,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {\n\tJSONRPCMessageSchema,\n\tListToolsRequestSchema,\n\tCallToolRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { randomUUID } from 'crypto';\nimport type * as express from 'express';\nimport type { IncomingMessage } from 'http';\nimport { jsonParse, OperationalError, type Logger } from 'n8n-workflow';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\n\nimport { FlushingSSEServerTransport, FlushingStreamableHTTPTransport } from './FlushingTransport';\nimport type { CompressionResponse } from './FlushingTransport';\n\n/**\n * Parses the JSONRPC message and checks whether the method used was a tool\n * call. This is necessary in order to not have executions for listing tools\n * and other commands sent by the MCP client\n */\nfunction wasToolCall(body: string) {\n\ttry {\n\t\tconst message: unknown = JSON.parse(body);\n\t\tconst parsedMessage: JSONRPCMessage = JSONRPCMessageSchema.parse(message);\n\t\treturn (\n\t\t\t'method' in parsedMessage &&\n\t\t\t'id' in parsedMessage &&\n\t\t\tparsedMessage?.method === CallToolRequestSchema.shape.method.value\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Extracts the request ID from a JSONRPC message (for example for tool calls).\n * Returns undefined if the message doesn't have an ID (for example on a tool list request)\n *\n */\nfunction getRequestId(message: unknown): string | undefined {\n\ttry {\n\t\tconst parsedMessage: JSONRPCMessage = JSONRPCMessageSchema.parse(message);\n\t\treturn 'id' in parsedMessage ? String(parsedMessage.id) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * This singleton is shared across the instance, making sure it is the one\n * keeping account of MCP servers.\n * It needs to stay in memory to keep track of the long-lived connections.\n * It requires a logger at first creation to set everything up.\n */\nexport class McpServerManager {\n\tstatic #instance: McpServerManager;\n\n\tservers: { [sessionId: string]: Server } = {};\n\n\ttransports: {\n\t\t[sessionId: string]: FlushingSSEServerTransport | FlushingStreamableHTTPTransport;\n\t} = {};\n\n\tprivate tools: { [sessionId: string]: Tool[] } = {};\n\n\tprivate resolveFunctions: { [callId: string]: CallableFunction } = {};\n\n\tlogger: Logger;\n\n\tprivate constructor(logger: Logger) {\n\t\tthis.logger = logger;\n\t\tthis.logger.debug('MCP Server created');\n\t}\n\n\tstatic instance(logger: Logger): McpServerManager {\n\t\tif (!McpServerManager.#instance) {\n\t\t\tMcpServerManager.#instance = new McpServerManager(logger);\n\t\t\tlogger.debug('Created singleton MCP manager');\n\t\t}\n\n\t\treturn McpServerManager.#instance;\n\t}\n\n\tasync createServerWithSSETransport(\n\t\tserverName: string,\n\t\tpostUrl: string,\n\t\tresp: CompressionResponse,\n\t): Promise<void> {\n\t\tconst server = new Server(\n\t\t\t{\n\t\t\t\tname: serverName,\n\t\t\t\tversion: '0.1.0',\n\t\t\t},\n\t\t\t{\n\t\t\t\tcapabilities: {\n\t\t\t\t\ttools: {},\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst transport = new FlushingSSEServerTransport(postUrl, resp);\n\n\t\tthis.setUpHandlers(server);\n\n\t\tconst sessionId = transport.sessionId;\n\t\tthis.transports[sessionId] = transport;\n\t\tthis.servers[sessionId] = server;\n\n\t\tresp.on('close', async () => {\n\t\t\tthis.logger.debug(`Deleting transport for ${sessionId}`);\n\t\t\tdelete this.tools[sessionId];\n\t\t\tdelete this.transports[sessionId];\n\t\t\tdelete this.servers[sessionId];\n\t\t});\n\t\tawait server.connect(transport);\n\n\t\t// Make sure we flush the compression middleware, so that it's not waiting for more content to be added to the buffer\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\t}\n\n\tgetSessionId(req: express.Request): string | undefined {\n\t\t// Session ID can be passed either as a query parameter (SSE transport)\n\t\t// or in the header (StreamableHTTP transport).\n\t\treturn (req.query.sessionId ?? req.headers['mcp-session-id']) as string | undefined;\n\t}\n\n\tgetTransport(\n\t\tsessionId: string,\n\t): FlushingSSEServerTransport | FlushingStreamableHTTPTransport | undefined {\n\t\treturn this.transports[sessionId];\n\t}\n\n\tasync createServerWithStreamableHTTPTransport(\n\t\tserverName: string,\n\t\tresp: CompressionResponse,\n\t\treq?: express.Request,\n\t): Promise<void> {\n\t\tconst server = new Server(\n\t\t\t{\n\t\t\t\tname: serverName,\n\t\t\t\tversion: '0.1.0',\n\t\t\t},\n\t\t\t{\n\t\t\t\tcapabilities: {\n\t\t\t\t\ttools: {},\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst transport = new FlushingStreamableHTTPTransport(\n\t\t\t{\n\t\t\t\tsessionIdGenerator: () => randomUUID(),\n\t\t\t\tonsessioninitialized: (sessionId) => {\n\t\t\t\t\tthis.logger.debug(`New session initialized: ${sessionId}`);\n\t\t\t\t\ttransport.onclose = () => {\n\t\t\t\t\t\tthis.logger.debug(`Deleting transport for ${sessionId}`);\n\t\t\t\t\t\tdelete this.tools[sessionId];\n\t\t\t\t\t\tdelete this.transports[sessionId];\n\t\t\t\t\t\tdelete this.servers[sessionId];\n\t\t\t\t\t};\n\t\t\t\t\tthis.transports[sessionId] = transport;\n\t\t\t\t\tthis.servers[sessionId] = server;\n\t\t\t\t},\n\t\t\t},\n\t\t\tresp,\n\t\t);\n\n\t\tthis.setUpHandlers(server);\n\n\t\tawait server.connect(transport);\n\n\t\tawait transport.handleRequest(req as IncomingMessage, resp, req?.body);\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\t}\n\n\tasync handlePostMessage(req: express.Request, resp: CompressionResponse, connectedTools: Tool[]) {\n\t\t// Session ID can be passed either as a query parameter (SSE transport)\n\t\t// or in the header (StreamableHTTP transport).\n\t\tconst sessionId = this.getSessionId(req);\n\t\tconst transport = this.getTransport(sessionId as string);\n\t\tif (sessionId && transport) {\n\t\t\t// We need to add a promise here because the `handlePostMessage` will send something to the\n\t\t\t// MCP Server, that will run in a different context. This means that the return will happen\n\t\t\t// almost immediately, and will lead to marking the sub-node as \"running\" in the final execution\n\t\t\tconst message = jsonParse(req.rawBody.toString());\n\t\t\tconst messageId = getRequestId(message);\n\t\t\t// Use session & message ID if available, otherwise fall back to sessionId\n\t\t\tconst callId = messageId ? `${sessionId}_${messageId}` : sessionId;\n\t\t\tthis.tools[sessionId] = connectedTools;\n\n\t\t\ttry {\n\t\t\t\tawait new Promise(async (resolve) => {\n\t\t\t\t\tthis.resolveFunctions[callId] = resolve;\n\t\t\t\t\tawait transport.handleRequest(req, resp, message as IncomingMessage);\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tdelete this.resolveFunctions[callId];\n\t\t\t}\n\t\t} else {\n\t\t\tthis.logger.warn(`No transport found for session ${sessionId}`);\n\t\t\tresp.status(401).send('No transport found for sessionId');\n\t\t}\n\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\n\t\treturn wasToolCall(req.rawBody.toString());\n\t}\n\n\tasync handleDeleteRequest(req: express.Request, resp: CompressionResponse) {\n\t\tconst sessionId = this.getSessionId(req);\n\n\t\tif (!sessionId) {\n\t\t\tresp.status(400).send('No sessionId provided');\n\t\t\treturn;\n\t\t}\n\n\t\tconst transport = this.getTransport(sessionId);\n\n\t\tif (transport) {\n\t\t\tif (transport instanceof FlushingStreamableHTTPTransport) {\n\t\t\t\tawait transport.handleRequest(req, resp);\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\t// For SSE transport, we don't support DELETE requests\n\t\t\t\tresp.status(405).send('Method Not Allowed');\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tresp.status(404).send('Session not found');\n\t}\n\n\tsetUpHandlers(server: Server) {\n\t\tserver.setRequestHandler(\n\t\t\tListToolsRequestSchema,\n\t\t\tasync (_, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {\n\t\t\t\tif (!extra.sessionId) {\n\t\t\t\t\tthrow new OperationalError('Require a sessionId for the listing of tools');\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\ttools: this.tools[extra.sessionId].map((tool) => {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\t\t// Allow additional properties on tool call input\n\t\t\t\t\t\t\tinputSchema: zodToJsonSchema(tool.schema, { removeAdditionalStrategy: 'strict' }),\n\t\t\t\t\t\t};\n\t\t\t\t\t}),\n\t\t\t\t};\n\t\t\t},\n\t\t);\n\n\t\tserver.setRequestHandler(\n\t\t\tCallToolRequestSchema,\n\t\t\tasync (request, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {\n\t\t\t\tif (!request.params?.name || !request.params?.arguments) {\n\t\t\t\t\tthrow new OperationalError('Require a name and arguments for the tool call');\n\t\t\t\t}\n\t\t\t\tif (!extra.sessionId) {\n\t\t\t\t\tthrow new OperationalError('Require a sessionId for the tool call');\n\t\t\t\t}\n\n\t\t\t\tconst callId = extra.requestId ? `${extra.sessionId}_${extra.requestId}` : extra.sessionId;\n\n\t\t\t\tconst requestedTool: Tool | undefined = this.tools[extra.sessionId].find(\n\t\t\t\t\t(tool) => tool.name === request.params.name,\n\t\t\t\t);\n\t\t\t\tif (!requestedTool) {\n\t\t\t\t\tthrow new OperationalError('Tool not found');\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await requestedTool.invoke(request.params.arguments);\n\t\t\t\t\tif (this.resolveFunctions[callId]) {\n\t\t\t\t\t\tthis.resolveFunctions[callId]();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.logger.warn(`No resolve function found for ${callId}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.logger.debug(`Got request for ${requestedTool.name}, and executed it.`);\n\n\t\t\t\t\tif (typeof result === 'object') {\n\t\t\t\t\t\treturn { content: [{ type: 'text', text: JSON.stringify(result) }] };\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof result === 'string') {\n\t\t\t\t\t\treturn { content: [{ type: 'text', text: result }] };\n\t\t\t\t\t}\n\t\t\t\t\treturn { content: [{ type: 'text', text: String(result) }] };\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.logger.error(`Error while executing Tool ${requestedTool.name}: ${error}`);\n\t\t\t\t\treturn { isError: true, content: [{ type: 'text', text: `Error: ${error.message}` }] };\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tserver.onclose = () => {\n\t\t\tthis.logger.debug('Closing MCP Server');\n\t\t};\n\t\tserver.onerror = (error: unknown) => {\n\t\t\tthis.logger.error(`MCP Error: ${error}`);\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}