feat(vercel-ai-sdk): migrate to Vercel AI SDK v6 (#4741)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
This commit is contained in:
co-authored by
kartik-mem0
parent
b819d95d18
commit
b4a50e3dc8
@@ -12,7 +12,7 @@ export class Mem0 {
|
||||
provider: 'openai',
|
||||
}) {
|
||||
this.baseURL =
|
||||
withoutTrailingSlash(options.baseURL) ?? 'http://127.0.0.1:11434/api'
|
||||
withoutTrailingSlash(options.baseURL) ?? 'https://api.openai.com'
|
||||
|
||||
this.headers = options.headers
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* eslint-disable camelcase */
|
||||
import {
|
||||
LanguageModelV2CallOptions,
|
||||
LanguageModelV2Message,
|
||||
LanguageModelV2Source
|
||||
LanguageModelV3,
|
||||
LanguageModelV3CallOptions,
|
||||
LanguageModelV3GenerateResult,
|
||||
LanguageModelV3Message,
|
||||
LanguageModelV3Source,
|
||||
LanguageModelV3StreamResult,
|
||||
} from '@ai-sdk/provider';
|
||||
|
||||
import { LanguageModelV2 } from '@ai-sdk/provider';
|
||||
// streaming uses provider-native doStream; no middleware needed
|
||||
|
||||
import { Mem0ChatConfig, Mem0ChatModelId, Mem0ChatSettings, Mem0ConfigSettings, Mem0StreamResponse } from "./mem0-types";
|
||||
import { Mem0ChatConfig, Mem0ChatModelId, Mem0ChatSettings, Mem0ConfigSettings } from "./mem0-types";
|
||||
import { Mem0ClassSelector } from "./mem0-provider-selector";
|
||||
import { Mem0ProviderSettings } from "./mem0-provider";
|
||||
import { addMemories, getMemories } from "./mem0-utils";
|
||||
@@ -17,12 +17,8 @@ const generateRandomId = () => {
|
||||
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
||||
}
|
||||
|
||||
export class Mem0GenericLanguageModel implements LanguageModelV2 {
|
||||
readonly specificationVersion = "v2";
|
||||
readonly defaultObjectGenerationMode = "json";
|
||||
// We don't support images for now
|
||||
readonly supportsImageUrls = false;
|
||||
// Allow All Media Types for now
|
||||
export class Mem0GenericLanguageModel implements LanguageModelV3 {
|
||||
readonly specificationVersion = "v3";
|
||||
readonly supportedUrls: Record<string, RegExp[]> = {
|
||||
'*': [/.*/]
|
||||
};
|
||||
@@ -38,148 +34,108 @@ export class Mem0GenericLanguageModel implements LanguageModelV2 {
|
||||
|
||||
provider: string;
|
||||
|
||||
private async processMemories(messagesPrompts: LanguageModelV2Message[], mem0Config: Mem0ConfigSettings) {
|
||||
private async processMemories(messagesPrompts: LanguageModelV3Message[], mem0Config: Mem0ConfigSettings) {
|
||||
try {
|
||||
// Add New Memories
|
||||
addMemories(messagesPrompts, mem0Config).then((res) => {
|
||||
return res;
|
||||
}).catch((e) => {
|
||||
console.error("Error while adding memories");
|
||||
return { memories: [], messagesPrompts: [] };
|
||||
// Add new memories — await to ensure writes complete before returning
|
||||
try {
|
||||
await addMemories(messagesPrompts, mem0Config);
|
||||
} catch (e) {
|
||||
console.error("Error while adding memories");
|
||||
}
|
||||
|
||||
// Get memories (always returns an array since graph support is removed)
|
||||
const memories: any[] = await getMemories(messagesPrompts, mem0Config) ?? [];
|
||||
|
||||
const mySystemPrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n";
|
||||
|
||||
let memoriesText = "";
|
||||
try {
|
||||
memoriesText = memories
|
||||
?.map((memory: any) => `Memory: ${memory?.memory}\n\n`)
|
||||
.join("\n\n");
|
||||
} catch (e) {
|
||||
console.error("Error while parsing memories");
|
||||
}
|
||||
|
||||
const memoriesPrompt = `System Message: ${mySystemPrompt} ${memoriesText} `;
|
||||
|
||||
// Clone the prompt array to avoid mutating the caller's reference on retries
|
||||
const updatedPrompts = [...messagesPrompts];
|
||||
|
||||
if (memories?.length > 0) {
|
||||
const systemPrompt: LanguageModelV3Message = {
|
||||
role: "system",
|
||||
content: memoriesPrompt
|
||||
};
|
||||
updatedPrompts.unshift(systemPrompt);
|
||||
}
|
||||
|
||||
return { memories, messagesPrompts: updatedPrompts };
|
||||
} catch (e) {
|
||||
console.error("Error while processing memories");
|
||||
return { memories: [], messagesPrompts: [...messagesPrompts] };
|
||||
}
|
||||
}
|
||||
|
||||
async doGenerate(options: LanguageModelV3CallOptions): Promise<LanguageModelV3GenerateResult> {
|
||||
const provider = this.config.provider;
|
||||
const mem0_api_key = this.config.mem0ApiKey;
|
||||
|
||||
const settings: Mem0ProviderSettings = {
|
||||
provider: provider,
|
||||
mem0ApiKey: mem0_api_key,
|
||||
apiKey: this.config.apiKey,
|
||||
modelType: this.config.modelType,
|
||||
}
|
||||
|
||||
const mem0Config: Mem0ConfigSettings = {
|
||||
mem0ApiKey: mem0_api_key,
|
||||
...this.config.mem0Config,
|
||||
...this.settings,
|
||||
}
|
||||
|
||||
const selector = new Mem0ClassSelector(this.modelId, settings, this.provider_config);
|
||||
|
||||
const { memories, messagesPrompts: updatedPrompts } = await this.processMemories(options.prompt, mem0Config);
|
||||
|
||||
const model = selector.createProvider();
|
||||
|
||||
const ans = await model.doGenerate({
|
||||
...options,
|
||||
prompt: updatedPrompts,
|
||||
});
|
||||
|
||||
// Get Memories
|
||||
let memories = await getMemories(messagesPrompts, mem0Config);
|
||||
|
||||
const mySystemPrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n";
|
||||
|
||||
const isGraphEnabled = mem0Config?.enable_graph;
|
||||
|
||||
let memoriesText = "";
|
||||
let memoriesText2 = "";
|
||||
try {
|
||||
// @ts-ignore
|
||||
if (isGraphEnabled) {
|
||||
memoriesText = memories?.results?.map((memory: any) => {
|
||||
return `Memory: ${memory?.memory}\n\n`;
|
||||
}).join("\n\n");
|
||||
|
||||
memoriesText2 = memories?.relations?.map((memory: any) => {
|
||||
return `Relation: ${memory?.source} -> ${memory?.relationship} -> ${memory?.target} \n\n`;
|
||||
}).join("\n\n");
|
||||
} else {
|
||||
memoriesText = memories?.map((memory: any) => {
|
||||
return `Memory: ${memory?.memory}\n\n`;
|
||||
}).join("\n\n");
|
||||
}
|
||||
} catch(e) {
|
||||
console.error("Error while parsing memories");
|
||||
if (!memories || memories?.length === 0) {
|
||||
return ans;
|
||||
}
|
||||
|
||||
let graphPrompt = "";
|
||||
if (isGraphEnabled) {
|
||||
graphPrompt = `HERE ARE THE GRAPHS RELATIONS FOR THE PREFERENCES OF THE USER:\n\n ${memoriesText2}`;
|
||||
}
|
||||
|
||||
const memoriesPrompt = `System Message: ${mySystemPrompt} ${memoriesText} ${graphPrompt} `;
|
||||
|
||||
// System Prompt - The memories go as a system prompt
|
||||
const systemPrompt: LanguageModelV2Message = {
|
||||
role: "system",
|
||||
content: memoriesPrompt
|
||||
const mem0Source: LanguageModelV3Source = {
|
||||
type: "source",
|
||||
sourceType: "url",
|
||||
id: "mem0-" + generateRandomId(),
|
||||
url: "https://app.mem0.ai?utm_source=oss&utm_medium=vercel-ai-sdk-src",
|
||||
title: "Mem0 Memories",
|
||||
providerMetadata: {
|
||||
mem0: {
|
||||
memories: memories,
|
||||
memoriesText: memories
|
||||
?.map((memory: any) => memory?.memory)
|
||||
.join("\n\n"),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Add the system prompt to the beginning of the messages if there are memories
|
||||
if (memories?.length > 0) {
|
||||
messagesPrompts.unshift(systemPrompt);
|
||||
}
|
||||
|
||||
if (isGraphEnabled) {
|
||||
memories = memories?.results;
|
||||
}
|
||||
|
||||
return { memories, messagesPrompts };
|
||||
} catch(e) {
|
||||
console.error("Error while processing memories");
|
||||
return { memories: [], messagesPrompts };
|
||||
}
|
||||
return {
|
||||
...ans,
|
||||
content: [...(ans.content ?? []), mem0Source],
|
||||
};
|
||||
}
|
||||
|
||||
async doGenerate(options: LanguageModelV2CallOptions): Promise<Awaited<ReturnType<LanguageModelV2['doGenerate']>>> {
|
||||
try {
|
||||
const provider = this.config.provider;
|
||||
const mem0_api_key = this.config.mem0ApiKey;
|
||||
|
||||
const settings: Mem0ProviderSettings = {
|
||||
provider: provider,
|
||||
mem0ApiKey: mem0_api_key,
|
||||
apiKey: this.config.apiKey,
|
||||
}
|
||||
|
||||
const mem0Config: Mem0ConfigSettings = {
|
||||
mem0ApiKey: mem0_api_key,
|
||||
...this.config.mem0Config,
|
||||
...this.settings,
|
||||
}
|
||||
|
||||
const selector = new Mem0ClassSelector(this.modelId, settings, this.provider_config);
|
||||
|
||||
let messagesPrompts = options.prompt;
|
||||
|
||||
// Process memories and update prompts
|
||||
const { memories, messagesPrompts: updatedPrompts } = await this.processMemories(messagesPrompts, mem0Config);
|
||||
|
||||
const model = selector.createProvider();
|
||||
|
||||
const ans = await model.doGenerate({
|
||||
...options,
|
||||
prompt: updatedPrompts,
|
||||
});
|
||||
|
||||
// If there are no memories, return the original response
|
||||
if (!memories || memories?.length === 0) {
|
||||
return ans;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create sources array with existing sources
|
||||
const sources: LanguageModelV2Source[] = [
|
||||
{
|
||||
type: "source",
|
||||
title: "Mem0 Memories",
|
||||
sourceType: "url",
|
||||
id: "mem0-" + generateRandomId(),
|
||||
url: "https://app.mem0.ai?utm_source=oss&utm_medium=vercel-ai-sdk-src",
|
||||
providerMetadata: {
|
||||
mem0: {
|
||||
memories: memories,
|
||||
memoriesText: memories
|
||||
?.map((memory: any) => memory?.memory)
|
||||
.join("\n\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
} catch (e) {
|
||||
console.error("Error while creating sources");
|
||||
}
|
||||
|
||||
return {
|
||||
...ans,
|
||||
// sources
|
||||
};
|
||||
} catch (error) {
|
||||
// Handle errors properly
|
||||
console.error("Error in doGenerate:", error);
|
||||
throw new Error("Failed to generate response.");
|
||||
}
|
||||
}
|
||||
|
||||
async doStream(options: LanguageModelV2CallOptions): Promise<Awaited<ReturnType<LanguageModelV2['doStream']>>> {
|
||||
async doStream(options: LanguageModelV3CallOptions): Promise<LanguageModelV3StreamResult> {
|
||||
try {
|
||||
const provider = this.config.provider;
|
||||
const mem0_api_key = this.config.mem0ApiKey;
|
||||
|
||||
|
||||
const settings: Mem0ProviderSettings = {
|
||||
provider: provider,
|
||||
mem0ApiKey: mem0_api_key,
|
||||
@@ -194,31 +150,18 @@ export class Mem0GenericLanguageModel implements LanguageModelV2 {
|
||||
}
|
||||
|
||||
const selector = new Mem0ClassSelector(this.modelId, settings, this.provider_config);
|
||||
|
||||
let messagesPrompts = options.prompt;
|
||||
|
||||
// Process memories and update prompts
|
||||
const { memories, messagesPrompts: updatedPrompts } = await this.processMemories(messagesPrompts, mem0Config);
|
||||
|
||||
const { messagesPrompts: updatedPrompts } = await this.processMemories(options.prompt, mem0Config);
|
||||
|
||||
const baseModel = selector.createProvider();
|
||||
|
||||
// Use the provider's native streaming directly to avoid buffering
|
||||
const streamResponse = await baseModel.doStream({
|
||||
...options,
|
||||
prompt: updatedPrompts,
|
||||
});
|
||||
|
||||
// If there are no memories, return the original stream
|
||||
if (!memories || memories?.length === 0) {
|
||||
return streamResponse;
|
||||
}
|
||||
|
||||
// Return stream untouched for true streaming behavior
|
||||
return {
|
||||
stream: streamResponse.stream,
|
||||
request: streamResponse.request,
|
||||
response: streamResponse.response,
|
||||
};
|
||||
// Return the full stream response, preserving all V3 fields (warnings, etc.)
|
||||
return streamResponse;
|
||||
} catch (error) {
|
||||
console.error("Error in doStream:", error);
|
||||
throw new Error("Streaming failed or method not implemented.");
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Mem0ProviderSettings } from "./mem0-provider";
|
||||
import Mem0AITextGenerator, { ProviderSettings } from "./provider-response-provider";
|
||||
import { LanguageModelV2 } from '@ai-sdk/provider';
|
||||
import { LanguageModelV3 } from '@ai-sdk/provider';
|
||||
|
||||
class Mem0ClassSelector {
|
||||
modelId: string;
|
||||
provider_wrapper: string;
|
||||
config: Mem0ProviderSettings;
|
||||
provider_config?: ProviderSettings;
|
||||
static supportedProviders = ["openai", "anthropic", "cohere", "groq", "google"];
|
||||
static supportedProviders = ["openai", "anthropic", "cohere", "groq", "google", "gemini"];
|
||||
|
||||
constructor(modelId: string, config: Mem0ProviderSettings, provider_config?: ProviderSettings) {
|
||||
this.modelId = modelId;
|
||||
@@ -24,7 +24,7 @@ class Mem0ClassSelector {
|
||||
}
|
||||
}
|
||||
|
||||
createProvider(): LanguageModelV2 {
|
||||
createProvider(): LanguageModelV3 {
|
||||
return new Mem0AITextGenerator(this.modelId, this.config , this.provider_config || {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { ProviderV2 } from '@ai-sdk/provider';
|
||||
import { LanguageModelV2 } from '@ai-sdk/provider';
|
||||
import { ProviderV3 } from '@ai-sdk/provider';
|
||||
import { LanguageModelV3 } from '@ai-sdk/provider';
|
||||
import { withoutTrailingSlash } from "@ai-sdk/provider-utils";
|
||||
import { Mem0ChatModelId, Mem0ChatSettings, Mem0Config } from "./mem0-types";
|
||||
import { Mem0GenericLanguageModel } from "./mem0-generic-language-model";
|
||||
import { LLMProviderSettings } from "./mem0-types";
|
||||
|
||||
export interface Mem0Provider extends ProviderV2 {
|
||||
(modelId: Mem0ChatModelId, settings?: Mem0ChatSettings): LanguageModelV2;
|
||||
export interface Mem0Provider extends ProviderV3 {
|
||||
(modelId: Mem0ChatModelId, settings?: Mem0ChatSettings): LanguageModelV3;
|
||||
|
||||
chat(modelId: Mem0ChatModelId, settings?: Mem0ChatSettings): LanguageModelV2;
|
||||
completion(modelId: Mem0ChatModelId, settings?: Mem0ChatSettings): LanguageModelV2;
|
||||
chat(modelId: Mem0ChatModelId, settings?: Mem0ChatSettings): LanguageModelV3;
|
||||
completion(modelId: Mem0ChatModelId, settings?: Mem0ChatSettings): LanguageModelV3;
|
||||
|
||||
languageModel(
|
||||
modelId: Mem0ChatModelId,
|
||||
settings?: Mem0ChatSettings
|
||||
): LanguageModelV2;
|
||||
): LanguageModelV3;
|
||||
}
|
||||
|
||||
export interface Mem0ProviderSettings {
|
||||
@@ -31,7 +31,7 @@ export interface Mem0ProviderSettings {
|
||||
/**
|
||||
* Custom headers to include in the requests.
|
||||
*/
|
||||
headers?: Record<string, string>;
|
||||
headers?: Record<string, string | undefined>;
|
||||
name?: string;
|
||||
mem0ApiKey?: string;
|
||||
apiKey?: string;
|
||||
@@ -51,7 +51,7 @@ export function createMem0(
|
||||
}
|
||||
): Mem0Provider {
|
||||
const baseURL =
|
||||
withoutTrailingSlash(options.baseURL) ?? "http://api.openai.com";
|
||||
withoutTrailingSlash(options.baseURL) ?? "https://api.openai.com";
|
||||
const getHeaders = () => ({
|
||||
...options.headers,
|
||||
});
|
||||
@@ -113,7 +113,7 @@ export function createMem0(
|
||||
mem0ApiKey: options.mem0ApiKey,
|
||||
apiKey: options.apiKey,
|
||||
mem0Config: options.mem0Config,
|
||||
modelType: "completion",
|
||||
modelType: "chat",
|
||||
},
|
||||
options.config
|
||||
);
|
||||
@@ -131,6 +131,7 @@ export function createMem0(
|
||||
return createGenericModel(modelId, settings);
|
||||
};
|
||||
|
||||
provider.specificationVersion = 'v3';
|
||||
provider.languageModel = createGenericModel;
|
||||
provider.completion = createCompletionModel;
|
||||
provider.chat = createChatModel;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Mem0ProviderSettings } from "./mem0-provider";
|
||||
import { OpenAIProviderSettings } from "@ai-sdk/openai";
|
||||
import { AnthropicProviderSettings } from "@ai-sdk/anthropic";
|
||||
import { LanguageModelV2 } from '@ai-sdk/provider';
|
||||
import { CohereProviderSettings } from "@ai-sdk/cohere";
|
||||
import { GroqProviderSettings } from "@ai-sdk/groq";
|
||||
import { GoogleGenerativeAIProviderSettings } from "@ai-sdk/google";
|
||||
export type Mem0ChatModelId =
|
||||
| (string & NonNullable<unknown>);
|
||||
|
||||
@@ -12,10 +12,6 @@ export interface Mem0ConfigSettings {
|
||||
app_id?: string;
|
||||
agent_id?: string;
|
||||
run_id?: string;
|
||||
org_name?: string;
|
||||
project_name?: string;
|
||||
org_id?: string;
|
||||
project_id?: string;
|
||||
metadata?: Record<string, any>;
|
||||
filters?: Record<string, any>;
|
||||
infer?: boolean;
|
||||
@@ -25,20 +21,12 @@ export interface Mem0ConfigSettings {
|
||||
top_k?: number;
|
||||
threshold?: number;
|
||||
rerank?: boolean;
|
||||
enable_graph?: boolean;
|
||||
host?: string;
|
||||
output_format?: string;
|
||||
filter_memories?: boolean;
|
||||
async_mode?: boolean;
|
||||
}
|
||||
|
||||
export interface Mem0ChatConfig extends Mem0ConfigSettings, Mem0ProviderSettings {}
|
||||
|
||||
export interface LLMProviderSettings extends OpenAIProviderSettings, AnthropicProviderSettings, CohereProviderSettings, GroqProviderSettings {}
|
||||
export type LLMProviderSettings = OpenAIProviderSettings | AnthropicProviderSettings | CohereProviderSettings | GroqProviderSettings | GoogleGenerativeAIProviderSettings;
|
||||
|
||||
export interface Mem0Config extends Mem0ConfigSettings {}
|
||||
export interface Mem0ChatSettings extends Mem0ConfigSettings {}
|
||||
|
||||
export interface Mem0StreamResponse extends Awaited<ReturnType<LanguageModelV2['doStream']>> {
|
||||
memories: any;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LanguageModelV2Prompt } from '@ai-sdk/provider';
|
||||
import { LanguageModelV3Prompt } from '@ai-sdk/provider';
|
||||
import { Mem0ConfigSettings } from './mem0-types';
|
||||
import { loadApiKey } from '@ai-sdk/provider-utils';
|
||||
interface MultimodalContent {
|
||||
@@ -26,7 +26,7 @@ interface Message {
|
||||
content: string | MultimodalContent | Array<MultimodalContent>;
|
||||
}
|
||||
|
||||
const flattenPrompt = (prompt: LanguageModelV2Prompt) => {
|
||||
const flattenPrompt = (prompt: LanguageModelV3Prompt) => {
|
||||
try {
|
||||
return prompt.map((part) => {
|
||||
if (part.role === "user") {
|
||||
@@ -42,7 +42,6 @@ const flattenPrompt = (prompt: LanguageModelV2Prompt) => {
|
||||
if (content.type === 'text' && content.text) {
|
||||
return content.text;
|
||||
} else if (content.type === 'file') {
|
||||
// For file content, we'll include a descriptive placeholder
|
||||
if (content.mediaType === 'application/pdf') {
|
||||
return '[PDF document]';
|
||||
} else if (content.mediaType === 'text/markdown' || content.mediaType === 'application/mdx') {
|
||||
@@ -54,8 +53,6 @@ const flattenPrompt = (prompt: LanguageModelV2Prompt) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
// For non-text content (images, pdfs, mdx), we'll include a placeholder
|
||||
// This helps maintain context for memory search while not breaking the text flow
|
||||
return "[multimodal content]";
|
||||
}
|
||||
return "";
|
||||
@@ -66,7 +63,7 @@ const flattenPrompt = (prompt: LanguageModelV2Prompt) => {
|
||||
}
|
||||
}
|
||||
|
||||
const convertToMem0Format = (messages: LanguageModelV2Prompt) => {
|
||||
const convertToMem0Format = (messages: LanguageModelV3Prompt) => {
|
||||
try {
|
||||
return messages.flatMap((message: any) => {
|
||||
try {
|
||||
@@ -85,7 +82,6 @@ const convertToMem0Format = (messages: LanguageModelV2Prompt) => {
|
||||
content: obj.text,
|
||||
};
|
||||
} else if (obj.type === "file") {
|
||||
// Handle LanguageModelV2Prompt file format
|
||||
if (obj.mediaType === "application/pdf") {
|
||||
return {
|
||||
role: message.role,
|
||||
@@ -155,7 +151,6 @@ const convertToMem0Format = (messages: LanguageModelV2Prompt) => {
|
||||
}
|
||||
}).filter((item: null) => item !== null);
|
||||
} else {
|
||||
// Handle single multimodal content object
|
||||
const obj = message.content;
|
||||
if (obj.type === "text") {
|
||||
return {
|
||||
@@ -163,7 +158,6 @@ const convertToMem0Format = (messages: LanguageModelV2Prompt) => {
|
||||
content: obj.text,
|
||||
};
|
||||
} else if (obj.type === "file") {
|
||||
// Handle LanguageModelV2Prompt file format
|
||||
if (obj.mediaType === "application/pdf") {
|
||||
return {
|
||||
role: message.role,
|
||||
@@ -239,63 +233,57 @@ const convertToMem0Format = (messages: LanguageModelV2Prompt) => {
|
||||
}
|
||||
}
|
||||
|
||||
const searchInternalMemories = async (query: string, config?: Mem0ConfigSettings, top_k: number = 5) => {
|
||||
const searchInternalMemories = async (query: string, config?: Mem0ConfigSettings, top_k: number = 10) => {
|
||||
try {
|
||||
const filters: { OR: Array<{ [key: string]: string | undefined }> } = {
|
||||
OR: [],
|
||||
// v3: entity IDs go inside the filters object, not as top-level params
|
||||
const filters: Record<string, any> = {
|
||||
...(config?.filters ?? {}),
|
||||
};
|
||||
if (config?.user_id) {
|
||||
filters.OR.push({
|
||||
user_id: config.user_id,
|
||||
});
|
||||
filters.user_id = config.user_id;
|
||||
}
|
||||
if (config?.app_id) {
|
||||
filters.OR.push({
|
||||
app_id: config.app_id,
|
||||
});
|
||||
filters.app_id = config.app_id;
|
||||
}
|
||||
if (config?.agent_id) {
|
||||
filters.OR.push({
|
||||
agent_id: config.agent_id,
|
||||
});
|
||||
filters.agent_id = config.agent_id;
|
||||
}
|
||||
if (config?.run_id) {
|
||||
filters.OR.push({
|
||||
run_id: config.run_id,
|
||||
});
|
||||
}
|
||||
const org_project_filters = {
|
||||
org_id: config&&config.org_id,
|
||||
project_id: config&&config.project_id,
|
||||
org_name: !config?.org_id ? config&&config.org_name : undefined,
|
||||
project_name: !config?.org_id ? config&&config.project_name : undefined,
|
||||
filters.run_id = config.run_id;
|
||||
}
|
||||
|
||||
const apiKey = loadApiKey({
|
||||
apiKey: (config&&config.mem0ApiKey),
|
||||
apiKey: config?.mem0ApiKey,
|
||||
environmentVariableName: "MEM0_API_KEY",
|
||||
description: "Mem0",
|
||||
});
|
||||
|
||||
const body: Record<string, any> = {
|
||||
query,
|
||||
filters,
|
||||
top_k: config?.top_k ?? top_k,
|
||||
};
|
||||
if (config?.threshold != null) {
|
||||
body.threshold = config.threshold;
|
||||
}
|
||||
if (config?.rerank != null) {
|
||||
body.rerank = config.rerank;
|
||||
}
|
||||
if (config?.metadata) {
|
||||
body.metadata = config.metadata;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Token ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
filters,
|
||||
...config,
|
||||
top_k: config&&config.top_k || top_k,
|
||||
version: "v2",
|
||||
output_format: "v1.1",
|
||||
...org_project_filters
|
||||
}),
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
const baseUrl = config?.host || 'https://api.mem0.ai';
|
||||
const response = await fetch(`${baseUrl}/v2/memories/search/`, options);
|
||||
const response = await fetch(`${baseUrl}/v3/memories/search/`, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
@@ -307,7 +295,7 @@ const searchInternalMemories = async (query: string, config?: Mem0ConfigSettings
|
||||
}
|
||||
}
|
||||
|
||||
const addMemories = async (messages: LanguageModelV2Prompt, config?: Mem0ConfigSettings) => {
|
||||
const addMemories = async (messages: LanguageModelV3Prompt, config?: Mem0ConfigSettings) => {
|
||||
try {
|
||||
let finalMessages: Array<Message> = [];
|
||||
if (typeof messages === "string") {
|
||||
@@ -326,22 +314,30 @@ const addMemories = async (messages: LanguageModelV2Prompt, config?: Mem0ConfigS
|
||||
const updateMemories = async (messages: Array<Message>, config?: Mem0ConfigSettings) => {
|
||||
try {
|
||||
const apiKey = loadApiKey({
|
||||
apiKey: (config&&config.mem0ApiKey),
|
||||
apiKey: config?.mem0ApiKey,
|
||||
environmentVariableName: "MEM0_API_KEY",
|
||||
description: "Mem0",
|
||||
});
|
||||
|
||||
const body: Record<string, any> = { messages };
|
||||
if (config?.user_id) body.user_id = config.user_id;
|
||||
if (config?.app_id) body.app_id = config.app_id;
|
||||
if (config?.agent_id) body.agent_id = config.agent_id;
|
||||
if (config?.run_id) body.run_id = config.run_id;
|
||||
if (config?.metadata) body.metadata = config.metadata;
|
||||
if (config?.infer != null) body.infer = config.infer;
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Token ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({messages, ...config, version: "v2"}),
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
const baseUrl = config?.host || 'https://api.mem0.ai';
|
||||
const response = await fetch(`${baseUrl}/v1/memories/`, options);
|
||||
const response = await fetch(`${baseUrl}/v3/memories/add/`, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
@@ -353,58 +349,43 @@ const updateMemories = async (messages: Array<Message>, config?: Mem0ConfigSetti
|
||||
}
|
||||
}
|
||||
|
||||
const retrieveMemories = async (prompt: LanguageModelV2Prompt | string, config?: Mem0ConfigSettings) => {
|
||||
const retrieveMemories = async (prompt: LanguageModelV3Prompt | string, config?: Mem0ConfigSettings) => {
|
||||
try {
|
||||
const message = typeof prompt === 'string' ? prompt : flattenPrompt(prompt);
|
||||
const systemPrompt = "These are the memories I have stored. Give more weightage to the question by users and try to answer that first. You have to modify your answer based on the memories I have provided. If the memories are irrelevant you can ignore them. Also don't reply to this section of the prompt, or the memories, they are only for your reference. The System prompt starts after text System Message: \n\n";
|
||||
|
||||
const memories = await searchInternalMemories(message, config);
|
||||
let memoriesText1 = "";
|
||||
let memoriesText2 = "";
|
||||
let graphPrompt = "";
|
||||
|
||||
try {
|
||||
memoriesText1 = memories?.results?.map((memory: any) => {
|
||||
return `Memory: ${memory.memory}\n\n`;
|
||||
}).join("\n\n");
|
||||
const data = await searchInternalMemories(message, config);
|
||||
// The API response may be a flat array or an object with a results key
|
||||
const memories: any[] = Array.isArray(data) ? data : (data?.results ?? []);
|
||||
|
||||
if (config?.enable_graph) {
|
||||
memoriesText2 = memories?.relations?.map((memory: any) => {
|
||||
return `Relation: ${memory.source} -> ${memory.relationship} -> ${memory.target} \n\n`;
|
||||
}).join("\n\n");
|
||||
graphPrompt = `HERE ARE THE GRAPHS RELATIONS FOR THE PREFERENCES OF THE USER:\n\n ${memoriesText2}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error while parsing memories:", error);
|
||||
}
|
||||
|
||||
if (!memories || memories?.length === 0) {
|
||||
if (memories.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `System Message: ${systemPrompt} ${memoriesText1} ${graphPrompt}`;
|
||||
const memoriesText = memories
|
||||
.map((memory: any) => `Memory: ${memory.memory}\n\n`)
|
||||
.join("\n\n");
|
||||
|
||||
return `System Message: ${systemPrompt} ${memoriesText}`;
|
||||
} catch (error) {
|
||||
console.error("Error in retrieveMemories:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const getMemories = async (prompt: LanguageModelV2Prompt | string, config?: Mem0ConfigSettings) => {
|
||||
const getMemories = async (prompt: LanguageModelV3Prompt | string, config?: Mem0ConfigSettings) => {
|
||||
try {
|
||||
const message = typeof prompt === 'string' ? prompt : flattenPrompt(prompt);
|
||||
const memories = await searchInternalMemories(message, config);
|
||||
|
||||
if (!config?.enable_graph) {
|
||||
return memories?.results;
|
||||
}
|
||||
return memories;
|
||||
const data = await searchInternalMemories(message, config);
|
||||
// Normalize: always return a flat array regardless of API response shape
|
||||
return Array.isArray(data) ? data : (data?.results ?? []);
|
||||
} catch (error) {
|
||||
console.error("Error in getMemories:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const searchMemories = async (prompt: LanguageModelV2Prompt | string, config?: Mem0ConfigSettings) => {
|
||||
const searchMemories = async (prompt: LanguageModelV3Prompt | string, config?: Mem0ConfigSettings) => {
|
||||
try {
|
||||
const message = typeof prompt === 'string' ? prompt : flattenPrompt(prompt);
|
||||
const memories = await searchInternalMemories(message, config);
|
||||
@@ -415,4 +396,4 @@ const searchMemories = async (prompt: LanguageModelV2Prompt | string, config?: M
|
||||
}
|
||||
}
|
||||
|
||||
export {addMemories, updateMemories, retrieveMemories, flattenPrompt, searchMemories, getMemories};
|
||||
export {addMemories, updateMemories, retrieveMemories, flattenPrompt, searchMemories, getMemories};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LanguageModelV2, LanguageModelV2CallOptions } from "@ai-sdk/provider";
|
||||
import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3GenerateResult, LanguageModelV3StreamResult } from "@ai-sdk/provider";
|
||||
import { Mem0ProviderSettings } from "./mem0-provider";
|
||||
import { createOpenAI, OpenAIProviderSettings } from "@ai-sdk/openai";
|
||||
import { CohereProviderSettings, createCohere } from "@ai-sdk/cohere";
|
||||
@@ -7,10 +7,8 @@ import { createGoogleGenerativeAI, GoogleGenerativeAIProviderSettings } from "@a
|
||||
import { createGroq, GroqProviderSettings } from "@ai-sdk/groq";
|
||||
|
||||
// Define a private provider field
|
||||
class Mem0AITextGenerator implements LanguageModelV2 {
|
||||
readonly specificationVersion = "v2";
|
||||
readonly defaultObjectGenerationMode = "json";
|
||||
readonly supportsImageUrls = false;
|
||||
class Mem0AITextGenerator implements LanguageModelV3 {
|
||||
readonly specificationVersion = "v3";
|
||||
readonly modelId: string;
|
||||
readonly provider = "mem0";
|
||||
readonly supportedUrls: Record<string, RegExp[]> = {
|
||||
@@ -59,11 +57,6 @@ class Mem0AITextGenerator implements LanguageModelV2 {
|
||||
})(modelId);
|
||||
break;
|
||||
case "google":
|
||||
this.languageModel = createGoogleGenerativeAI({
|
||||
apiKey: config?.apiKey,
|
||||
...provider_config as GoogleGenerativeAIProviderSettings,
|
||||
})(modelId);
|
||||
break;
|
||||
case "gemini":
|
||||
this.languageModel = createGoogleGenerativeAI({
|
||||
apiKey: config?.apiKey,
|
||||
@@ -74,15 +67,15 @@ class Mem0AITextGenerator implements LanguageModelV2 {
|
||||
throw new Error("Invalid provider");
|
||||
}
|
||||
}
|
||||
|
||||
async doGenerate(options: LanguageModelV2CallOptions): Promise<Awaited<ReturnType<LanguageModelV2['doGenerate']>>> {
|
||||
|
||||
async doGenerate(options: LanguageModelV3CallOptions): Promise<LanguageModelV3GenerateResult> {
|
||||
const result = await this.languageModel.doGenerate(options);
|
||||
return result as Awaited<ReturnType<LanguageModelV2['doGenerate']>>;
|
||||
return result as LanguageModelV3GenerateResult;
|
||||
}
|
||||
|
||||
async doStream(options: LanguageModelV2CallOptions): Promise<Awaited<ReturnType<LanguageModelV2['doStream']>>> {
|
||||
async doStream(options: LanguageModelV3CallOptions): Promise<LanguageModelV3StreamResult> {
|
||||
const result = await this.languageModel.doStream(options);
|
||||
return result as Awaited<ReturnType<LanguageModelV2['doStream']>>;
|
||||
return result as LanguageModelV3StreamResult;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user