150 lines
4.7 KiB
JavaScript
150 lines
4.7 KiB
JavaScript
function normalizeProjectId(value) {
|
|
const raw = String(value ?? '').trim();
|
|
if (!raw) return null;
|
|
if (!/^[a-zA-Z0-9_-]+$/.test(raw)) {
|
|
throw new Error(`Invalid MEMORY_MEM0_PROJECT_ID: ${raw}`);
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
function timeoutSignal(timeoutMs) {
|
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return null;
|
|
const controller = new AbortController();
|
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
return {
|
|
signal: controller.signal,
|
|
clear() {
|
|
clearTimeout(timeout);
|
|
},
|
|
};
|
|
}
|
|
|
|
function joinUrl(baseUrl, path) {
|
|
return `${String(baseUrl).replace(/\/$/, '')}/${String(path).replace(/^\//, '')}`;
|
|
}
|
|
|
|
export function createMem0HttpClient({
|
|
apiKey,
|
|
projectId = null,
|
|
baseUrl = 'https://api.mem0.ai',
|
|
writePath = '/v1/memories',
|
|
compactPath = '/v1/memories/compact',
|
|
modelProviderKeyId = null,
|
|
model = null,
|
|
modelApiType = null,
|
|
fetchImpl = globalThis.fetch,
|
|
timeoutMs = 3000,
|
|
} = {}) {
|
|
const resolvedApiKey = String(apiKey ?? '').trim();
|
|
if (!resolvedApiKey) throw new Error('createMem0HttpClient requires MEMORY_MEM0_API_KEY');
|
|
if (typeof fetchImpl !== 'function') throw new Error('createMem0HttpClient requires fetch');
|
|
const resolvedProjectId = normalizeProjectId(projectId);
|
|
|
|
async function postJson(path, body) {
|
|
const timeout = timeoutSignal(Number(timeoutMs));
|
|
try {
|
|
const response = await fetchImpl(joinUrl(baseUrl, path), {
|
|
method: 'POST',
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
authorization: `Bearer ${resolvedApiKey}`,
|
|
},
|
|
body: JSON.stringify(body),
|
|
...(timeout?.signal ? { signal: timeout.signal } : {}),
|
|
});
|
|
if (!response?.ok) {
|
|
throw new Error(`Mem0 request failed: ${response?.status ?? 'unknown'}`);
|
|
}
|
|
return response.json?.() ?? {};
|
|
} finally {
|
|
timeout?.clear();
|
|
}
|
|
}
|
|
|
|
return {
|
|
async write(input = {}) {
|
|
const data = await postJson(writePath, {
|
|
user_id: input.userId,
|
|
session_id: input.sessionId,
|
|
messages: Array.isArray(input.messages) ? input.messages : [],
|
|
project_id: resolvedProjectId,
|
|
model_provider_key_id: String(modelProviderKeyId ?? '').trim() || null,
|
|
model: String(model ?? '').trim() || null,
|
|
model_api_type: String(modelApiType ?? '').trim() || null,
|
|
});
|
|
return {
|
|
saved: Number(data?.saved ?? data?.count ?? 1),
|
|
analyzed: Number(data?.analyzed ?? 1),
|
|
memories: Number(data?.memories ?? data?.memory_count ?? data?.count ?? 0),
|
|
};
|
|
},
|
|
async compact(input = {}) {
|
|
const data = await postJson(compactPath, {
|
|
user_id: input.userId,
|
|
project_id: resolvedProjectId,
|
|
model_provider_key_id: String(modelProviderKeyId ?? '').trim() || null,
|
|
model: String(model ?? '').trim() || null,
|
|
model_api_type: String(modelApiType ?? '').trim() || null,
|
|
});
|
|
return {
|
|
analyzed: Number(data?.analyzed ?? 1),
|
|
memories: Number(data?.memories ?? data?.memory_count ?? data?.count ?? 0),
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
export function createMem0MemoryBackend({
|
|
enabled = false,
|
|
apiKey = null,
|
|
projectId = null,
|
|
client = null,
|
|
unavailableReason = null,
|
|
} = {}) {
|
|
const hasApiKey = Boolean(String(apiKey ?? '').trim());
|
|
const resolvedProjectId = normalizeProjectId(projectId);
|
|
const configured = Boolean(enabled && hasApiKey);
|
|
const hasClient = Boolean(client?.write || client?.compact);
|
|
const wired = Boolean(configured && hasClient);
|
|
const reason = unavailableReason
|
|
?? (enabled
|
|
? (!hasApiKey ? 'api_key_not_configured' : 'client_not_configured')
|
|
: 'not_configured');
|
|
|
|
return {
|
|
name: 'mem0',
|
|
category: 'extraction',
|
|
role: 'automatic-memory-generation',
|
|
flag: 'MEMORY_MEM0_ENABLED',
|
|
unavailableReason: reason,
|
|
projectId: resolvedProjectId,
|
|
|
|
isAvailable() {
|
|
return Boolean(wired);
|
|
},
|
|
|
|
getUnavailableReason() {
|
|
return this.isAvailable() ? null : reason;
|
|
},
|
|
|
|
async write(input = {}) {
|
|
if (!this.isAvailable() || !client?.write) return { saved: 0, analyzed: 0, memories: 0 };
|
|
const result = await client.write({ ...input, projectId: resolvedProjectId });
|
|
return {
|
|
saved: Number(result?.saved ?? 0),
|
|
analyzed: Number(result?.analyzed ?? 0),
|
|
memories: Number(result?.memories ?? 0),
|
|
};
|
|
},
|
|
|
|
async compact(input = {}) {
|
|
if (!this.isAvailable() || !client?.compact) return { analyzed: 0, memories: 0 };
|
|
const result = await client.compact({ ...input, projectId: resolvedProjectId });
|
|
return {
|
|
analyzed: Number(result?.analyzed ?? 0),
|
|
memories: Number(result?.memories ?? 0),
|
|
};
|
|
},
|
|
};
|
|
}
|