Cherry-picked from feat/mem0-haiku-oauth onto a clean nanobot-owned branch.
Uses the main agent's existing LLM provider to extract facts from conversations, then stores them with infer=False — bypassing mem0's default GPT-nano call.
extract_facts(): sends conversation to provider.chat() with one-liner extraction prompt
store_facts(): stores each fact via mem0 with infer=False
consolidate(): calls extract_facts + store_facts instead of add_conversation
No new files, no Dockerfile changes, no mem0 package patches.
Cherry-picked from feat/mem0-haiku-oauth onto a clean nanobot-owned branch.
Uses the main agent's existing LLM provider to extract facts from conversations, then stores them with infer=False — bypassing mem0's default GPT-nano call.
- extract_facts(): sends conversation to provider.chat() with one-liner extraction prompt
- store_facts(): stores each fact via mem0 with infer=False
- consolidate(): calls extract_facts + store_facts instead of add_conversation
No new files, no Dockerfile changes, no mem0 package patches.
Instead of hacking mem0's provider system, use the main agent's
existing LLM (already running, already paid for) to extract facts
from conversations, then store them with infer=False.
- extract_facts(): sends conversation to provider.chat() with extraction prompt
- store_facts(): stores each fact via mem0 with infer=False
- consolidate(): calls extract_facts + store_facts instead of add_conversation
- No new files, no Dockerfile changes, no mem0 package patches
Lines 61-80 show code fragments not inside any method:
# These lines are orphaned in __init__ but reference non-existent async methods:facts=awaitself.extract_facts(mem0_messages,provider,model)self.store_facts(facts,user_id=user_id,session_id=session.key)# Lines 64-80: Code without method wrapperifarchive_all:session.last_consolidated=len(session.messages)# ... (belongs to consolidate() but that method definition is missing)
4. Incomplete Cherry-Pick 🟡
The commit message describes implementing extract_facts() and store_facts(), but these methods were never included in the cherry-picked commit. Likely the original feat/mem0-haiku-oauth branch had these in a separate commit that wasn't picked.
What the PR Should Do
According to the description (and it's a good architectural idea):
Use the main agent's LLM to extract facts (avoiding mem0's GPT-nano calls)
Store with infer=False to bypass mem0's extraction
No new dependencies
The concept is sound, but the implementation is incomplete.
Required Fixes
To make this PR viable:
Implement extract_facts() method:
asyncdefextract_facts(self,messages:list[dict],provider:LLMProvider,model:str)->list[str]:"""Extract facts using the agent's LLM."""# Build extraction prompt with custom_prompt# Call provider.chat() with the prompt# Parse JSON response {"facts": [...]}# Return list of facts
Implement store_facts() method:
defstore_facts(self,facts:list[str],user_id:str,session_id:str)->None:"""Store facts in mem0 with infer=False."""forfactinfacts:self.memory.add(fact,user_id=user_id,metadata={"session_id":session_id},infer=False# KEY: bypass GPT-nano)
Restore complete consolidate() method with proper structure
Handle deleted methods:
If removing search_memories(), get_memory_context(), etc., update all calling code in agent/loop.py
Otherwise, restore these methods
Recommendation
DO NOT MERGE. Options:
Close and re-implement properly with complete methods
Push fixes to feat/mem0-extract-llm branch with missing implementations
Check original branchfeat/mem0-haiku-oauth for missing commits and cherry-pick properly
Impact if merged: Immediate runtime failure. The nanobot agent will crash on first memory consolidation attempt.
## Code Review: Critical Issues Found ❌
### Summary
This PR is **BROKEN and cannot be merged**. The code references undefined methods and will cause runtime failures.
---
### Critical Issues
#### 1. Missing Method Implementations 🔴
The code calls two methods that are **never defined**:
- `await self.extract_facts(mem0_messages, provider, model)` (line ~61)
- `self.store_facts(facts, user_id=user_id, session_id=session.key)` (line ~62)
These methods don't exist anywhere in the file. **Runtime will fail immediately with `AttributeError`**.
#### 2. Incomplete Class Structure 🔴
The modified file is only **80 lines** vs **431 lines** on main. Critical deletions include:
- `search_memories()` - semantic search (deleted, no replacement)
- `get_memory_context()` - system prompt integration (deleted, **breaks agent loop**)
- `add_conversation()` - message ingestion (deleted)
- `update_memory()`, `delete_memory()`, `get_all_memories()` - all CRUD operations deleted
- **Complete `consolidate()` method** - file shows orphaned code fragments without proper method definition
#### 3. Broken Code Structure 🔴
Lines 61-80 show code fragments **not inside any method**:
```python
# These lines are orphaned in __init__ but reference non-existent async methods:
facts = await self.extract_facts(mem0_messages, provider, model)
self.store_facts(facts, user_id=user_id, session_id=session.key)
# Lines 64-80: Code without method wrapper
if archive_all:
session.last_consolidated = len(session.messages)
# ... (belongs to consolidate() but that method definition is missing)
```
#### 4. Incomplete Cherry-Pick 🟡
The commit message describes implementing `extract_facts()` and `store_facts()`, but these methods were **never included** in the cherry-picked commit. Likely the original `feat/mem0-haiku-oauth` branch had these in a **separate commit** that wasn't picked.
---
### What the PR Should Do
According to the description (and it's a good architectural idea):
1. Use the main agent's LLM to extract facts (avoiding mem0's GPT-nano calls)
2. Store with `infer=False` to bypass mem0's extraction
3. No new dependencies
**The concept is sound**, but the implementation is incomplete.
---
### Required Fixes
To make this PR viable:
1. **Implement `extract_facts()` method**:
```python
async def extract_facts(
self,
messages: list[dict],
provider: LLMProvider,
model: str
) -> list[str]:
"""Extract facts using the agent's LLM."""
# Build extraction prompt with custom_prompt
# Call provider.chat() with the prompt
# Parse JSON response {"facts": [...]}
# Return list of facts
```
2. **Implement `store_facts()` method**:
```python
def store_facts(
self,
facts: list[str],
user_id: str,
session_id: str
) -> None:
"""Store facts in mem0 with infer=False."""
for fact in facts:
self.memory.add(
fact,
user_id=user_id,
metadata={"session_id": session_id},
infer=False # KEY: bypass GPT-nano
)
```
3. **Restore complete `consolidate()` method** with proper structure
4. **Handle deleted methods**:
- If removing `search_memories()`, `get_memory_context()`, etc., update **all calling code** in `agent/loop.py`
- Otherwise, restore these methods
---
### Recommendation
**DO NOT MERGE**. Options:
1. **Close** and re-implement properly with complete methods
2. **Push fixes** to `feat/mem0-extract-llm` branch with missing implementations
3. **Check original branch** `feat/mem0-haiku-oauth` for missing commits and cherry-pick properly
---
**Impact if merged**: Immediate runtime failure. The nanobot agent will crash on first memory consolidation attempt.
Uses the main agent's existing LLM provider to extract facts from
conversations, then stores them with infer=False — bypassing mem0's
default GPT-nano call.
- extract_facts(): sends conversation to provider.chat() with one-liner prompt
- store_facts(): stores each fact via mem0 with infer=False
- consolidate(): calls extract_facts + store_facts instead of add_conversation
Problem: This prompt is never used. The new flow calls provider.chat() directly with self.custom_prompt, completely bypassing mem0's extraction system.
Fix: Remove this dead code:
# Delete line 60, just keep:self.custom_prompt=custom_promptmem0_config=MemoryConfig(**mem0_cfg_dict)# Don't pass custom_fact_extraction_prompt
2. JSON Parsing Bug 🔴
Lines 184-188: Missing .strip() will cause parse failures:
iftext.startswith("```"):text=text.split("```")[1]# "json\n{\"facts\": [...]}\n"iftext.startswith("json"):text=text[4:]# "\n{\"facts\": [...]}\n" ← leading newline!data=_json.loads(text)# JSONDecodeError
When it breaks: LLM returns:
```json
{"facts": [...]}
**Fix:**
```python
if text.startswith("```"):
text = text.split("```")[1]
if text.startswith("json"):
text = text[4:]
text = text.strip() # ← Add this
data = _json.loads(text)
3. No Response Validation 🟡
Line 189:data.get("facts", []) assumes the JSON is well-formed.
What if:
LLM returns {"facts": "not a list"} → returns string, not list → runtime error later
LLM returns plain text instead of JSON → already caught by try/except, returns []
Fix:
data=_json.loads(text.strip())facts=data.get("facts",[])ifnotisinstance(facts,list):# ← Add validationlogger.warning(f"LLM returned non-list facts: {type(facts)}")return[]returnfacts
Quality Concerns
4. Prompt May Be Too Minimal 🟡
The old prompt was 100 lines with:
Context about nanobot's research/debugging use case
Examples of what to extract vs skip
Date-inclusion rules for time-sensitive facts
Tool output filtering instructions
New prompt:
"Extract dated facts from this conversation as JSON: {\"facts\": [...]}. Today is {today}.\n\n"
Risk: Without guardrails, the LLM may:
Extract tool output/command invocations as "facts"
Miss important assistant research findings
Not date-stamp time-sensitive information
Extract noise from routine acknowledgments
Recommendation: At minimum, add:
custom_prompt=f"""Extract important facts from this conversation as JSON.
Focus on: research findings, user interests, technical work completed.
Skip: tool output, commands, routine responses.
For time-sensitive facts, include the date.
Today is {today}.
Format: {{"facts": ["fact1", "fact2", ...]}}"""
5. Message Content Handling 🟢
Lines 166-169:extract_facts() only processes string content:
This is actually fine because consolidate() normalizes list content to strings before calling extract_facts() (lines 360-380). Messages with tool_use blocks are already flattened.
Performance/Economics
6. API Call Impact 🟡
Old flow:
User message → agent processes (1 API call)
Consolidate → mem0 extracts internally (0 additional calls to main provider)
New flow:
User message → agent processes (1 API call)
Consolidate → extract_facts() (1 additional API call)
Question: Does this actually save costs, or just shift them?
If mem0 was using GPT-nano (external): This saves $ by using already-paid subscription
If using OAuth with quota limits: This increases quota burn
With max_tokens=2000 per extraction, frequent consolidations could burn quota faster than the old approach.
HOLD - Implementation logic is sound, but has bugs that will cause runtime failures:
Must fix: JSON parsing (add .strip())
Should fix: Remove dead code, add validation
Consider: Improving prompt (middle ground between old verbose and new minimal)
Verify: Quota economics work in your favor
Once bugs are fixed, this is a reasonable approach if prompt quality holds up in practice.
## Code Review
### Implementation Overview
The PR successfully implements the described feature:
- ✅ `extract_facts()`: Uses agent's LLM to extract facts from conversation
- ✅ `store_facts()`: Stores with `infer=False` to bypass mem0's GPT-nano
- ✅ `consolidate()`: Integrated to use new extraction flow
### Critical Issues
#### 1. Dead Code - Custom Prompt in MemoryConfig 🔴
**Lines 60-67:** The code still sets `custom_fact_extraction_prompt` in MemoryConfig:
```python
mem0_cfg_dict["custom_fact_extraction_prompt"] = custom_prompt
mem0_config = MemoryConfig(**mem0_cfg_dict)
self.memory = Memory(config=mem0_config)
```
**Problem:** This prompt is never used. The new flow calls `provider.chat()` directly with `self.custom_prompt`, completely bypassing mem0's extraction system.
**Fix:** Remove this dead code:
```python
# Delete line 60, just keep:
self.custom_prompt = custom_prompt
mem0_config = MemoryConfig(**mem0_cfg_dict) # Don't pass custom_fact_extraction_prompt
```
#### 2. JSON Parsing Bug 🔴
**Lines 184-188:** Missing `.strip()` will cause parse failures:
```python
if text.startswith("```"):
text = text.split("```")[1] # "json\n{\"facts\": [...]}\n"
if text.startswith("json"):
text = text[4:] # "\n{\"facts\": [...]}\n" ← leading newline!
data = _json.loads(text) # JSONDecodeError
```
**When it breaks:** LLM returns:
```
```json
{"facts": [...]}
```
```
**Fix:**
```python
if text.startswith("```"):
text = text.split("```")[1]
if text.startswith("json"):
text = text[4:]
text = text.strip() # ← Add this
data = _json.loads(text)
```
#### 3. No Response Validation 🟡
**Line 189:** `data.get("facts", [])` assumes the JSON is well-formed.
**What if:**
- LLM returns `{"facts": "not a list"}` → returns string, not list → runtime error later
- LLM returns `{"thoughts": "...", "facts": [...]}` → works, but wastes tokens
- LLM returns plain text instead of JSON → already caught by try/except, returns `[]`
**Fix:**
```python
data = _json.loads(text.strip())
facts = data.get("facts", [])
if not isinstance(facts, list): # ← Add validation
logger.warning(f"LLM returned non-list facts: {type(facts)}")
return []
return facts
```
### Quality Concerns
#### 4. Prompt May Be Too Minimal 🟡
The old prompt was 100 lines with:
- Context about nanobot's research/debugging use case
- Examples of what to extract vs skip
- Date-inclusion rules for time-sensitive facts
- Tool output filtering instructions
New prompt:
```python
"Extract dated facts from this conversation as JSON: {\"facts\": [...]}. Today is {today}.\n\n"
```
**Risk:** Without guardrails, the LLM may:
- Extract tool output/command invocations as "facts"
- Miss important assistant research findings
- Not date-stamp time-sensitive information
- Extract noise from routine acknowledgments
**Recommendation:** At minimum, add:
```python
custom_prompt = f"""Extract important facts from this conversation as JSON.
Focus on: research findings, user interests, technical work completed.
Skip: tool output, commands, routine responses.
For time-sensitive facts, include the date.
Today is {today}.
Format: {{"facts": ["fact1", "fact2", ...]}}
"""
```
#### 5. Message Content Handling 🟢
**Lines 166-169:** `extract_facts()` only processes string content:
```python
for msg in messages:
content_val = msg.get("content", "")
if isinstance(content_val, str) and content_val.strip():
conv_text += f"{role}: {content_val}\n\n"
```
This is **actually fine** because `consolidate()` normalizes list content to strings before calling `extract_facts()` (lines 360-380). Messages with tool_use blocks are already flattened.
### Performance/Economics
#### 6. API Call Impact 🟡
Old flow:
1. User message → agent processes (1 API call)
2. Consolidate → mem0 extracts internally (0 additional calls to main provider)
New flow:
1. User message → agent processes (1 API call)
2. Consolidate → `extract_facts()` (1 additional API call)
**Question:** Does this actually save costs, or just shift them?
- **If mem0 was using GPT-nano (external):** This saves $ by using already-paid subscription
- **If using OAuth with quota limits:** This **increases** quota burn
With `max_tokens=2000` per extraction, frequent consolidations could burn quota faster than the old approach.
### Testing Requirements
Before merge:
- [ ] **Fix JSON parsing** (add `.strip()`)
- [ ] **Remove dead code** (custom_prompt in MemoryConfig)
- [ ] **Add facts validation** (check `isinstance(facts, list)`)
- [ ] **Test minimal prompt** - if quality drops, add brief instructions
- [ ] **Verify quota impact** - monitor OAuth usage before/after
- [ ] **Test edge cases:**
- LLM returns markdown fence
- LLM returns malformed JSON
- LLM returns `{"facts": "string"}` instead of list
- Empty conversation
### Verdict
**HOLD** - Implementation logic is sound, but has bugs that will cause runtime failures:
1. **Must fix:** JSON parsing (add `.strip()`)
2. **Should fix:** Remove dead code, add validation
3. **Consider:** Improving prompt (middle ground between old verbose and new minimal)
4. **Verify:** Quota economics work in your favor
Once bugs are fixed, this is a reasonable approach if prompt quality holds up in practice.
The implementation is now solid and won't cause runtime failures.
Remaining items are monitoring tasks:
Monitor prompt quality in production (adjust if extraction quality drops)
Monitor quota usage (verify OAuth economics work as expected)
Ready to merge.
## ✅ Approved
All critical issues have been fixed in commit 790bdd6:
✅ Dead code removed (custom_fact_extraction_prompt in MemoryConfig)
✅ JSON parsing bug fixed (added `.strip()`)
✅ Facts validation added (`isinstance(facts, list)` check)
The implementation is now solid and won't cause runtime failures.
**Remaining items are monitoring tasks:**
- Monitor prompt quality in production (adjust if extraction quality drops)
- Monitor quota usage (verify OAuth economics work as expected)
Ready to merge.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Cherry-picked from feat/mem0-haiku-oauth onto a clean nanobot-owned branch.
Uses the main agent's existing LLM provider to extract facts from conversations, then stores them with infer=False — bypassing mem0's default GPT-nano call.
No new files, no Dockerfile changes, no mem0 package patches.
77e54b6bc6to9e8c910ab1Code Review: Critical Issues Found ❌
Summary
This PR is BROKEN and cannot be merged. The code references undefined methods and will cause runtime failures.
Critical Issues
1. Missing Method Implementations 🔴
The code calls two methods that are never defined:
await self.extract_facts(mem0_messages, provider, model)(line ~61)self.store_facts(facts, user_id=user_id, session_id=session.key)(line ~62)These methods don't exist anywhere in the file. Runtime will fail immediately with
AttributeError.2. Incomplete Class Structure 🔴
The modified file is only 80 lines vs 431 lines on main. Critical deletions include:
search_memories()- semantic search (deleted, no replacement)get_memory_context()- system prompt integration (deleted, breaks agent loop)add_conversation()- message ingestion (deleted)update_memory(),delete_memory(),get_all_memories()- all CRUD operations deletedconsolidate()method - file shows orphaned code fragments without proper method definition3. Broken Code Structure 🔴
Lines 61-80 show code fragments not inside any method:
4. Incomplete Cherry-Pick 🟡
The commit message describes implementing
extract_facts()andstore_facts(), but these methods were never included in the cherry-picked commit. Likely the originalfeat/mem0-haiku-oauthbranch had these in a separate commit that wasn't picked.What the PR Should Do
According to the description (and it's a good architectural idea):
infer=Falseto bypass mem0's extractionThe concept is sound, but the implementation is incomplete.
Required Fixes
To make this PR viable:
Implement
extract_facts()method:Implement
store_facts()method:Restore complete
consolidate()method with proper structureHandle deleted methods:
search_memories(),get_memory_context(), etc., update all calling code inagent/loop.pyRecommendation
DO NOT MERGE. Options:
feat/mem0-extract-llmbranch with missing implementationsfeat/mem0-haiku-oauthfor missing commits and cherry-pick properlyImpact if merged: Immediate runtime failure. The nanobot agent will crash on first memory consolidation attempt.
Code Review
Implementation Overview
The PR successfully implements the described feature:
extract_facts(): Uses agent's LLM to extract facts from conversationstore_facts(): Stores withinfer=Falseto bypass mem0's GPT-nanoconsolidate(): Integrated to use new extraction flowCritical Issues
1. Dead Code - Custom Prompt in MemoryConfig 🔴
Lines 60-67: The code still sets
custom_fact_extraction_promptin MemoryConfig:Problem: This prompt is never used. The new flow calls
provider.chat()directly withself.custom_prompt, completely bypassing mem0's extraction system.Fix: Remove this dead code:
2. JSON Parsing Bug 🔴
Lines 184-188: Missing
.strip()will cause parse failures:When it breaks: LLM returns:
3. No Response Validation 🟡
Line 189:
data.get("facts", [])assumes the JSON is well-formed.What if:
{"facts": "not a list"}→ returns string, not list → runtime error later{"thoughts": "...", "facts": [...]}→ works, but wastes tokens[]Fix:
Quality Concerns
4. Prompt May Be Too Minimal 🟡
The old prompt was 100 lines with:
New prompt:
Risk: Without guardrails, the LLM may:
Recommendation: At minimum, add:
5. Message Content Handling 🟢
Lines 166-169:
extract_facts()only processes string content:This is actually fine because
consolidate()normalizes list content to strings before callingextract_facts()(lines 360-380). Messages with tool_use blocks are already flattened.Performance/Economics
6. API Call Impact 🟡
Old flow:
New flow:
extract_facts()(1 additional API call)Question: Does this actually save costs, or just shift them?
With
max_tokens=2000per extraction, frequent consolidations could burn quota faster than the old approach.Testing Requirements
Before merge:
.strip())isinstance(facts, list)){"facts": "string"}instead of listVerdict
HOLD - Implementation logic is sound, but has bugs that will cause runtime failures:
.strip())Once bugs are fixed, this is a reasonable approach if prompt quality holds up in practice.
✅ Approved
All critical issues have been fixed in commit
790bdd6:✅ Dead code removed (custom_fact_extraction_prompt in MemoryConfig)
✅ JSON parsing bug fixed (added
.strip())✅ Facts validation added (
isinstance(facts, list)check)The implementation is now solid and won't cause runtime failures.
Remaining items are monitoring tasks:
Ready to merge.