n8n-nodes-tembory 1.0.0 → 1.0.2
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.
- package/README.md +6 -2
- package/dist/credentials/Mem0Api.credentials.js +5 -20
- package/dist/nodes/Mem0/GenericFunctions.js +31 -30
- package/dist/nodes/Mem0/Mem0Memory.node.js +22 -49
- package/package.json +2 -3
- package/dist/credentials/Mem0SelfHostedApi.credentials.d.ts +0 -10
- package/dist/credentials/Mem0SelfHostedApi.credentials.js +0 -45
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Node de memoria operacional da Tembory para agentes de IA no n8n.
|
|
4
4
|
|
|
5
|
-
Versao atual: `1.0.
|
|
5
|
+
Versao atual: `1.0.1`.
|
|
6
6
|
|
|
7
7
|
O Tembory entrega contexto rico para o AI Agent sem depender apenas do historico textual da conversa. Ele combina memoria semantica, working memory, decision state, fatos estaveis do lead, historico de tools, estado operacional, action ledger, timeline de entidades, compressao de memoria, grafo, mensagens recentes e diagnosticos.
|
|
8
8
|
|
|
@@ -19,7 +19,7 @@ O smoke simula 12 trocas com tools de agenda, valida tool history, working memor
|
|
|
19
19
|
## Principais recursos
|
|
20
20
|
|
|
21
21
|
- Porta `ai_memory` para conectar direto no AI Agent do n8n.
|
|
22
|
-
- Campo `Projeto` para isolar memórias por cliente/workspace mesmo quando a origem repete o mesmo ID de conversa.
|
|
22
|
+
- Campo `Projeto` para isolar memórias por cliente/workspace mesmo quando a origem repete o mesmo ID de conversa. Ele e um namespace operacional do workflow; nao e o Project ID da credencial.
|
|
23
23
|
- Entradas opcionais `ai_languageModel` e `ai_embedding` para usar os modelos conectados pelo cliente no proprio n8n.
|
|
24
24
|
- Profile facts: nome, empresa, email, telefone, preferencias e contexto comercial.
|
|
25
25
|
- Tool history: quais tools foram chamadas, quando, com qual status e resultado.
|
|
@@ -36,6 +36,10 @@ O smoke simula 12 trocas com tools de agenda, valida tool history, working memor
|
|
|
36
36
|
|
|
37
37
|
O node nao fornece LLM nem embedding gerenciados pela Tembory. Todo uso de LLM e embedding deve vir dos nodes/modelos conectados no cluster do n8n do cliente. O backend do Tembory armazena e consulta os vetores recebidos prontos.
|
|
38
38
|
|
|
39
|
+
## Credencial
|
|
40
|
+
|
|
41
|
+
Use apenas a credencial `Tembory API` com a API key gerada no SaaS. A API key ja identifica a conta e o projeto comercial no Tembory; por isso o node nao pede Organization ID, Project ID nem URL self-hosted.
|
|
42
|
+
|
|
39
43
|
## Presets operacionais
|
|
40
44
|
|
|
41
45
|
O campo avancado `Preset Operacional` preenche defaults sem sobrescrever valores configurados manualmente:
|
|
@@ -4,7 +4,7 @@ exports.Mem0Api = void 0;
|
|
|
4
4
|
class Mem0Api {
|
|
5
5
|
constructor() {
|
|
6
6
|
this.name = 'mem0Api';
|
|
7
|
-
this.displayName = 'Tembory
|
|
7
|
+
this.displayName = 'Tembory API';
|
|
8
8
|
this.documentationUrl = 'https://tembory.com';
|
|
9
9
|
this.properties = [
|
|
10
10
|
{
|
|
@@ -14,39 +14,24 @@ class Mem0Api {
|
|
|
14
14
|
typeOptions: { password: true },
|
|
15
15
|
default: '',
|
|
16
16
|
required: true,
|
|
17
|
-
description: 'API key
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
displayName: 'Organization ID',
|
|
21
|
-
name: 'orgId',
|
|
22
|
-
type: 'string',
|
|
23
|
-
default: '',
|
|
24
|
-
description: 'Optional: Organization ID',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
displayName: 'Project ID',
|
|
28
|
-
name: 'projectId',
|
|
29
|
-
type: 'string',
|
|
30
|
-
default: '',
|
|
31
|
-
description: 'Optional: Project ID',
|
|
17
|
+
description: 'API key generated in the Tembory SaaS dashboard. It already identifies the account and project.',
|
|
32
18
|
},
|
|
33
19
|
];
|
|
34
20
|
this.authenticate = {
|
|
35
21
|
type: 'generic',
|
|
36
22
|
properties: {
|
|
37
23
|
headers: {
|
|
38
|
-
'
|
|
24
|
+
'x-tembory-api-key': '={{$credentials.apiKey}}',
|
|
39
25
|
},
|
|
40
26
|
},
|
|
41
27
|
};
|
|
42
28
|
this.test = {
|
|
43
29
|
request: {
|
|
44
|
-
baseURL: 'https://api.
|
|
45
|
-
url: '/
|
|
30
|
+
baseURL: 'https://api.tembory.com',
|
|
31
|
+
url: '/healthz',
|
|
46
32
|
method: 'GET',
|
|
47
33
|
},
|
|
48
34
|
};
|
|
49
35
|
}
|
|
50
36
|
}
|
|
51
37
|
exports.Mem0Api = Mem0Api;
|
|
52
|
-
|
|
@@ -3,46 +3,47 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.mem0ApiRequest = mem0ApiRequest;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
async function mem0ApiRequest(method, endpoint, body = {}, qs = {}) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (authenticationType === 'cloud')
|
|
15
|
-
authenticationType = 'nuvem';
|
|
16
|
-
if (authenticationType === 'selfHosted')
|
|
17
|
-
authenticationType = 'selfHosted';
|
|
18
|
-
}
|
|
19
|
-
let credentials;
|
|
20
|
-
let baseUrl;
|
|
21
|
-
if (authenticationType === 'nuvem') {
|
|
22
|
-
credentials = await this.getCredentials('mem0Api');
|
|
23
|
-
baseUrl = 'https://api.mem0.ai';
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
credentials = await this.getCredentials('mem0SelfHostedApi');
|
|
27
|
-
baseUrl = credentials.baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
28
|
-
}
|
|
6
|
+
const credentials = await this.getCredentials('mem0Api');
|
|
7
|
+
const baseUrl = 'https://api.tembory.com';
|
|
8
|
+
const operation = resolveGatewayOperation(method, endpoint);
|
|
9
|
+
const payload = {
|
|
10
|
+
...(qs && typeof qs === 'object' ? qs : {}),
|
|
11
|
+
...(body && typeof body === 'object' ? body : {}),
|
|
12
|
+
operation,
|
|
13
|
+
};
|
|
29
14
|
const options = {
|
|
30
|
-
method,
|
|
31
|
-
body,
|
|
32
|
-
|
|
33
|
-
url: `${baseUrl}${endpoint}`,
|
|
15
|
+
method: 'POST',
|
|
16
|
+
body: payload,
|
|
17
|
+
url: `${baseUrl}/api/tembory/memory`,
|
|
34
18
|
json: true,
|
|
35
19
|
};
|
|
36
20
|
if (credentials.apiKey) {
|
|
37
21
|
options.headers = {
|
|
38
|
-
'
|
|
22
|
+
'x-tembory-api-key': credentials.apiKey,
|
|
39
23
|
};
|
|
40
24
|
}
|
|
41
25
|
try {
|
|
42
|
-
|
|
26
|
+
const response = await this.helpers.request(options);
|
|
27
|
+
return unwrapGatewayResponse(response);
|
|
43
28
|
}
|
|
44
29
|
catch (error) {
|
|
45
30
|
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
46
31
|
}
|
|
47
32
|
}
|
|
48
|
-
|
|
33
|
+
function resolveGatewayOperation(method, endpoint) {
|
|
34
|
+
const normalizedMethod = String(method || '').toUpperCase();
|
|
35
|
+
const path = String(endpoint || '').toLowerCase();
|
|
36
|
+
if (path.includes('search') || normalizedMethod === 'GET')
|
|
37
|
+
return 'memory.search';
|
|
38
|
+
if (path.includes('delete'))
|
|
39
|
+
return 'memory.delete';
|
|
40
|
+
if (path.includes('relation'))
|
|
41
|
+
return 'memory.search';
|
|
42
|
+
return 'memory.add';
|
|
43
|
+
}
|
|
44
|
+
function unwrapGatewayResponse(response) {
|
|
45
|
+
if (response && typeof response === 'object' && response.memoryCore && typeof response.memoryCore === 'object' && 'response' in response.memoryCore) {
|
|
46
|
+
return response.memoryCore.response;
|
|
47
|
+
}
|
|
48
|
+
return response;
|
|
49
|
+
}
|
|
@@ -112,8 +112,12 @@ const memoryText = (memory) => {
|
|
|
112
112
|
if (!memory)
|
|
113
113
|
return '';
|
|
114
114
|
if (typeof memory === 'string')
|
|
115
|
-
return memory;
|
|
116
|
-
|
|
115
|
+
return parseRecentMessageMarker(memory)?.content || memory;
|
|
116
|
+
const meta = metadataOf(memory);
|
|
117
|
+
if (meta.kind === 'recent_message' && meta.content)
|
|
118
|
+
return String(meta.content);
|
|
119
|
+
const text = memory.memory || memory.text || memory.value || memory.content || memory.data || safeStringify(memory);
|
|
120
|
+
return parseRecentMessageMarker(text)?.content || text;
|
|
117
121
|
};
|
|
118
122
|
const normalizeResults = (value) => {
|
|
119
123
|
if (!value)
|
|
@@ -1512,20 +1516,6 @@ class Mem0Memory {
|
|
|
1512
1516
|
{
|
|
1513
1517
|
name: 'mem0Api',
|
|
1514
1518
|
required: true,
|
|
1515
|
-
displayOptions: {
|
|
1516
|
-
show: {
|
|
1517
|
-
authType: ['cloud'],
|
|
1518
|
-
},
|
|
1519
|
-
},
|
|
1520
|
-
},
|
|
1521
|
-
{
|
|
1522
|
-
name: 'mem0SelfHostedApi',
|
|
1523
|
-
required: true,
|
|
1524
|
-
displayOptions: {
|
|
1525
|
-
show: {
|
|
1526
|
-
authType: ['selfHosted'],
|
|
1527
|
-
},
|
|
1528
|
-
},
|
|
1529
1519
|
},
|
|
1530
1520
|
],
|
|
1531
1521
|
codex: {
|
|
@@ -1536,16 +1526,6 @@ class Mem0Memory {
|
|
|
1536
1526
|
},
|
|
1537
1527
|
},
|
|
1538
1528
|
properties: [
|
|
1539
|
-
{
|
|
1540
|
-
displayName: 'Autenticação',
|
|
1541
|
-
name: 'authType',
|
|
1542
|
-
type: 'options',
|
|
1543
|
-
options: [
|
|
1544
|
-
{ name: 'Tembory Cloud', value: 'cloud' },
|
|
1545
|
-
{ name: 'Self-hosted', value: 'selfHosted' },
|
|
1546
|
-
],
|
|
1547
|
-
default: 'cloud',
|
|
1548
|
-
},
|
|
1549
1529
|
{
|
|
1550
1530
|
displayName: 'ID da Thread',
|
|
1551
1531
|
name: 'threadId',
|
|
@@ -1558,7 +1538,7 @@ class Mem0Memory {
|
|
|
1558
1538
|
name: 'project',
|
|
1559
1539
|
type: 'string',
|
|
1560
1540
|
default: '={{ $json.project || $json.projectId || $json.accountId || $json.body?.account?.id || "" }}',
|
|
1561
|
-
description: 'Namespace
|
|
1541
|
+
description: 'Namespace operacional do workflow para separar clientes/workspaces. Nao e o Project ID da credencial; a API key do Tembory ja identifica o projeto comercial.',
|
|
1562
1542
|
},
|
|
1563
1543
|
{
|
|
1564
1544
|
displayName: 'Modo de Recuperação de Contexto',
|
|
@@ -2192,38 +2172,31 @@ class Mem0Memory {
|
|
|
2192
2172
|
}
|
|
2193
2173
|
let persistedRecentMessages = [];
|
|
2194
2174
|
let persistedToolHistory = [];
|
|
2195
|
-
|
|
2175
|
+
let persistedMemoryItems = [];
|
|
2176
|
+
if (adv.includeRecentMessages !== false || adv.includeToolHistory !== false) {
|
|
2196
2177
|
try {
|
|
2197
2178
|
const qs = { user_id: key };
|
|
2198
2179
|
if (adv.agentId)
|
|
2199
2180
|
qs.agent_id = String(adv.agentId);
|
|
2200
2181
|
if (adv.runId)
|
|
2201
2182
|
qs.run_id = String(adv.runId);
|
|
2202
|
-
const
|
|
2203
|
-
|
|
2204
|
-
.map(recentMessageFromMemory)
|
|
2205
|
-
.filter(Boolean)
|
|
2206
|
-
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
|
|
2183
|
+
const persistedRes = await GenericFunctions_1.mem0ApiRequest.call(this, 'GET', '/v1/memories/', {}, qs);
|
|
2184
|
+
persistedMemoryItems = normalizeResults(persistedRes);
|
|
2207
2185
|
}
|
|
2208
2186
|
catch (error) {
|
|
2209
|
-
connectedAi.errors.push(`
|
|
2187
|
+
connectedAi.errors.push(`persistedContext.load: ${error.message || String(error)}`);
|
|
2210
2188
|
}
|
|
2211
2189
|
}
|
|
2190
|
+
if (adv.includeRecentMessages !== false) {
|
|
2191
|
+
persistedRecentMessages = persistedMemoryItems
|
|
2192
|
+
.map(recentMessageFromMemory)
|
|
2193
|
+
.filter(Boolean)
|
|
2194
|
+
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
|
|
2195
|
+
}
|
|
2212
2196
|
if (adv.includeToolHistory !== false) {
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
qs.agent_id = String(adv.agentId);
|
|
2217
|
-
if (adv.runId)
|
|
2218
|
-
qs.run_id = String(adv.runId);
|
|
2219
|
-
const toolRes = await GenericFunctions_1.mem0ApiRequest.call(this, 'GET', '/v1/memories/', {}, qs);
|
|
2220
|
-
persistedToolHistory = normalizeResults(toolRes)
|
|
2221
|
-
.flatMap(toolHistoryItemsFromMemory)
|
|
2222
|
-
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
|
|
2223
|
-
}
|
|
2224
|
-
catch (error) {
|
|
2225
|
-
connectedAi.errors.push(`toolHistory.load: ${error.message || String(error)}`);
|
|
2226
|
-
}
|
|
2197
|
+
persistedToolHistory = persistedMemoryItems
|
|
2198
|
+
.flatMap(toolHistoryItemsFromMemory)
|
|
2199
|
+
.sort((a, b) => new Date(a.at || 0).getTime() - new Date(b.at || 0).getTime());
|
|
2227
2200
|
}
|
|
2228
2201
|
const allRecentMessages = dedupeRecentMessages((store.recentMessages[key] || []).concat(persistedRecentMessages));
|
|
2229
2202
|
const recentMessages = adv.includeRecentMessages === false ? [] : pruneByLimit(allRecentMessages, adv.recentMessagesLastN || 8);
|
|
@@ -2354,7 +2327,7 @@ class Mem0Memory {
|
|
|
2354
2327
|
superseded_by: enriched.superseded_by,
|
|
2355
2328
|
id: m.id || m.uuid || m.memory_id,
|
|
2356
2329
|
created_at: m.created_at || m.createdAt,
|
|
2357
|
-
raw: m,
|
|
2330
|
+
raw: adv.includeDiagnostics ? m : undefined,
|
|
2358
2331
|
};
|
|
2359
2332
|
});
|
|
2360
2333
|
const summary = vectorMemories.slice(0, Number(adv.summaryMaxFacts || 4)).map(memoryText).filter(Boolean);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-tembory",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Tembory node for n8n AI Agents with profile, tools, timeline, graph and semantic memory",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://tembory.com",
|
|
@@ -36,8 +36,7 @@
|
|
|
36
36
|
"n8n": {
|
|
37
37
|
"n8nNodesApiVersion": 1,
|
|
38
38
|
"credentials": [
|
|
39
|
-
"dist/credentials/Mem0Api.credentials.js"
|
|
40
|
-
"dist/credentials/Mem0SelfHostedApi.credentials.js"
|
|
39
|
+
"dist/credentials/Mem0Api.credentials.js"
|
|
41
40
|
],
|
|
42
41
|
"nodes": [
|
|
43
42
|
"dist/nodes/Mem0/Mem0Memory.node.js"
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
-
export declare class Mem0SelfHostedApi implements ICredentialType {
|
|
3
|
-
name: string;
|
|
4
|
-
displayName: string;
|
|
5
|
-
documentationUrl: string;
|
|
6
|
-
properties: INodeProperties[];
|
|
7
|
-
authenticate: IAuthenticateGeneric;
|
|
8
|
-
test: ICredentialTestRequest;
|
|
9
|
-
}
|
|
10
|
-
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Mem0SelfHostedApi = void 0;
|
|
4
|
-
class Mem0SelfHostedApi {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.name = 'mem0SelfHostedApi';
|
|
7
|
-
this.displayName = 'Tembory Self-Hosted API';
|
|
8
|
-
this.documentationUrl = 'https://tembory.com';
|
|
9
|
-
this.properties = [
|
|
10
|
-
{
|
|
11
|
-
displayName: 'Base URL',
|
|
12
|
-
name: 'baseUrl',
|
|
13
|
-
type: 'string',
|
|
14
|
-
default: 'http://localhost:8000',
|
|
15
|
-
required: true,
|
|
16
|
-
description: 'The base URL of your self-hosted Tembory memory backend',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
displayName: 'API Key',
|
|
20
|
-
name: 'apiKey',
|
|
21
|
-
type: 'string',
|
|
22
|
-
typeOptions: { password: true },
|
|
23
|
-
default: '',
|
|
24
|
-
description: 'API key if authentication enabled',
|
|
25
|
-
},
|
|
26
|
-
];
|
|
27
|
-
this.authenticate = {
|
|
28
|
-
type: 'generic',
|
|
29
|
-
properties: {
|
|
30
|
-
headers: {
|
|
31
|
-
'Authorization': '={{$credentials.apiKey ? "Token " + $credentials.apiKey : ""}}',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
this.test = {
|
|
36
|
-
request: {
|
|
37
|
-
baseURL: '={{$credentials.baseUrl}}',
|
|
38
|
-
url: '/v1/entities/',
|
|
39
|
-
method: 'GET',
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
exports.Mem0SelfHostedApi = Mem0SelfHostedApi;
|
|
45
|
-
|