@n8n/ai-utilities 0.3.0 → 0.4.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 (240) hide show
  1. package/README.md +9 -462
  2. package/dist/{adapters → cjs/adapters}/langchain-chat-model.d.ts +1 -1
  3. package/dist/cjs/adapters/langchain-chat-model.js +201 -0
  4. package/dist/cjs/adapters/langchain-chat-model.js.map +1 -0
  5. package/dist/cjs/adapters/langchain-history.js +37 -0
  6. package/dist/cjs/adapters/langchain-history.js.map +1 -0
  7. package/dist/cjs/adapters/langchain-memory.js +46 -0
  8. package/dist/cjs/adapters/langchain-memory.js.map +1 -0
  9. package/dist/cjs/ai-node-sdk-version.d.ts +1 -0
  10. package/dist/cjs/ai-node-sdk-version.js +15 -0
  11. package/dist/cjs/ai-node-sdk-version.js.map +1 -0
  12. package/dist/cjs/chat-model/base.js +35 -0
  13. package/dist/cjs/chat-model/base.js.map +1 -0
  14. package/dist/{converters → cjs/converters}/message.d.ts +1 -0
  15. package/dist/cjs/converters/message.js +388 -0
  16. package/dist/cjs/converters/message.js.map +1 -0
  17. package/dist/cjs/converters/tool.js +69 -0
  18. package/dist/cjs/converters/tool.js.map +1 -0
  19. package/dist/cjs/guards.js +37 -0
  20. package/dist/cjs/guards.js.map +1 -0
  21. package/dist/{index.d.ts → cjs/index.d.ts} +7 -5
  22. package/dist/cjs/index.js +73 -0
  23. package/dist/cjs/index.js.map +1 -0
  24. package/dist/cjs/memory/base-chat-history.js +22 -0
  25. package/dist/cjs/memory/base-chat-history.js.map +1 -0
  26. package/dist/cjs/memory/base-chat-memory.js +17 -0
  27. package/dist/cjs/memory/base-chat-memory.js.map +1 -0
  28. package/dist/cjs/memory/windowed-chat-memory.js +48 -0
  29. package/dist/cjs/memory/windowed-chat-memory.js.map +1 -0
  30. package/dist/cjs/suppliers/supplyMemory.js +24 -0
  31. package/dist/cjs/suppliers/supplyMemory.js.map +1 -0
  32. package/dist/cjs/suppliers/supplyModel.d.ts +8 -0
  33. package/dist/cjs/suppliers/supplyModel.js +89 -0
  34. package/dist/cjs/suppliers/supplyModel.js.map +1 -0
  35. package/dist/cjs/typecheck.tsbuildinfo +1 -0
  36. package/dist/cjs/types/chat-model.js +13 -0
  37. package/dist/{types → cjs/types}/chat-model.js.map +1 -1
  38. package/dist/cjs/types/json.js +13 -0
  39. package/dist/{types → cjs/types}/json.js.map +1 -1
  40. package/dist/cjs/types/memory.js +13 -0
  41. package/dist/{types → cjs/types}/memory.js.map +1 -1
  42. package/dist/{types → cjs/types}/message.d.ts +8 -1
  43. package/dist/cjs/types/message.js +13 -0
  44. package/dist/{types → cjs/types}/message.js.map +1 -1
  45. package/dist/cjs/types/openai.js +13 -0
  46. package/dist/{types → cjs/types}/openai.js.map +1 -1
  47. package/dist/cjs/types/output.js +13 -0
  48. package/dist/{types → cjs/types}/output.js.map +1 -1
  49. package/dist/cjs/types/tool.js +13 -0
  50. package/dist/{types → cjs/types}/tool.js.map +1 -1
  51. package/dist/cjs/utils/embeddings-input-validation.js +38 -0
  52. package/dist/cjs/utils/embeddings-input-validation.js.map +1 -0
  53. package/dist/cjs/utils/failed-attempt-handler/n8nDefaultFailedAttemptHandler.js +40 -0
  54. package/dist/cjs/utils/failed-attempt-handler/n8nDefaultFailedAttemptHandler.js.map +1 -0
  55. package/dist/cjs/utils/failed-attempt-handler/n8nLlmFailedAttemptHandler.js +38 -0
  56. package/dist/cjs/utils/failed-attempt-handler/n8nLlmFailedAttemptHandler.js.map +1 -0
  57. package/dist/cjs/utils/helpers.js +63 -0
  58. package/dist/cjs/utils/helpers.js.map +1 -0
  59. package/dist/cjs/utils/http-proxy-agent.js +58 -0
  60. package/dist/cjs/utils/http-proxy-agent.js.map +1 -0
  61. package/dist/cjs/utils/log-ai-event.js +23 -0
  62. package/dist/cjs/utils/log-ai-event.js.map +1 -0
  63. package/dist/cjs/utils/log-wrapper.js +339 -0
  64. package/dist/cjs/utils/log-wrapper.js.map +1 -0
  65. package/dist/cjs/utils/n8n-binary-loader.js +169 -0
  66. package/dist/cjs/utils/n8n-binary-loader.js.map +1 -0
  67. package/dist/cjs/utils/n8n-json-loader.js +76 -0
  68. package/dist/cjs/utils/n8n-json-loader.js.map +1 -0
  69. package/dist/cjs/utils/n8n-llm-tracing.js +167 -0
  70. package/dist/cjs/utils/n8n-llm-tracing.js.map +1 -0
  71. package/dist/{utils → cjs/utils}/sse.d.ts +1 -1
  72. package/dist/cjs/utils/sse.js +107 -0
  73. package/dist/cjs/utils/sse.js.map +1 -0
  74. package/dist/cjs/utils/tokenizer/tiktoken.js +50 -0
  75. package/dist/cjs/utils/tokenizer/tiktoken.js.map +1 -0
  76. package/dist/cjs/utils/tokenizer/token-estimator.js +108 -0
  77. package/dist/cjs/utils/tokenizer/token-estimator.js.map +1 -0
  78. package/dist/esm/adapters/langchain-chat-model.d.ts +20 -0
  79. package/dist/{adapters → esm/adapters}/langchain-chat-model.js +4 -4
  80. package/dist/esm/adapters/langchain-chat-model.js.map +1 -0
  81. package/dist/esm/adapters/langchain-history.d.ts +12 -0
  82. package/dist/esm/adapters/langchain-history.js.map +1 -0
  83. package/dist/esm/adapters/langchain-memory.d.ts +11 -0
  84. package/dist/esm/adapters/langchain-memory.js.map +1 -0
  85. package/dist/esm/ai-node-sdk-version.d.ts +1 -0
  86. package/dist/esm/ai-node-sdk-version.js +5 -0
  87. package/dist/esm/ai-node-sdk-version.js.map +1 -0
  88. package/dist/esm/chat-model/base.d.ts +15 -0
  89. package/dist/esm/chat-model/base.js.map +1 -0
  90. package/dist/esm/converters/message.d.ts +7 -0
  91. package/dist/{converters → esm/converters}/message.js +24 -13
  92. package/dist/esm/converters/message.js.map +1 -0
  93. package/dist/esm/converters/tool.d.ts +5 -0
  94. package/dist/esm/converters/tool.js.map +1 -0
  95. package/dist/esm/guards.d.ts +8 -0
  96. package/dist/esm/guards.js.map +1 -0
  97. package/dist/esm/index.d.ts +30 -0
  98. package/dist/{index.js → esm/index.js} +13 -6
  99. package/dist/esm/index.js.map +1 -0
  100. package/dist/esm/memory/base-chat-history.d.ts +8 -0
  101. package/dist/esm/memory/base-chat-history.js.map +1 -0
  102. package/dist/esm/memory/base-chat-memory.d.ts +8 -0
  103. package/dist/esm/memory/base-chat-memory.js.map +1 -0
  104. package/dist/esm/memory/windowed-chat-memory.d.ts +14 -0
  105. package/dist/esm/memory/windowed-chat-memory.js.map +1 -0
  106. package/dist/esm/suppliers/supplyMemory.d.ts +6 -0
  107. package/dist/esm/suppliers/supplyMemory.js.map +1 -0
  108. package/dist/esm/suppliers/supplyModel.d.ts +8 -0
  109. package/dist/{suppliers → esm/suppliers}/supplyModel.js +1 -1
  110. package/dist/esm/suppliers/supplyModel.js.map +1 -0
  111. package/dist/esm/typecheck.tsbuildinfo +1 -0
  112. package/dist/esm/types/chat-model.d.ts +26 -0
  113. package/dist/esm/types/chat-model.js.map +1 -0
  114. package/dist/esm/types/json.d.ts +5 -0
  115. package/dist/esm/types/json.js.map +1 -0
  116. package/dist/esm/types/memory.d.ts +13 -0
  117. package/dist/esm/types/memory.js.map +1 -0
  118. package/dist/esm/types/message.d.ts +56 -0
  119. package/dist/esm/types/message.js.map +1 -0
  120. package/dist/esm/types/openai.d.ts +39 -0
  121. package/dist/esm/types/openai.js.map +1 -0
  122. package/dist/esm/types/output.d.ts +47 -0
  123. package/dist/esm/types/output.js.map +1 -0
  124. package/dist/esm/types/tool.d.ts +28 -0
  125. package/dist/esm/types/tool.js.map +1 -0
  126. package/dist/esm/utils/embeddings-input-validation.d.ts +3 -0
  127. package/dist/esm/utils/embeddings-input-validation.js.map +1 -0
  128. package/dist/esm/utils/failed-attempt-handler/n8nDefaultFailedAttemptHandler.d.ts +1 -0
  129. package/dist/esm/utils/failed-attempt-handler/n8nDefaultFailedAttemptHandler.js.map +1 -0
  130. package/dist/esm/utils/failed-attempt-handler/n8nLlmFailedAttemptHandler.d.ts +3 -0
  131. package/dist/esm/utils/failed-attempt-handler/n8nLlmFailedAttemptHandler.js.map +1 -0
  132. package/dist/esm/utils/helpers.d.ts +3 -0
  133. package/dist/esm/utils/helpers.js.map +1 -0
  134. package/dist/esm/utils/http-proxy-agent.d.ts +10 -0
  135. package/dist/esm/utils/http-proxy-agent.js.map +1 -0
  136. package/dist/esm/utils/log-ai-event.d.ts +2 -0
  137. package/dist/esm/utils/log-ai-event.js.map +1 -0
  138. package/dist/esm/utils/log-wrapper.d.ts +28 -0
  139. package/dist/esm/utils/log-wrapper.js.map +1 -0
  140. package/dist/esm/utils/n8n-binary-loader.d.ts +18 -0
  141. package/dist/esm/utils/n8n-binary-loader.js.map +1 -0
  142. package/dist/esm/utils/n8n-json-loader.d.ts +11 -0
  143. package/dist/esm/utils/n8n-json-loader.js.map +1 -0
  144. package/dist/esm/utils/n8n-llm-tracing.d.ts +46 -0
  145. package/dist/esm/utils/n8n-llm-tracing.js.map +1 -0
  146. package/dist/esm/utils/sse.d.ts +8 -0
  147. package/dist/{utils → esm/utils}/sse.js +17 -27
  148. package/dist/esm/utils/sse.js.map +1 -0
  149. package/dist/esm/utils/tokenizer/cl100k_base.json +1 -0
  150. package/dist/esm/utils/tokenizer/o200k_base.json +1 -0
  151. package/dist/esm/utils/tokenizer/tiktoken.d.ts +4 -0
  152. package/dist/esm/utils/tokenizer/tiktoken.js.map +1 -0
  153. package/dist/esm/utils/tokenizer/token-estimator.d.ts +4 -0
  154. package/dist/esm/utils/tokenizer/token-estimator.js.map +1 -0
  155. package/package.json +19 -12
  156. package/dist/adapters/langchain-chat-model.js.map +0 -1
  157. package/dist/adapters/langchain-history.js.map +0 -1
  158. package/dist/adapters/langchain-memory.js.map +0 -1
  159. package/dist/build.tsbuildinfo +0 -1
  160. package/dist/chat-model/base.js.map +0 -1
  161. package/dist/converters/message.js.map +0 -1
  162. package/dist/converters/tool.js.map +0 -1
  163. package/dist/guards.js.map +0 -1
  164. package/dist/index.js.map +0 -1
  165. package/dist/memory/base-chat-history.js.map +0 -1
  166. package/dist/memory/base-chat-memory.js.map +0 -1
  167. package/dist/memory/windowed-chat-memory.js.map +0 -1
  168. package/dist/suppliers/supplyMemory.js.map +0 -1
  169. package/dist/suppliers/supplyModel.d.ts +0 -15
  170. package/dist/suppliers/supplyModel.js.map +0 -1
  171. package/dist/utils/embeddings-input-validation.js.map +0 -1
  172. package/dist/utils/failed-attempt-handler/n8nDefaultFailedAttemptHandler.js.map +0 -1
  173. package/dist/utils/failed-attempt-handler/n8nLlmFailedAttemptHandler.js.map +0 -1
  174. package/dist/utils/helpers.js.map +0 -1
  175. package/dist/utils/http-proxy-agent.js.map +0 -1
  176. package/dist/utils/log-ai-event.js.map +0 -1
  177. package/dist/utils/log-wrapper.js.map +0 -1
  178. package/dist/utils/n8n-binary-loader.js.map +0 -1
  179. package/dist/utils/n8n-json-loader.js.map +0 -1
  180. package/dist/utils/n8n-llm-tracing.js.map +0 -1
  181. package/dist/utils/sse.js.map +0 -1
  182. package/dist/utils/tokenizer/tiktoken.js.map +0 -1
  183. package/dist/utils/tokenizer/token-estimator.js.map +0 -1
  184. /package/dist/{adapters → cjs/adapters}/langchain-history.d.ts +0 -0
  185. /package/dist/{adapters → cjs/adapters}/langchain-memory.d.ts +0 -0
  186. /package/dist/{chat-model → cjs/chat-model}/base.d.ts +0 -0
  187. /package/dist/{converters → cjs/converters}/tool.d.ts +0 -0
  188. /package/dist/{guards.d.ts → cjs/guards.d.ts} +0 -0
  189. /package/dist/{memory → cjs/memory}/base-chat-history.d.ts +0 -0
  190. /package/dist/{memory → cjs/memory}/base-chat-memory.d.ts +0 -0
  191. /package/dist/{memory → cjs/memory}/windowed-chat-memory.d.ts +0 -0
  192. /package/dist/{suppliers → cjs/suppliers}/supplyMemory.d.ts +0 -0
  193. /package/dist/{types → cjs/types}/chat-model.d.ts +0 -0
  194. /package/dist/{types → cjs/types}/json.d.ts +0 -0
  195. /package/dist/{types → cjs/types}/memory.d.ts +0 -0
  196. /package/dist/{types → cjs/types}/openai.d.ts +0 -0
  197. /package/dist/{types → cjs/types}/output.d.ts +0 -0
  198. /package/dist/{types → cjs/types}/tool.d.ts +0 -0
  199. /package/dist/{utils → cjs/utils}/embeddings-input-validation.d.ts +0 -0
  200. /package/dist/{utils → cjs/utils}/failed-attempt-handler/n8nDefaultFailedAttemptHandler.d.ts +0 -0
  201. /package/dist/{utils → cjs/utils}/failed-attempt-handler/n8nLlmFailedAttemptHandler.d.ts +0 -0
  202. /package/dist/{utils → cjs/utils}/helpers.d.ts +0 -0
  203. /package/dist/{utils → cjs/utils}/http-proxy-agent.d.ts +0 -0
  204. /package/dist/{utils → cjs/utils}/log-ai-event.d.ts +0 -0
  205. /package/dist/{utils → cjs/utils}/log-wrapper.d.ts +0 -0
  206. /package/dist/{utils → cjs/utils}/n8n-binary-loader.d.ts +0 -0
  207. /package/dist/{utils → cjs/utils}/n8n-json-loader.d.ts +0 -0
  208. /package/dist/{utils → cjs/utils}/n8n-llm-tracing.d.ts +0 -0
  209. /package/dist/{utils → cjs/utils}/tokenizer/cl100k_base.json +0 -0
  210. /package/dist/{utils → cjs/utils}/tokenizer/o200k_base.json +0 -0
  211. /package/dist/{utils → cjs/utils}/tokenizer/tiktoken.d.ts +0 -0
  212. /package/dist/{utils → cjs/utils}/tokenizer/token-estimator.d.ts +0 -0
  213. /package/dist/{adapters → esm/adapters}/langchain-history.js +0 -0
  214. /package/dist/{adapters → esm/adapters}/langchain-memory.js +0 -0
  215. /package/dist/{chat-model → esm/chat-model}/base.js +0 -0
  216. /package/dist/{converters → esm/converters}/tool.js +0 -0
  217. /package/dist/{guards.js → esm/guards.js} +0 -0
  218. /package/dist/{memory → esm/memory}/base-chat-history.js +0 -0
  219. /package/dist/{memory → esm/memory}/base-chat-memory.js +0 -0
  220. /package/dist/{memory → esm/memory}/windowed-chat-memory.js +0 -0
  221. /package/dist/{suppliers → esm/suppliers}/supplyMemory.js +0 -0
  222. /package/dist/{types → esm/types}/chat-model.js +0 -0
  223. /package/dist/{types → esm/types}/json.js +0 -0
  224. /package/dist/{types → esm/types}/memory.js +0 -0
  225. /package/dist/{types → esm/types}/message.js +0 -0
  226. /package/dist/{types → esm/types}/openai.js +0 -0
  227. /package/dist/{types → esm/types}/output.js +0 -0
  228. /package/dist/{types → esm/types}/tool.js +0 -0
  229. /package/dist/{utils → esm/utils}/embeddings-input-validation.js +0 -0
  230. /package/dist/{utils → esm/utils}/failed-attempt-handler/n8nDefaultFailedAttemptHandler.js +0 -0
  231. /package/dist/{utils → esm/utils}/failed-attempt-handler/n8nLlmFailedAttemptHandler.js +0 -0
  232. /package/dist/{utils → esm/utils}/helpers.js +0 -0
  233. /package/dist/{utils → esm/utils}/http-proxy-agent.js +0 -0
  234. /package/dist/{utils → esm/utils}/log-ai-event.js +0 -0
  235. /package/dist/{utils → esm/utils}/log-wrapper.js +0 -0
  236. /package/dist/{utils → esm/utils}/n8n-binary-loader.js +0 -0
  237. /package/dist/{utils → esm/utils}/n8n-json-loader.js +0 -0
  238. /package/dist/{utils → esm/utils}/n8n-llm-tracing.js +0 -0
  239. /package/dist/{utils → esm/utils}/tokenizer/tiktoken.js +0 -0
  240. /package/dist/{utils → esm/utils}/tokenizer/token-estimator.js +0 -0
