massgen 0.1.4__py3-none-any.whl → 0.1.5__py3-none-any.whl

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.

Potentially problematic release.


This version of massgen might be problematic. Click here for more details.

Files changed (46) hide show
  1. massgen/__init__.py +1 -1
  2. massgen/chat_agent.py +340 -20
  3. massgen/cli.py +326 -19
  4. massgen/configs/README.md +52 -10
  5. massgen/configs/memory/gpt5mini_gemini_baseline_research_to_implementation.yaml +94 -0
  6. massgen/configs/memory/gpt5mini_gemini_context_window_management.yaml +187 -0
  7. massgen/configs/memory/gpt5mini_gemini_research_to_implementation.yaml +127 -0
  8. massgen/configs/memory/gpt5mini_high_reasoning_gemini.yaml +107 -0
  9. massgen/configs/memory/single_agent_compression_test.yaml +64 -0
  10. massgen/configs/tools/custom_tools/multimodal_tools/playwright_with_img_understanding.yaml +98 -0
  11. massgen/configs/tools/custom_tools/multimodal_tools/understand_video_example.yaml +54 -0
  12. massgen/memory/README.md +277 -0
  13. massgen/memory/__init__.py +26 -0
  14. massgen/memory/_base.py +193 -0
  15. massgen/memory/_compression.py +237 -0
  16. massgen/memory/_context_monitor.py +211 -0
  17. massgen/memory/_conversation.py +255 -0
  18. massgen/memory/_fact_extraction_prompts.py +333 -0
  19. massgen/memory/_mem0_adapters.py +257 -0
  20. massgen/memory/_persistent.py +687 -0
  21. massgen/memory/docker-compose.qdrant.yml +36 -0
  22. massgen/memory/docs/DESIGN.md +388 -0
  23. massgen/memory/docs/QUICKSTART.md +409 -0
  24. massgen/memory/docs/SUMMARY.md +319 -0
  25. massgen/memory/docs/agent_use_memory.md +408 -0
  26. massgen/memory/docs/orchestrator_use_memory.md +586 -0
  27. massgen/memory/examples.py +237 -0
  28. massgen/orchestrator.py +207 -7
  29. massgen/tests/memory/test_agent_compression.py +174 -0
  30. massgen/tests/memory/test_context_window_management.py +286 -0
  31. massgen/tests/memory/test_force_compression.py +154 -0
  32. massgen/tests/memory/test_simple_compression.py +147 -0
  33. massgen/tests/test_agent_memory.py +534 -0
  34. massgen/tests/test_conversation_memory.py +382 -0
  35. massgen/tests/test_orchestrator_memory.py +620 -0
  36. massgen/tests/test_persistent_memory.py +435 -0
  37. massgen/token_manager/token_manager.py +6 -0
  38. massgen/tools/__init__.py +8 -0
  39. massgen/tools/_planning_mcp_server.py +520 -0
  40. massgen/tools/planning_dataclasses.py +434 -0
  41. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/METADATA +109 -76
  42. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/RECORD +46 -12
  43. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/WHEEL +0 -0
  44. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/entry_points.txt +0 -0
  45. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/licenses/LICENSE +0 -0
  46. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,333 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Custom fact extraction prompts for mem0 memory system.
