Skip to content

Commit c698195

Browse files
alibaizhanovclaude
andcommitted
feat: add Mengram memory component
Add Mengram as a new memory integration component with three outputs: - Search Results: semantic search across all memory types - Cognitive Profile: system prompt from stored memories - Mengram Memory: ingest messages with automatic extraction Follows the same pattern as the existing Mem0 integration. Uses lazy import to handle graceful ImportError when mengram-ai is not installed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7640ce6 commit c698195

11 files changed

Lines changed: 212 additions & 1 deletion

File tree

src/backend/base/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ ragstack = ["ragstack-ai-knowledge-store==0.2.1"]
288288
opensearch = ["opensearch-py==2.8.0"]
289289
atlassian = ["atlassian-python-api==3.41.16"]
290290
mem0 = ["mem0ai>=0.1.34"]
291+
mengram = ["mengram-ai>=2.24.1"]
291292
needle = ["needle-python>=0.4.0"]
292293
sseclient = ["sseclient-py==1.8.0"]
293294
openinference = ["openinference-instrumentation-langchain>=0.1.29"]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
export default function SvgMengram(props) {
2+
return (
3+
<svg
4+
viewBox="0 0 120 120"
5+
preserveAspectRatio="xMidYMid meet"
6+
xmlns="http://www.w3.org/2000/svg"
7+
{...props}
8+
>
9+
<path
10+
d="M60 16 Q92 16 96 48 Q100 78 72 88 Q50 96 38 76 Q26 58 46 46 Q62 38 70 52 Q76 64 62 68"
11+
fill="none"
12+
stroke={props.isDark ? "#c084fc" : "#a855f7"}
13+
strokeWidth="10"
14+
strokeLinecap="round"
15+
/>
16+
<circle
17+
cx="62"
18+
cy="68"
19+
r="10"
20+
fill={props.isDark ? "#c084fc" : "#a855f7"}
21+
/>
22+
<circle
23+
cx="62"
24+
cy="68"
25+
r="4.5"
26+
fill={props.isDark ? "#1a1a2e" : "#ffffff"}
27+
/>
28+
</svg>
29+
);
30+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React, { forwardRef } from "react";
2+
import SvgMengram from "./SvgMengram";
3+
4+
export const MengramIcon = forwardRef<
5+
SVGSVGElement,
6+
React.PropsWithChildren<{}>
7+
>((props, ref) => {
8+
return <SvgMengram className="icon" ref={ref} {...props} />;
9+
});

src/frontend/src/icons/eagerIconImports.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import { LiteLLMIcon } from "@/icons/LiteLLM";
6262
import { LMStudioIcon } from "@/icons/LMStudio";
6363
import { MaritalkIcon } from "@/icons/Maritalk";
6464
import { Mem0 } from "@/icons/Mem0";
65+
import { MengramIcon } from "@/icons/Mengram";
6566
import { MetaIcon } from "@/icons/Meta";
6667
import { MidjourneyIcon } from "@/icons/Midjorney";
6768
import { MilvusIcon } from "@/icons/Milvus";
@@ -186,6 +187,7 @@ export const eagerIconsMapping = {
186187
LMStudio: LMStudioIcon,
187188
Maritalk: MaritalkIcon,
188189
Mem0: Mem0,
190+
Mengram: MengramIcon,
189191
Meta: MetaIcon,
190192
Midjourney: MidjourneyIcon,
191193
Milvus: MilvusIcon,

src/frontend/src/icons/lazyIconImports.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ export const lazyIconsMapping = {
333333
import("@/icons/Mem0Composio").then((mod) => ({
334334
default: mod.Mem0IconComposio,
335335
})),
336+
Mengram: () => import("@/icons/Mengram").then((mod) => ({ default: mod.MengramIcon })),
336337
Meta: () => import("@/icons/Meta").then((mod) => ({ default: mod.MetaIcon })),
337338
Midjourney: () =>
338339
import("@/icons/Midjorney").then((mod) => ({

src/frontend/src/utils/styleUtils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ export const SIDEBAR_BUNDLES = [
443443
{ display_name: "MariTalk", name: "maritalk", icon: "Maritalk" },
444444
{ display_name: "Mem0", name: "mem0", icon: "Mem0" },
445445
{ display_name: "Memories", name: "memories", icon: "Cpu" },
446+
{ display_name: "Mengram", name: "mengram", icon: "Mengram" },
446447
{ display_name: "Milvus", name: "milvus", icon: "Milvus" },
447448
{ display_name: "MistralAI", name: "mistral", icon: "MistralAI" },
448449
{ display_name: "MongoDB", name: "mongodb", icon: "MongoDB" },

src/lfx/src/lfx/components/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
logic,
6464
maritalk,
6565
mem0,
66+
mengram,
6667
milvus,
6768
mistral,
6869
models_and_agents,
@@ -167,6 +168,7 @@
167168
"logic": "__module__",
168169
"maritalk": "__module__",
169170
"mem0": "__module__",
171+
"mengram": "__module__",
170172
"milvus": "__module__",
171173
"mistral": "__module__",
172174
"models_and_agents": "__module__",
@@ -299,6 +301,7 @@ def _discover_components_from_module(module_name):
299301
"logic",
300302
"maritalk",
301303
"mem0",
304+
"mengram",
302305
"milvus",
303306
"mistral",
304307
"models_and_agents",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .mengram_memory import MengramMemoryComponent
2+
3+
__all__ = ["MengramMemoryComponent"]
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from lfx.base.memory.model import LCChatMemoryComponent
2+
from lfx.inputs.inputs import IntInput, MessageTextInput, SecretStrInput
3+
from lfx.io import Output
4+
from lfx.log.logger import logger
5+
from lfx.schema.data import Data
6+
7+
8+
class MengramMemoryComponent(LCChatMemoryComponent):
9+
display_name = "Mengram Memory"
10+
description = (
11+
"Store and retrieve memories using Mengram — AI memory with semantic, episodic, and procedural types. "
12+
"Automatically extracts facts, events, and workflows from conversations."
13+
)
14+
name = "mengram_memory"
15+
icon: str = "Mengram"
16+
17+
inputs = [
18+
SecretStrInput(
19+
name="mengram_api_key",
20+
display_name="Mengram API Key",
21+
info="API key for Mengram (starts with 'om-'). Get one at mengram.io.",
22+
),
23+
MessageTextInput(
24+
name="user_id",
25+
display_name="User ID",
26+
info="Identifier for the user associated with the memories.",
27+
),
28+
MessageTextInput(
29+
name="ingest_message",
30+
display_name="Message to Ingest",
31+
info="The message content to be ingested into Mengram memory.",
32+
),
33+
MessageTextInput(
34+
name="search_query",
35+
display_name="Search Query",
36+
info="Input text for searching related memories across all memory types.",
37+
),
38+
MessageTextInput(
39+
name="agent_id",
40+
display_name="Agent ID",
41+
info="Optional identifier for the agent sending the message.",
42+
advanced=True,
43+
),
44+
MessageTextInput(
45+
name="app_id",
46+
display_name="App ID",
47+
info="Optional identifier for the application.",
48+
advanced=True,
49+
),
50+
IntInput(
51+
name="top_k",
52+
display_name="Top K",
53+
info="Maximum number of results per memory type.",
54+
value=5,
55+
advanced=True,
56+
),
57+
MessageTextInput(
58+
name="api_url",
59+
display_name="API URL",
60+
info="Mengram API base URL.",
61+
value="https://mengram.io",
62+
advanced=True,
63+
),
64+
]
65+
66+
outputs = [
67+
Output(name="search_results", display_name="Search Results", method="search_memories"),
68+
Output(name="cognitive_profile", display_name="Cognitive Profile", method="get_cognitive_profile"),
69+
Output(name="memory", display_name="Mengram Memory", method="ingest_data"),
70+
]
71+
72+
def _build_client(self):
73+
"""Initialize a Mengram client instance."""
74+
try:
75+
from mengram import Mengram
76+
except ImportError as e:
77+
msg = "Mengram is not installed. Please install it with 'pip install mengram-ai'."
78+
raise ImportError(msg) from e
79+
return Mengram(api_key=self.mengram_api_key, base_url=self.api_url)
80+
81+
def ingest_data(self) -> Data:
82+
"""Ingest a message into Mengram memory and return the result."""
83+
client = self._build_client()
84+
85+
if not self.ingest_message or not self.user_id:
86+
logger.warning("Missing 'ingest_message' or 'user_id'; cannot ingest data.")
87+
return Data(data={"status": "skipped", "reason": "missing ingest_message or user_id"})
88+
89+
logger.info("Ingesting message for user_id: %s", self.user_id)
90+
91+
try:
92+
result = client.add(
93+
[{"role": "user", "content": self.ingest_message}],
94+
user_id=self.user_id,
95+
agent_id=self.agent_id or None,
96+
app_id=self.app_id or None,
97+
)
98+
except Exception:
99+
logger.exception("Failed to add message to Mengram memory.")
100+
raise
101+
102+
return Data(data={"status": "success", "result": result})
103+
104+
def search_memories(self) -> Data:
105+
"""Search Mengram memory for related memories across all memory types."""
106+
client = self._build_client()
107+
108+
if not self.search_query or not self.user_id:
109+
logger.warning("Missing 'search_query' or 'user_id'; cannot search.")
110+
return Data(data={})
111+
112+
logger.info("Searching memories for user_id: %s", self.user_id)
113+
114+
try:
115+
results = client.search_all(
116+
self.search_query,
117+
limit=self.top_k,
118+
user_id=self.user_id,
119+
)
120+
except Exception:
121+
logger.exception("Failed to search Mengram memory.")
122+
raise
123+
124+
return Data(data=results)
125+
126+
def get_cognitive_profile(self) -> Data:
127+
"""Get the Cognitive Profile — a system prompt generated from all memory types."""
128+
client = self._build_client()
129+
130+
if not self.user_id:
131+
logger.warning("Missing 'user_id'; cannot get cognitive profile.")
132+
return Data(data={"system_prompt": ""})
133+
134+
logger.info("Getting cognitive profile for user_id: %s", self.user_id)
135+
136+
try:
137+
profile = client.get_profile(self.user_id)
138+
except Exception:
139+
logger.exception("Failed to get Mengram cognitive profile.")
140+
raise
141+
142+
return Data(data=profile)

src/lfx/src/lfx/utils/flow_requirements.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"cv2": "opencv-python",
6262
"googleapiclient": "google-api-python-client",
6363
"mem0": "mem0ai",
64+
"mengram": "mengram-ai",
6465
"sklearn": "scikit-learn",
6566
"attr": "attrs",
6667
"gi": "PyGObject",

0 commit comments

Comments
 (0)