package/README.md CHANGED
@@ -1,10 +1,14 @@
1
1
  # @n8n/ai-utilities
2
2
 
3
- Utilities for building AI nodes in n8n.
3
+ Core utilities and abstractions for AI functionality in n8n. This package provides the foundational building blocks used internally by the n8n platform.
4
4
 
5
- ## Installation
5
+ This package is reexported from @n8n/ai-node-sdk, that exposes methods and types for public usage.
6
6
 
7
- This package is part of the n8n monorepo and should be installed via the workspace.
7
+ When changing logic in this package, make sure your changes are backwards compatible. What that means:
8
+ - don't remove existing interfaces or properties in them
9
+ - make new properties optional or create new versions of interfaces
10
+ - publicly exposed methods should handle both old and new interfaces
11
+ - when making a breaking change or adding a new public helper function that is exported in `@n8n/ai-node-sdk`, make sure to update `AI_NODE_SDK_VERSION` in `ai-node-sdk-version.ts`
8
12
 
9
13
  ## Development
10
14
 
@@ -19,464 +23,7 @@ pnpm test
19
23
  pnpm dev
20
24
  ```
21
25
 
22
- ## Running examples
23
26
 
24
- ```bash
25
- pnpm build:examples
26
- ```
27
-
28
- Update your env file with:
29
-
30
- ```bash
31
- N8N_CUSTOM_EXTENSIONS="<PATH_TO_N8N>/packages/@n8n/ai-utilities/dist_examples/examples/nodes"
32
- ```
33
-
34
- Start n8n and add "OpenAI Simple" or "OpenAI Custom" to workflows
35
-
36
- # Chat model SDK
37
-
38
- ## Core Pattern
39
-
40
- ### Option A: OpenAI-Compatible APIs (easiest)
41
-
42
- Pass config directly to `supplyModel` for providers that follow the OpenAI API format:
43
-
44
- ```typescript
45
- import { supplyModel } from '@n8n/ai-utilities';
46
-
47
- return supplyModel(this, {
48
- type: 'openai',
49
- modelId: 'model-name',
50
- apiKey: 'your-api-key',
51
- baseURL: 'https://api.provider.com/v1', // OpenRouter, DeepSeek, etc.
52
- });
53
- ```
54
-
55
- ### Option B: Custom API (full control)
56
-
57
- Extend `BaseChatModel` and implement `generate()` + `stream()`:
58
-
59
- ```typescript
60
- import { BaseChatModel, supplyModel, type Message, type GenerateResult, type StreamChunk } from '@n8n/ai-utilities';
61
-
62
- class MyChatModel extends BaseChatModel {
63
- async generate(messages: Message[]): Promise<GenerateResult> {
64
- // Call your API, convert messages to provider format...
65
- return { text: '...', toolCalls: [...] };
66
- }
67
-
68
- async *stream(messages: Message[]): AsyncIterable<StreamChunk> {
69
- // Stream from your API...
70
- yield { type: 'text-delta', textDelta: '...' };
71
- yield { type: 'finish', finishReason: 'stop' };
72
- }
73
- }
74
-
75
- const model = new MyChatModel('my-provider', 'model-id', { apiKey: '...' });
76
- return supplyModel(this, model);
77
- ```
78
-
79
- ---
80
-
81
- ## Before/After Examples
82
-
83
- ### Example 1: LmChatOpenRouter
84
-
85
- **Before (LangChain):**
86
-
87
- ```typescript
88
- import { ChatOpenAI } from '@langchain/openai';
89
- import { N8nLlmTracing } from '../N8nLlmTracing';
90
-
91
- async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
92
- const credentials = await this.getCredentials<OpenAICompatibleCredential>('openRouterApi');
93
- const modelName = this.getNodeParameter('model', itemIndex) as string;
94
- const options = this.getNodeParameter('options', itemIndex, {}) as { ... };
95
-
96
- const model = new ChatOpenAI({
97
- apiKey: credentials.apiKey,
98
- model: modelName,
99
- ...options,
100
- configuration: { baseURL: credentials.url, ... },
101
- callbacks: [new N8nLlmTracing(this)],
102
- onFailedAttempt: makeN8nLlmFailedAttemptHandler(this, openAiFailedAttemptHandler),
103
- });
104
-
105
- return { response: model };
106
- }
107
- ```
108
-
109
- **After (SDK):**
110
-
111
- ```typescript
112
- import { supplyModel } from '@n8n/ai-utilities';
113
-
114
- async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
115
- const credentials = await this.getCredentials<{ url: string; apiKey: string }>('openRouterApi');
116
- const modelName = this.getNodeParameter('model', itemIndex) as string;
117
- const options = this.getNodeParameter('options', itemIndex, {}) as { temperature?: number };
118
-
119
- return supplyModel(this, {
120
- type: 'openai',
121
- modelId: modelName,
122
- apiKey: credentials.apiKey,
123
- baseURL: credentials.url,
124
- ...options,
125
- });
126
- }
127
- ```
128
-
129
- > **Note:** `type: 'openai'` uses the SDK's built-in OpenAI-compatible implementation.
130
- > Works with OpenRouter, DeepSeek, Azure OpenAI, and any provider following the OpenAI API format.
131
-
132
- ---
133
-
134
- ## Community Node Examples
135
-
136
- ### ImaginaryLLM Chat Model
137
-
138
- ```typescript
139
- import {
140
- BaseChatModel,
141
- supplyModel,
142
- type Message,
143
- type GenerateResult,
144
- type StreamChunk,
145
- type ChatModelConfig,
146
- } from '@n8n/ai-utilities';
147
- import { NodeConnectionTypes, type INodeType, type ISupplyDataFunctions, type SupplyData } from 'n8n-workflow';
148
-
149
- // Custom chat model extending BaseChatModel
150
- class ImaginaryLlmChatModel extends BaseChatModel {
151
- constructor(
152
- private apiKey: string,
153
- modelId: string,
154
- config?: ChatModelConfig,
155
- ) {
156
- super('imaginary-llm', modelId, config);
157
- }
158
-
159
- async generate(messages: Message[], config?: ChatModelConfig): Promise<GenerateResult> {
160
- // Convert n8n messages to provider format
161
- const providerMessages = messages.map(m => ({
162
- speaker: m.role === 'user' ? 'user' : m.role === 'assistant' ? 'bot' : m.role,
163
- text: m.content.find(c => c.type === 'text')?.text ?? '',
164
- }));
165
-
166
- // Call the API
167
- const response = await fetch('https://api.imaginary-llm.example.com/v1/generate', {
168
- method: 'POST',
169
- headers: {
170
- 'Authorization': `Bearer ${this.apiKey}`,
171
- 'Content-Type': 'application/json',
172
- },
173
- body: JSON.stringify({
174
- model: this.modelId,
175
- conversation: providerMessages,
176
- settings: {
177
- creativity: config?.temperature ?? 0.7,
178
- max_length: config?.maxTokens,
179
- },
180
- }),
181
- });
182
-
183
- const data = await response.json();
184
-
185
- return {
186
- text: data.reply.text,
187
- toolCalls: data.reply.actions?.map((a: any) => ({
188
- id: a.id,
189
- name: a.name,
190
- arguments: a.params,
191
- })),
192
- usage: data.metrics ? {
193
- promptTokens: data.metrics.input_tokens,
194
- completionTokens: data.metrics.output_tokens,
195
- totalTokens: data.metrics.input_tokens + data.metrics.output_tokens,
196
- } : undefined,
197
- };
198
- }
199
-
200
- async *stream(messages: Message[], config?: ChatModelConfig): AsyncIterable<StreamChunk> {
201
- // Streaming implementation...
202
- yield { type: 'text-delta', textDelta: '...' };
203
- yield { type: 'finish', finishReason: 'stop' };
204
- }
205
- }
206
-
207
- // The n8n node
208
- export class LmChatImaginaryLlm implements INodeType {
209
- description = {
210
- displayName: 'ImaginaryLLM Chat Model',
211
- name: 'lmChatImaginaryLlm',
212
- outputs: [NodeConnectionTypes.AiLanguageModel],
213
- credentials: [{ name: 'imaginaryLlmApi', required: true }],
214
- properties: [
215
- { displayName: 'Model', name: 'model', type: 'options', options: [
216
- { name: 'Imaginary Pro', value: 'imaginary-pro' },
217
- { name: 'Imaginary Fast', value: 'imaginary-fast' },
218
- ], default: 'imaginary-pro' },
219
- { displayName: 'Temperature', name: 'temperature', type: 'number', default: 0.7 },
220
- ],
221
- };
222
-
223
- async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
224
- const credentials = await this.getCredentials<{ apiKey: string }>('imaginaryLlmApi');
225
- const modelName = this.getNodeParameter('model', itemIndex) as string;
226
- const temperature = this.getNodeParameter('temperature', itemIndex) as number;
227
-
228
- const model = new ImaginaryLlmChatModel(credentials.apiKey, modelName, { temperature });
229
-
230
- return supplyModel(this, model);
231
- }
232
- }
233
- ```
234
-
235
- ---
236
-
237
- # Memory SDK
238
-
239
- The Memory SDK provides abstractions for building conversation memory nodes without LangChain dependencies.
240
-
241
- ## Architecture
242
-
243
- Memory uses a **two-layer design**:
244
-
245
- 1. **ChatHistory** (Storage Layer) - Where messages are stored (your custom implementation)
246
- 2. **ChatMemory** (Logic Layer) - How messages are managed (windowing, session scoping)
247
-
248
- ### Naming Convention
249
-
250
- The SDK uses n8n-specific naming to avoid confusion with LangChain classes:
251
-
252
- | n8n SDK | LangChain Equivalent |
253
- |---------|---------------------|
254
- | `ChatHistory` (interface) | `BaseChatMessageHistory` |
255
- | `BaseChatHistory` (base class) | `BaseChatMessageHistory` |
256
- | `ChatMemory` (interface) | `BaseChatMemory` |
257
- | `BaseChatMemory` (base class) | `BaseChatMemory` |
258
- | `WindowedChatMemory` | `BufferWindowMemory` |
259
-
260
- ## Core Pattern
261
-
262
- ### Option A: Custom Storage
263
-
264
- For exotic databases not covered by the SDK, extend `BaseChatHistory`:
265
-
266
- ```typescript
267
- import {
268
- BaseChatHistory,
269
- WindowedChatMemory,
270
- supplyMemory,
271
- type Message,
272
- } from '@n8n/ai-utilities';
273
-
274
- class MyChatHistory extends BaseChatHistory {
275
- constructor(private sessionId: string) {
276
- super();
277
- }
278
-
279
- async getMessages(): Promise<Message[]> {
280
- // Read from your storage...
281
- return [];
282
- }
283
-
284
- async addMessage(message: Message): Promise<void> {
285
- // Write to your storage...
286
- }
287
-
288
- async clear(): Promise<void> {
289
- // Clear your storage...
290
- }
291
- }
292
-
293
- const history = new MyChatHistory(sessionId);
294
- const memory = new WindowedChatMemory(history, { windowSize: 10 });
295
- return supplyMemory(this, memory);
296
- ```
297
-
298
- ### Option B: Custom Memory Logic
299
-
300
- For custom memory behavior (not just storage), extend `BaseChatMemory`:
301
-
302
- ```typescript
303
- import {
304
- BaseChatMemory,
305
- supplyMemory,
306
- type Message,
307
- type ChatHistory,
308
- type ChatMemory,
309
- } from '@n8n/ai-utilities';
310
-
311
- class MyCustomChatMemory extends BaseChatMemory {
312
- readonly chatHistory: ChatHistory;
313
-
314
- constructor(chatHistory: ChatHistory) {
315
- super();
316
- this.chatHistory = chatHistory;
317
- }
318
-
319
- async loadMessages(): Promise<Message[]> {
320
- const messages = await this.chatHistory.getMessages();
321
- // Apply your custom logic here...
322
- return messages;
323
- }
324
-
325
- async saveTurn(input: string, output: string): Promise<void> {
326
- await this.chatHistory.addMessages([
327
- { role: 'user', content: [{ type: 'text', text: input }] },
328
- { role: 'assistant', content: [{ type: 'text', text: output }] },
329
- ]);
330
- }
331
-
332
- async clear(): Promise<void> {
333
- await this.chatHistory.clear();
334
- }
335
- }
336
-
337
- const history = new MyChatHistory(sessionId);
338
- const memory = new MyCustomChatMemory(history);
339
- return supplyMemory(this, memory);
340
- ```
341
-
342
- ---
343
-
344
- ## Community Node Examples
345
-
346
- ### ImaginaryDB Memory Node
347
-
348
- ```typescript
349
- import {
350
- BaseChatHistory,
351
- WindowedChatMemory,
352
- supplyMemory,
353
- type Message,
354
- } from '@n8n/ai-utilities';
355
- import {
356
- NodeConnectionTypes,
357
- type INodeType,
358
- type ISupplyDataFunctions,
359
- type SupplyData,
360
- type IHttpRequestMethods,
361
- } from 'n8n-workflow';
362
-
363
- // Custom storage implementation using n8n's HTTP helpers
364
- class ImaginaryDbChatHistory extends BaseChatHistory {
365
- constructor(
366
- private sessionId: string,
367
- private baseUrl: string,
368
- private apiKey: string,
369
- private httpRequest: ISupplyDataFunctions['helpers']['httpRequest'],
370
- ) {
371
- super();
372
- }
373
-
374
- async getMessages(): Promise<Message[]> {
375
- const data = await this.httpRequest({
376
- method: 'GET',
377
- url: `${this.baseUrl}/sessions/${this.sessionId}/messages`,
378
- headers: { Authorization: `Bearer ${this.apiKey}` },
379
- json: true,
380
- });
381
-
382
- // Convert from provider format to n8n Message format
383
- return data.messages.map((m: any) => ({
384
- role: m.speaker === 'user' ? 'user' : m.speaker === 'bot' ? 'assistant' : m.speaker,
385
- content: [{ type: 'text', text: m.text }],
386
- }));
387
- }
388
-
389
- async addMessage(message: Message): Promise<void> {
390
- const text = message.content.find((c) => c.type === 'text')?.text ?? '';
391
- await this.httpRequest({
392
- method: 'POST',
393
- url: `${this.baseUrl}/sessions/${this.sessionId}/messages`,
394
- headers: { Authorization: `Bearer ${this.apiKey}` },
395
- body: {
396
- speaker: message.role === 'user' ? 'user' : message.role === 'assistant' ? 'bot' : message.role,
397
- text,
398
- },
399
- json: true,
400
- });
401
- }
402
-
403
- async clear(): Promise<void> {
404
- await this.httpRequest({
405
- method: 'DELETE',
406
- url: `${this.baseUrl}/sessions/${this.sessionId}`,
407
- headers: { Authorization: `Bearer ${this.apiKey}` },
408
- });
409
- }
410
- }
411
-
412
- // The n8n node
413
- export class MemoryImaginaryDb implements INodeType {
414
- description = {
415
- displayName: 'ImaginaryDB Memory',
416
- name: 'memoryImaginaryDb',
417
- icon: 'file:imaginarydb.svg',
418
- group: ['transform'],
419
- version: 1,
420
- description: 'Use ImaginaryDB for chat memory storage',
421
- defaults: { name: 'ImaginaryDB Memory' },
422
- codex: { categories: ['assistant'], subcategories: { AI: ['Memory'] } },
423
- inputs: [],
424
- outputs: [NodeConnectionTypes.AiMemory],
425
- outputNames: ['Memory'],
426
- credentials: [{ name: 'imaginaryDbApi', required: true }],
427
- properties: [
428
- {
429
- displayName: 'Session ID',
430
- name: 'sessionId',
431
- type: 'string',
432
- default: '={{ $json.sessionId }}',
433
- description: 'Unique identifier for the conversation session',
434
- },
435
- {
436
- displayName: 'Window Size',
437
- name: 'windowSize',
438
- type: 'number',
439
- default: 10,
440
- description: 'Number of recent message pairs to keep in context',
441
- },
442
- ],
443
- };
444
-
445
- async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
446
- const credentials = await this.getCredentials<{ apiKey: string; baseUrl: string }>('imaginaryDbApi');
447
- const sessionId = this.getNodeParameter('sessionId', itemIndex) as string;
448
- const windowSize = this.getNodeParameter('windowSize', itemIndex) as number;
449
-
450
- // Pass n8n's HTTP request helper directly
451
- const history = new ImaginaryDbChatHistory(
452
- sessionId,
453
- credentials.baseUrl,
454
- credentials.apiKey,
455
- this.helpers.httpRequest,
456
- );
457
- const memory = new WindowedChatMemory(history, { windowSize });
458
-
459
- return supplyMemory(this, memory);
460
- }
461
- }
462
- ```
463
-
464
- > **Note:** Community nodes must use `this.helpers.httpRequest` or `this.helpers.httpRequestWithAuthentication`
465
- > for HTTP calls. Direct `fetch` or other global APIs are not allowed.
466
-
467
-
468
- ---
469
-
470
- ## Summary
27
+ ## Usage
471
28
 
472
- | Before (LangChain) | After (SDK) |
473
- |--------------------|-------------|
474
- | `import { ChatOpenAI } from '@langchain/openai'` | `import { supplyModel } from '@n8n/ai-utilities'` |
475
- | `new ChatOpenAI({ ... })` | `supplyModel(this, { type: 'openai', ... })` |
476
- | Custom model provider | `class MyModel extends BaseChatModel { ... }` |
477
- | `return { response: model }` | `return supplyModel(this, model)` |
478
- | `import { BufferWindowMemory } from '@langchain/classic/memory'` | `import { WindowedChatMemory } from '@n8n/ai-utilities'` |
479
- | Custom storage backend | `class MyHistory extends BaseChatHistory { ... }` |
480
- | `return { response: logWrapper(memory, this) }` | `return supplyMemory(this, memory)` |
481
- | LangChain message types | `Message` with roles: `system`, `human`, `ai`, `tool` |
482
- | `tool_calls[].args` | `toolCalls[].arguments` |
29
+ For public SDK documentation see `@n8n/ai-node-sdk`.
@@ -9,7 +9,7 @@ import { ChatGenerationChunk } from '@langchain/core/outputs';
9
9
  import type { Runnable } from '@langchain/core/runnables';
10
10
  import type { ISupplyDataFunctions } from 'n8n-workflow';
11
11
  import type { ChatModel, ChatModelConfig } from '../types/chat-model';
12
- export declare class LangchainAdapter<CallOptions extends ChatModelConfig = ChatModelConfig> extends BaseChatModel<CallOptions> {
12
+ export declare class LangchainChatModelAdapter<CallOptions extends ChatModelConfig = ChatModelConfig> extends BaseChatModel<CallOptions> {
13
13
  private chatModel;
14
14
  private ctx?;
15
15
  constructor(chatModel: ChatModel, ctx?: ISupplyDataFunctions | undefined);
@@ -0,0 +1,201 @@
1
+ (function (factory) {
2
+ if (typeof module === "object" && typeof module.exports === "object") {
3
+ var v = factory(require, exports);
4
+ if (v !== undefined) module.exports = v;
5
+ }
6
+ else if (typeof define === "function" && define.amd) {
7
+ define(["require", "exports", "@langchain/core/language_models/chat_models", "@langchain/core/messages", "@langchain/core/outputs", "../converters/message", "../converters/tool", "../utils/failed-attempt-handler/n8nLlmFailedAttemptHandler", "../utils/n8n-llm-tracing"], factory);
8
+ }
9
+ })(function (require, exports) {
10
+ "use strict";
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.LangchainChatModelAdapter = void 0;
13
+ const chat_models_1 = require("@langchain/core/language_models/chat_models");
14
+ const messages_1 = require("@langchain/core/messages");
15
+ const outputs_1 = require("@langchain/core/outputs");
16
+ const message_1 = require("../converters/message");
17
+ const tool_1 = require("../converters/tool");
18
+ const n8nLlmFailedAttemptHandler_1 = require("../utils/failed-attempt-handler/n8nLlmFailedAttemptHandler");
19
+ const n8n_llm_tracing_1 = require("../utils/n8n-llm-tracing");
20
+ class LangchainChatModelAdapter extends chat_models_1.BaseChatModel {
21
+ constructor(chatModel, ctx) {
22
+ const params = {
23
+ ...(ctx
24
+ ? {
25
+ callbacks: [
26
+ new n8n_llm_tracing_1.N8nLlmTracing(ctx, {
27
+ tokensUsageParser: (result) => {
28
+ const tokenUsage = result?.llmOutput?.tokenUsage;
29
+ const completionTokens = tokenUsage?.output_tokens ?? 0;
30
+ const promptTokens = tokenUsage?.input_tokens ?? 0;
31
+ return {
32
+ completionTokens,
33
+ promptTokens,
34
+ totalTokens: completionTokens + promptTokens,
35
+ };
36
+ },
37
+ }),
38
+ ],
39
+ onFailedAttempt: (0, n8nLlmFailedAttemptHandler_1.makeN8nLlmFailedAttemptHandler)(ctx),
40
+ }
41
+ : {}),
42
+ };
43
+ super(params);
44
+ this.chatModel = chatModel;
45
+ this.ctx = ctx;
46
+ }
47
+ _llmType() {
48
+ return 'n8n-chat-model';
49
+ }
50
+ async _generate(messages, options) {
51
+ const transformedMessages = messages.map(message_1.fromLcMessage);
52
+ const result = await this.chatModel.generate(transformedMessages, options);
53
+ const lcMessage = (0, message_1.toLcMessage)(result.message);
54
+ const usage_metadata = result.usage
55
+ ? {
56
+ input_tokens: result.usage.promptTokens ?? 0,
57
+ output_tokens: result.usage.completionTokens ?? 0,
58
+ total_tokens: result.usage.totalTokens ?? 0,
59
+ input_token_details: result.usage.inputTokenDetails
60
+ ? {
61
+ cache_read: result.usage.inputTokenDetails.cacheRead,
62
+ }
63
+ : undefined,
64
+ output_token_details: result.usage.outputTokenDetails
65
+ ? {
66
+ reasoning: result.usage.outputTokenDetails.reasoning,
67
+ }
68
+ : undefined,
69
+ }
70
+ : undefined;
71
+ if (messages_1.AIMessage.isInstance(lcMessage)) {
72
+ lcMessage.usage_metadata = usage_metadata;
73
+ }
74
+ lcMessage.response_metadata = {
75
+ ...result.providerMetadata,
76
+ model: this.chatModel.modelId,
77
+ provider: this.chatModel.provider,
78
+ };
79
+ return {
80
+ generations: [
81
+ {
82
+ text: lcMessage.text,
83
+ message: lcMessage,
84
+ },
85
+ ],
86
+ llmOutput: {
87
+ id: result.id,
88
+ tokenUsage: usage_metadata,
89
+ },
90
+ };
91
+ }
92
+ async *_streamResponseChunks(messages, options, runManager) {
93
+ const genericMessages = messages.map(message_1.fromLcMessage);
94
+ const stream = this.chatModel.stream(genericMessages, options);
95
+ for await (const chunk of stream) {
96
+ let lcChunk = undefined;
97
+ if (chunk.type === 'text-delta') {
98
+ const content = [
99
+ {
100
+ type: 'text',
101
+ text: chunk.delta,
102
+ },
103
+ ];
104
+ lcChunk = new outputs_1.ChatGenerationChunk({
105
+ message: new messages_1.AIMessageChunk({
106
+ content,
107
+ }),
108
+ text: chunk.delta,
109
+ });
110
+ }
111
+ else if (chunk.type === 'tool-call-delta') {
112
+ const tool_call_chunks = [
113
+ {
114
+ type: 'tool_call_chunk',
115
+ id: chunk.id,
116
+ name: chunk.name,
117
+ args: chunk.argumentsDelta,
118
+ index: 0,
119
+ },
120
+ ];
121
+ lcChunk = new outputs_1.ChatGenerationChunk({
122
+ message: new messages_1.AIMessageChunk({
123
+ content: '',
124
+ tool_call_chunks,
125
+ }),
126
+ text: '',
127
+ });
128
+ }
129
+ else if (chunk.type === 'finish') {
130
+ const usage_metadata = chunk.usage
131
+ ? {
132
+ input_tokens: chunk.usage.promptTokens ?? 0,
133
+ output_tokens: chunk.usage.completionTokens ?? 0,
134
+ total_tokens: chunk.usage.totalTokens ?? 0,
135
+ }
136
+ : undefined;
137
+ lcChunk = new outputs_1.ChatGenerationChunk({
138
+ message: new messages_1.AIMessageChunk({
139
+ content: '',
140
+ usage_metadata,
141
+ response_metadata: {
142
+ finish_reason: chunk.finishReason,
143
+ },
144
+ }),
145
+ text: '',
146
+ generationInfo: {
147
+ finish_reason: chunk.finishReason,
148
+ },
149
+ });
150
+ }
151
+ else if (chunk.type === 'error') {
152
+ lcChunk = new outputs_1.ChatGenerationChunk({
153
+ message: new messages_1.AIMessageChunk({
154
+ content: '',
155
+ response_metadata: {
156
+ finish_reason: 'error',
157
+ error: chunk.error,
158
+ },
159
+ }),
160
+ text: '',
161
+ generationInfo: {
162
+ finish_reason: 'error',
163
+ error: chunk.error,
164
+ },
165
+ });
166
+ }
167
+ else if (chunk.type === 'content') {
168
+ const lcMessage = (0, message_1.toLcMessage)({
169
+ role: 'assistant',
170
+ content: [chunk.content],
171
+ id: chunk.id,
172
+ });
173
+ const lcMessageChunk = new messages_1.AIMessageChunk({
174
+ content: lcMessage.content,
175
+ id: lcMessage.id,
176
+ name: lcMessage.name,
177
+ });
178
+ lcChunk = new outputs_1.ChatGenerationChunk({
179
+ message: lcMessageChunk,
180
+ text: lcMessage.text,
181
+ });
182
+ }
183
+ if (lcChunk) {
184
+ yield lcChunk;
185
+ await runManager?.handleLLMNewToken(lcChunk.text ?? '', {
186
+ prompt: 0,
187
+ completion: 0,
188
+ }, undefined, undefined, undefined, { chunk: lcChunk });
189
+ }
190
+ }
191
+ }
192
+ bindTools(tools) {
193
+ const genericTools = tools.map(tool_1.fromLcTool);
194
+ const newModel = this.chatModel.withTools(genericTools);
195
+ const newAdapter = new LangchainChatModelAdapter(newModel, this.ctx);
196
+ return newAdapter;
197
+ }
198
+ }
199
+ exports.LangchainChatModelAdapter = LangchainChatModelAdapter;
200
+ });
201
+ //# sourceMappingURL=langchain-chat-model.js.map