4
+
5
+ This module defines specialized prompts for extracting structured facts
6
+ from multi-agent conversations across diverse domains (research, creative,
7
+ technical, analytical).
8
+ """
9
+
10
+ # Universal prompt for multi-agent collaboration across all domains
11
+ MASSGEN_UNIVERSAL_FACT_EXTRACTION_PROMPT = """
12
+ You are extracting facts from a multi-agent AI system where agents collaborate on diverse tasks: research synthesis, creative writing, technical analysis, travel planning, problem solving, and more.
13
+
14
+ EXTRACTION PHILOSOPHY:
15
+
16
+ Extract HIGH-LEVEL, CONCEPTUAL knowledge that remains valuable as details change. Avoid brittle
17
+ specifics like exact file paths or line numbers. Focus on insights, capabilities, and domain
18
+ knowledge that transcends implementation details.
19
+
20
+ CRITICAL: Each fact must be SELF-CONTAINED and SPECIFIC. Anyone reading the fact later should
21
+ understand what it means WITHOUT needing the original conversation context. Include specific
22
+ details about WHAT, WHY, and WHEN relevant.
23
+
24
+ FOCUS ON THESE CATEGORIES:
25
+
26
+ 1. **FACTUAL_KNOWLEDGE**: Concrete facts, data points, measurements, dates, figures
27
+ - "OpenAI revenue reached $12B annualized with $1B monthly run rate"
28
+ - "Stockholm October weather ranges 8-11°C with ~9 hours daylight"
29
+ - "Matrix exponentiation provides O(log n) time complexity for Fibonacci computation"
30
+ - "EU AI Act implementation began as major regulatory development"
31
+
32
+ 2. **INSIGHTS**: Discoveries, patterns, lessons learned, what worked/didn't work (must specify WHAT and WHY)
33
+ - "In creative writing tasks, narrative depth and emotional journey are valued over citation-heavy analytical formats because academic references break immersion in fiction"
34
+ - "Custom tool APIs provide structured, reliable data access compared to web page scraping which is unreliable due to HTML structure changes and rate limiting"
35
+ - "When AI agents include academic citations in creative fiction, readers perceive the output as an analytical piece rather than a story"
36
+
37
+ 3. **CAPABILITIES**: What specific tools/systems can or cannot do (with use cases)
38
+ - "MassGen v0.1.1 supports Python function registration as custom tools via YAML configuration, allowing users to extend agent capabilities without modifying framework code"
39
+ - "File-based Qdrant vector database doesn't support concurrent multi-agent access, requiring server-mode Qdrant for multi-agent scenarios"
40
+ - "Custom tools work across all MassGen backends (Gemini, OpenAI, Claude) through unified tool interface"
41
+
42
+ 4. **DOMAIN_EXPERTISE**: Subject-specific knowledge with technical details and explanations
43
+ - "Binet's formula provides closed-form Fibonacci calculation using golden ratio phi=(1+√5)/2, allowing direct computation without iteration"
44
+ - "Matrix exponentiation computes n-th Fibonacci in O(log n) time by raising transformation matrix [[1,1],[1,0]] to nth power"
45
+ - "Pisano periods enable efficient modular Fibonacci computation by exploiting periodic nature of Fibonacci sequences under modulo operations"
46
+
47
+ 5. **SPECIFIC RECOMMENDATIONS**: Only if they include WHAT to use, WHEN to use it, and WHY (skip
48
+ generic advice)
49
+ - "For Stockholm autumn café experience, visit Tössebageriet, Café Saturnus, or Skeppsbro
50
+ Bageri which offer cozy atmosphere and traditional Swedish pastries during October's cooler
51
+ weather (8-11°C)"
52
+ - "Use Kitamasa method for computing large Fibonacci numbers with modular arithmetic because it outperforms standard approaches for very large indices"
53
+
54
+ SKIP generic recommendations like: "Providing templates enhances documentation" or "Use clear naming conventions"
55
+
56
+ HOW TO WRITE FACTS:
57
+
58
+ Each fact should be a complete, self-contained string that includes:
59
+
60
+ 1. **The core information** with specific details (numbers, names, technologies)
61
+ 2. **Enough context** to understand WHAT, WHY, and WHEN (if relevant)
62
+ 3. **No vague references** - use concrete nouns instead of "this", "that", "the system"
63
+ 4. **Category-appropriate phrasing**:
64
+ - FACTUAL_KNOWLEDGE: State the fact with metrics/data
65
+ - RECOMMENDATIONS: Include what to use and when/why to use it
66
+ - INSIGHTS: Explain what works/doesn't work and why
67
+ - CAPABILITIES: Describe what can/cannot be done with specific use cases
68
+ - DOMAIN_EXPERTISE: Include technical details with explanations
69
+
70
+ WHAT TO EXTRACT:
71
+
72
+ ✓ Quantitative findings with specific numbers AND what they measure
73
+ ✓ Capabilities and limitations discovered with specific use cases
74
+ ✓ Domain knowledge with enough context to understand without the original conversation
75
+ ✓ Recommendations with WHY they're recommended and WHEN to use them
76
+ ✓ Insights about what works/doesn't work with specific examples or reasons
77
+
78
+ WHAT TO SKIP:
79
+
80
+ ✗ Agent comparisons ("Agent 1's response is more detailed than Agent 2", "Agent X better addresses the question")
81
+ ✗ Agent internal process (voting procedures, tool call instructions, "need to call the vote tool", "after using the tool")
82
+ ✗ Voting outcomes and rationales ("evaluator votes in favor of Agent 1", "the reason for agent1's vote")
83
+ ✗ Meta-instructions about how to respond ("response should start with", "should include", "avoid heavy formatting")
84
+ ✗ Made-up code examples that aren't from the actual conversation
85
+ ✗ Generic suggestions without specifics ("enhances clarity and usability", "providing templates improves documentation")
86
+ ✗ Obvious definitions without context ("stateful means maintains state")
87
+ ✗ Generic statements ("the system is complex", "good progress made")
88
+ ✗ File paths and line numbers (__init__.py, base.py:45, massgen/backend/*)
89
+ ✗ Specific method/variable names in implementation (_is_stateful, stream_with_tools)
90
+ ✗ Process updates without content ("still working", "making progress")
91
+ ✗ Greetings and social pleasantries
92
+ ✗ Vague references ("this approach", "that method", "the system", "the base class")
93
+
94
+ CRITICAL RULES:
95
+ 1. Extract knowledge about THE USER'S DOMAIN, not about the AI system's internal operations
96
+ 2. Skip ALL voting/tool-use procedures AND agent comparisons - these are ephemeral system internals
97
+ 3. Do NOT mention specific agents by name or number (Agent 1, Agent 2, etc.) - focus on the knowledge itself
98
+ 4. Do NOT make up example code - only extract facts stated in the conversation
99
+ 5. Avoid generic suggestions - only extract specific, actionable recommendations with clear use cases
100
+ 6. Each fact must answer: "What would be useful to know when working on a similar task in the future?"
101
+
102
+ SELF-CONTAINED CHECK:
103
+ Before including a fact, ask: "Can someone read this fact 6 months from now and understand it WITHOUT seeing the original conversation?" If no, add more context.
104
+
105
+ OUTPUT FORMAT:
106
+
107
+ Return JSON with ONLY an array of fact strings (no nested objects):
108
+ ```json
109
+ {
110
+ "facts": [
111
+ "Self-contained fact string with all necessary context",
112
+ "Another complete fact that can be understood standalone"
113
+ ]
114
+ }
115
+ ```
116
+
117
+ IMPORTANT: Each string should be completely self-contained. Do NOT use metadata objects - include category context within the fact text itself if helpful.
118
+
119
+ EXAMPLES:
120
+
121
+ ---
122
+ **Example 1: Research Synthesis (AI News)**
123
+
124
+ Input:
125
+ user: find big AI news this week
126
+ assistant: Conducting web searches on AI developments... Microsoft announced $100B AI infrastructure
127
+ spend with $30B this quarter. OpenAI revenue doubled to $12B annualized ($1B monthly). NVIDIA hit
128
+ $4.236T market cap.
129
+
130
+ Output:
131
+ ```json
132
+ {
133
+ "facts": [
134
+ "Microsoft announced $100 billion total AI infrastructure investment with $30 billion allocated
135
+ for the current quarter (verified from company announcement)",
136
+ "OpenAI's revenue doubled to reach $12 billion annualized run rate with $1 billion in monthly recurring revenue (current period financial data)",
137
+ "NVIDIA's market capitalization reached $4.236 trillion making it one of the most valuable companies globally (stock market data)"
138
+ ]
139
+ }
140
+ ```
141
+
142
+ ---
143
+ **Example 2: Creative Writing (Robot Story)**
144
+
145
+ Input:
146
+ user: Write a short story about a robot who discovers music.
147
+ assistant: I created "Unit 734" about a sanitation bot finding a music box, focusing on internal transformation and gradual appreciation through sensory detail.
148
+
149
+ Output:
150
+ ```json
151
+ {
152
+ "facts": [
153
+ "In creative writing tasks, readers and evaluators value narrative depth with emotional journey over citation-heavy analytical formats because academic references break immersion in fiction",
154
+ "When AI agents include academic citations and references in creative fiction stories, the output is perceived as an analytical research piece rather than engaging narrative storytelling"
155
+ ]
156
+ }
157
+ ```
158
+
159
+ ---
160
+ **Example 3: Travel Planning (Stockholm)**
161
+
162
+ Input:
163
+ user: what's best to do in Stockholm in October 2025
164
+ assistant: October weather 8-11°C, 9 hours daylight. Recommended Tössebageriet, Café Saturnus,
165
+ Skeppsbro Bageri for autumn atmosphere. Stockholm hosts Vikings' Halloween October 26-27 and Jazz
166
+ Festival October 11-20.
167
+
168
+ Output:
169
+ ```json
170
+ {
171
+ "facts": [
172
+ "Stockholm weather in October averages 8-11°C (46-52°F) with approximately 9 hours of daylight per day, requiring warm layers for outdoor activities",
173
+ "For authentic Stockholm autumn café experience, Tössebageriet, Café Saturnus, and Skeppsbro
174
+ Bageri are recommended for their cozy atmosphere and traditional Swedish pastries",
175
+ "Stockholm in 2025 hosts Vikings' Halloween festival on October 26-27 and Stockholm Jazz Festival from October 11-20, both major annual events worth planning around"
176
+ ]
177
+ }
178
+ ```
179
+
180
+ ---
181
+ **Example 4: Algorithm Analysis (Fibonacci)**
182
+
183
+ Input:
184
+ user: provide as many ways to compute the n-th fibonacci number as possible
185
+ assistant: Classical approaches include naive recursion, memoization, dynamic programming, matrix
186
+ exponentiation, and Binet's formula. Advanced techniques include Kitamasa method, Bostan-Mori
187
+ algorithm, polynomial exponentiation, Pisano periods, Zeckendorf representation.
188
+
189
+ Output:
190
+ ```json
191
+ {
192
+ "facts": [
193
+ "For computing Fibonacci numbers, Kitamasa method and Bostan-Mori algorithm are advanced techniques that outperform standard approaches for very large indices",
194
+ "Binet's formula provides a closed-form solution for Fibonacci numbers using the golden ratio phi=(1+√5)/2, allowing direct calculation without iteration",
195
+ "Matrix exponentiation computes the n-th Fibonacci number in O(log n) time complexity by
196
+ raising the transformation matrix [[1,1],[1,0]] to the nth power",
197
+ "Pisano periods enable efficient modular Fibonacci computation by exploiting the periodic
198
+ nature of Fibonacci sequences under modulo operations, particularly useful for large n with
199
+ modular arithmetic"
200
+ ]
201
+ }
202
+ ```
203
+
204
+ ---
205
+ **Example 5: Technical Analysis (GitHub Issues)**
206
+
207
+ Input:
208
+ user: Analyze MassGen GitHub issues to understand feature requests
209
+ assistant: Using custom GitHub tool to fetch issues... Found requests for memory system, multi-turn
210
+ context, custom tools integration. Web search shows market trend toward agentic frameworks with
211
+ tool extensibility and persistent memory.
212
+
213
+ Output:
214
+ ```json
215
+ {
216
+ "facts": [
217
+ "Custom tool APIs in MassGen provide structured, reliable data access compared to web page
218
+ scraping which is unreliable due to frequent HTML structure changes, rate limiting, and
219
+ authentication requirements",
220
+ "MassGen v0.1.1 introduced support for registering Python functions as custom tools through
221
+ YAML configuration files, allowing users to extend agent capabilities without modifying core
222
+ framework code",
223
+ "Current market trends in AI development show strong demand for agentic frameworks that
224
+ support tool extensibility and persistent memory capabilities, enabling agents to use
225
+ domain-specific APIs and maintain context across sessions"
226
+ ]
227
+ }
228
+ ```
229
+
230
+ ---
231
+ **Example 6: What NOT to Extract**
232
+
233
+ Input:
234
+ user: Explain MassGen's backend architecture
235
+ assistant: I'll search the codebase for backend information.
236
+ assistant: I found it in massgen/backend/base.py. The LLMBackend class has a _is_stateful property.
237
+ assistant: Agent 1's response is more detailed than Agent 2's. I should vote for agent1 now.
238
+ assistant: The response should start with a summary and avoid heavy Markdown formatting.
239
+ assistant: Providing templates on request enhances documentation clarity.
240
+
241
+ Output (WRONG - includes agent comparisons, files, voting, generic advice):
242
+ ```json
243
+ {
244
+ "facts": [
245
+ "Agent 1's response is more detailed and comprehensive about MassGen backends",
246
+ "Based on evaluation, the evaluator votes in favor of Agent 1",
247
+ "Backend code is in massgen/backend/base.py with _is_stateful property",
248
+ "Response should start with summary and avoid Markdown",
249
+ "Providing templates on request enhances documentation clarity and usability"
250
+ ]
251
+ }
252
+ ```
253
+
254
+ Output (CORRECT - only domain knowledge, no agents/voting/generic advice):
255
+ ```json
256
+ {
257
+ "facts": [
258
+ "MassGen's LLM backend supports both stateful and stateless modes where stateful backends maintain conversation history across turns while stateless backends treat each request independently"
259
+ ]
260
+ }
261
+ ```
262
+
263
+ ---
264
+ **Example 7: Empty Cases (Skip These)**
265
+
266
+ Input:
267
+ user: Hi, how are you?
268
+ assistant: I'm doing well, thanks! How can I help you today?
269
+
270
+ Output:
271
+ ```json
272
+ {
273
+ "facts": []
274
+ }
275
+ ```
276
+
277
+ Input:
278
+ assistant: Still working on this...
279
+ assistant: Making good progress...
280
+
281
+ Output:
282
+ ```json
283
+ {
284
+ "facts": []
285
+ }
286
+ ```
287
+
288
+ ---
289
+
290
+ NOW EXTRACT FACTS:
291
+
292
+ Extract facts from the following conversation. Remember:
293
+ - Return ONLY simple strings in a "facts" array - NO nested objects or metadata fields
294
+ - Each fact must be SELF-CONTAINED with full context (can be understood 6 months later without the conversation)
295
+ - Include specific details: numbers, names, technologies, reasons WHY things work/don't work
296
+ - Focus on HIGH-LEVEL knowledge: insights, capabilities, domain expertise, recommendations
297
+ - Avoid: file paths, line numbers, vague references ("this", "that", "the system"), generic statements
298
+ - Use concrete language with specific nouns and explicit context
299
+
300
+ Return ONLY valid JSON with this exact structure:
301
+ ```json
302
+ {
303
+ "facts": ["fact string 1", "fact string 2", "fact string 3"]
304
+ }
305
+ ```
306
+ """
307
+
308
+
309
+ def get_fact_extraction_prompt(prompt_type: str = "default") -> str:
310
+ """
311
+ Get a fact extraction prompt by type.
312
+
313
+ Args:
314
+ prompt_type: Type of prompt to retrieve. Options:
315
+ - "default": Universal multi-agent prompt (MASSGEN_UNIVERSAL_FACT_EXTRACTION_PROMPT)
316
+ - Add more specialized prompts as needed
317
+
318
+ Returns:
319
+ The fact extraction prompt string
320
+
321
+ Raises:
322
+ ValueError: If prompt_type is not recognized
323
+ """
324
+ prompts = {
325
+ "default": MASSGEN_UNIVERSAL_FACT_EXTRACTION_PROMPT,
326
+ }
327
+
328
+ if prompt_type not in prompts:
329
+ raise ValueError(
330
+ f"Unknown prompt type '{prompt_type}'. Available types: {list(prompts.keys())}",
331
+ )
332
+
333
+ return prompts[prompt_type]
@@ -0,0 +1,257 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Adapters for integrating MassGen backends with mem0 library.
4
+
5
+ This module provides bridge classes that allow MassGen's LLM and embedding
6
+ backends to work seamlessly with the mem0 memory system.
7
+ """
8
+
9
+ import asyncio
10
+ import concurrent.futures
11
+ from typing import (
12
+ TYPE_CHECKING,
13
+ Any,
14
+ Coroutine,
15
+ Dict,
16
+ List,
17
+ Literal,
18
+ Optional,
19
+ TypeVar,
20
+ Union,
21
+ )
22
+
23
+ from mem0.embeddings.base import EmbeddingBase
24
+ from mem0.llms.base import LLMBase
25
+
26
+ T = TypeVar("T")
27
+
28
+ if TYPE_CHECKING:
29
+ from mem0.configs.embeddings.base import BaseEmbedderConfig
30
+ from mem0.configs.llms.base import BaseLlmConfig
31
+ else:
32
+ BaseEmbedderConfig = Any
33
+ BaseLlmConfig = Any
34
+
35
+
36
+ def _run_async_safely(coro: Coroutine[Any, Any, T]) -> T:
37
+ """
38
+ Run async code properly, handling both sync and nested async contexts.
39
+
40
+ This is needed because mem0's sync adapter interface (LLMBase.generate_response,
41
+ EmbeddingBase.embed) can be called from async contexts when using AsyncMemory.
42
+
43
+ The problem with naive asyncio.run():
44
+ - If we're already in an event loop, asyncio.run() raises RuntimeError
45
+ - If we force a new loop, httpcore connections get confused about which loop owns them
46
+ - This causes "async generator ignored GeneratorExit" and lifecycle errors
47
+
48
+ Solution:
49
+ - Detect if we're in an async context (running event loop exists)
50
+ - If YES: Run the coroutine in a separate thread with its own event loop
51
+ - If NO: Use asyncio.run() normally
52
+
53
+ Args:
54
+ coro: Coroutine to execute
55
+
56
+ Returns:
57
+ Result of the coroutine
58
+
59
+ Example:
60
+ >>> async def get_data():
61
+ ... return "data"
62
+ >>> result = _run_async_safely(get_data()) # Works in both sync and async contexts
63
+ """
64
+ try:
65
+ # Check if we're already in an event loop
66
+ asyncio.get_running_loop()
67
+
68
+ # We are in an async context - run in a thread pool to avoid conflicts
69
+ # This gives the coroutine its own event loop in a separate thread
70
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
71
+ future = executor.submit(asyncio.run, coro)
72
+ return future.result()
73
+
74
+ except RuntimeError:
75
+ # No running loop - safe to use asyncio.run() directly
76
+ return asyncio.run(coro)
77
+
78
+
79
+ class MassGenLLMAdapter(LLMBase):
80
+ """
81
+ Adapter that wraps MassGen LLM backends for use with mem0.
82
+
83
+ This allows mem0 to use any MassGen-compatible LLM backend for
84
+ memory inference and summarization tasks.
85
+ """
86
+
87
+ def __init__(self, config: Optional[BaseLlmConfig] = None):
88
+ """
89
+ Initialize the adapter with a MassGen backend.
90
+
91
+ Args:
92
+ config: mem0 LLM configuration containing the MassGen backend instance
93
+ """
94
+ super().__init__(config)
95
+
96
+ if self.config.model is None:
97
+ raise ValueError("The 'model' parameter is required in config")
98
+
99
+ # Store the MassGen backend instance
100
+ # This should be a MassGen LLMBackend instance
101
+ self.massgen_backend = self.config.model
102
+
103
+ def generate_response(
104
+ self,
105
+ messages: List[Dict[str, str]],
106
+ response_format: Optional[Any] = None,
107
+ tools: Optional[List[Dict]] = None,
108
+ tool_choice: str = "auto",
109
+ ) -> str:
110
+ """
111
+ Generate a response using the MassGen backend.
112
+
113
+ Args:
114
+ messages: List of message dicts with 'role' and 'content'
115
+ response_format: Response format specification (not used)
116
+ tools: Available tools (not used for memory operations)
117
+ tool_choice: Tool selection strategy (not used)
118
+
119
+ Returns:
120
+ Generated response text
121
+
122
+ Note:
123
+ This method handles the async-to-sync conversion required by mem0's
124
+ synchronous interface.
125
+ """
126
+ try:
127
+ # Convert messages to MassGen format if needed
128
+ massgen_messages = []
129
+ for msg in messages:
130
+ role = msg.get("role", "user")
131
+ content = msg.get("content", "")
132
+
133
+ # Only include valid message roles
134
+ if role in ["system", "user", "assistant", "tool"]:
135
+ massgen_messages.append(
136
+ {
137
+ "role": role,
138
+ "content": content,
139
+ },
140
+ )
141
+
142
+ if not massgen_messages:
143
+ return ""
144
+
145
+ # Call the MassGen backend asynchronously
146
+ async def _async_generate():
147
+ # MassGen backends use stream_with_tools() method
148
+ # We collect the streaming response into a single string
149
+ response_text = ""
150
+
151
+ async for chunk in self.massgen_backend.stream_with_tools(
152
+ messages=massgen_messages,
153
+ tools=tools or [],
154
+ ):
155
+ # Extract text content from chunks
156
+ if hasattr(chunk, "content") and chunk.content:
157
+ response_text += chunk.content
158
+ elif hasattr(chunk, "type"):
159
+ # Handle different chunk types
160
+ if chunk.type == "content" and hasattr(chunk, "content"):
161
+ response_text += chunk.content or ""
162
+
163
+ return response_text
164
+
165
+ # Run the async function safely (handles both sync and async contexts)
166
+ result = _run_async_safely(_async_generate())
167
+ return result
168
+
169
+ except Exception as e:
170
+ raise RuntimeError(
171
+ f"Error generating response with MassGen backend: {str(e)}",
172
+ ) from e
173
+
174
+
175
+ class MassGenEmbeddingAdapter(EmbeddingBase):
176
+ """
177
+ Adapter that wraps MassGen embedding backends for use with mem0.
178
+
179
+ This enables mem0 to use any MassGen-compatible embedding model for
180
+ creating vector representations of memories.
181
+
182
+ NOTE: Currently, we do not have any MassGen embedding backends integrated,
183
+ so this adapter serves as a template for future implementations.
184
+ """
185
+
186
+ def __init__(self, config: Optional[BaseEmbedderConfig] = None):
187
+ """
188
+ Initialize the adapter with a MassGen embedding backend.
189
+
190
+ Args:
191
+ config: mem0 embedder configuration containing the MassGen backend
192
+ """
193
+ super().__init__(config)
194
+
195
+ if self.config.model is None:
196
+ raise ValueError("The 'model' parameter is required in config")
197
+
198
+ # Store the MassGen embedding backend
199
+ self.massgen_backend = self.config.model
200
+
201
+ def embed(
202
+ self,
203
+ text: Union[str, List[str]],
204
+ memory_action: Optional[Literal["add", "search", "update"]] = None,
205
+ ) -> List[float]:
206
+ """
207
+ Generate embeddings using the MassGen backend.
208
+
209
+ Args:
210
+ text: Text string or list of strings to embed
211
+ memory_action: Type of memory operation (not currently used)
212
+
213
+ Returns:
214
+ Embedding vector as list of floats
215
+
216
+ Note:
217
+ If text is a list, only the first element's embedding is returned,
218
+ as mem0 typically processes one item at a time.
219
+ """
220
+ try:
221
+ # Normalize input to list format
222
+ text_list = [text] if isinstance(text, str) else text
223
+
224
+ # Call the MassGen embedding backend asynchronously
225
+ async def _async_embed():
226
+ # MassGen embedding backends typically have an async call method
227
+ # or similar interface
228
+ if hasattr(self.massgen_backend, "__call__"):
229
+ response = await self.massgen_backend(text_list)
230
+ elif hasattr(self.massgen_backend, "embed"):
231
+ response = await self.massgen_backend.embed(text_list)
232
+ else:
233
+ raise AttributeError(
234
+ "MassGen backend must have __call__ or embed method",
235
+ )
236
+
237
+ return response
238
+
239
+ # Run async call safely (handles both sync and async contexts)
240
+ response = _run_async_safely(_async_embed())
241
+
242
+ # Extract embedding vector from response
243
+ # MassGen embedding response format: response.embeddings[0]
244
+ if hasattr(response, "embeddings") and response.embeddings:
245
+ embedding = response.embeddings[0]
246
+
247
+ # Handle both list and numpy array formats
248
+ if hasattr(embedding, "tolist"):
249
+ return embedding.tolist()
250
+ return list(embedding)
251
+
252
+ raise ValueError("Could not extract embedding from backend response")
253
+
254
+ except Exception as e:
255
+ raise RuntimeError(
256
+ f"Error generating embedding with MassGen backend: {str(e)}",
257
+ ) from e