-
Notifications
You must be signed in to change notification settings - Fork 633
fix(migrate): resolve agent_id loss in OKF migration & add multi-agent showcase #1874
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kareemkhaalil
wants to merge
5
commits into
moorcheh-ai:main
Choose a base branch
from
kareemkhaalil:feat/fix-okf-agent-migration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
fa392eb
fix(migrate): preserve agent_id in OKF migration and add multi-agent …
kareemkhaalil 65114f7
feat(showcase): add Manzoma multi-agent migration showcase
kareemkhaalil 6d8aa63
refactor(showcase): improve showcase robustness and fix runner valida…
kareemkhaalil 711a1a6
refactor(showcase): add proper exit status for verification failures
9a8bb1d
docs: add README and docstrings for Manzoma migration showcase
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
110 changes: 110 additions & 0 deletions
110
examples/migrations/manzoma/manzoma_migration_showcase.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
|
|
||
| import os | ||
| import sys | ||
| import tempfile | ||
| from pathlib import Path | ||
| from datetime import datetime, timezone | ||
| from unittest.mock import MagicMock | ||
|
|
||
| # Ensure we can import memanto | ||
| sys.path.append(str(Path(__file__).parent.parent.parent.parent)) | ||
|
|
||
| from memanto.cli.migrate.runner import run_migration | ||
| from memanto.cli.migrate.okf_loader import load_okf_bundle | ||
|
|
||
| def run_manzoma_freedom_showcase(): | ||
| """ | ||
| SHOWCASE: The Manzoma Freedom Loop 🦋 | ||
|
|
||
| Scenario: A retail business in Egypt (Manzoma ERP) wants to migrate | ||
| memories for 3 different branches (Alpha, Beta, Gamma) into Memanto | ||
| without losing agent context. | ||
| """ | ||
| print("🚀 Starting Manzoma -> Memanto Multi-Agent Migration Showcase") | ||
|
|
||
| with tempfile.TemporaryDirectory() as tmp_dir: | ||
| showcase_dir = Path(tmp_dir) | ||
|
|
||
| branches = { | ||
| "branch_alpha": "Branch in Damietta - Focus on high-volume furniture sales.", | ||
| "branch_beta": "Branch in Cairo - Focus on tech gadgets and accessories.", | ||
| "branch_gamma": "Branch in Alexandria - Focus on apparel and clothing." | ||
| } | ||
|
|
||
| for agent_id, context in branches.items(): | ||
| content = f"""--- | ||
| type: observation | ||
| title: {agent_id} Market Context | ||
| agent_id: {agent_id} | ||
| tags: [manzoma, retail, egypt] | ||
| timestamp: {datetime.now(timezone.utc).isoformat()} | ||
| --- | ||
| {context} | ||
| Inventory sync status: 100% Verified. | ||
| Total volume processed: $10,000. | ||
| """ | ||
| (showcase_dir / f"{agent_id}.md").write_text(content) | ||
|
|
||
| print(f"✅ Prepared OKF bundle with {len(branches)} agent-specific memories in temporary directory.") | ||
|
|
||
| # 2. Setup a robust mock client that simulates agent isolation | ||
| mock_client = MagicMock() | ||
| mock_client.agent_id = "initial_agent" | ||
|
|
||
| # Track "stored" memories per agent | ||
| storage = {agent_id: [] for agent_id in branches.keys()} | ||
|
|
||
| def mock_batch_remember(agent_id, memories): | ||
| if agent_id not in storage: | ||
| storage[agent_id] = [] | ||
| storage[agent_id].extend(memories) | ||
| return {"results": [], "successful": len(memories), "failed": 0} | ||
|
|
||
| def mock_activate_agent(agent_id): | ||
| print(f"🔄 Client: Activating session for agent '{agent_id}'") | ||
| mock_client.agent_id = agent_id | ||
| return {"status": "active", "agent_id": agent_id} | ||
|
|
||
| mock_client.batch_remember.side_effect = mock_batch_remember | ||
| mock_client.activate_agent.side_effect = mock_activate_agent | ||
|
|
||
| print("📦 Executing Multi-Agent Migration...") | ||
|
|
||
| # Load the bundle | ||
| bundle = load_okf_bundle(showcase_dir) | ||
|
|
||
| # Run the migration | ||
| summary, rows = run_migration( | ||
| provider="okf", | ||
| export=bundle, | ||
| client=mock_client, | ||
| agent_id="default_fallback", | ||
| dry_run=False, | ||
| on_progress=print | ||
| ) | ||
|
|
||
| # 3. Verification of Round-Trip Fidelity and Agent Isolation | ||
| print("\n--- 📊 Migration Verification ---") | ||
|
|
||
| all_passed = True | ||
| for agent_id, expected_context in branches.items(): | ||
| stored_memories = storage.get(agent_id, []) | ||
| if len(stored_memories) == 1: | ||
| stored_content = stored_memories[0]['content'] | ||
| if expected_context in stored_content: | ||
| print(f"✅ Agent '{agent_id}': Data verified and isolated.") | ||
| else: | ||
| print(f"❌ Agent '{agent_id}': Data corruption detected!") | ||
| all_passed = False | ||
| else: | ||
| print(f"❌ Agent '{agent_id}': Expected 1 memory, found {len(stored_memories)}.") | ||
| all_passed = False | ||
|
|
||
| if all_passed: | ||
| print("\n🏆 SHOWCASE SUCCESS: All Manzoma branches migrated with perfect isolation!") | ||
| print("Proof of the 'Freedom Loop': Data -> OKF -> Multi-Agent Memanto.") | ||
| else: | ||
| print("\n❌ SHOWCASE FAILED: Verification errors found.") | ||
|
|
||
| if __name__ == "__main__": | ||
| run_manzoma_freedom_showcase() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
|
|
||
| import pytest | ||
| from pathlib import Path | ||
| from memanto.cli.migrate.okf_loader import load_okf_bundle | ||
| from memanto.cli.migrate.mappers import map_okf | ||
| from memanto.cli.migrate.runner import run_migration | ||
| from unittest.mock import MagicMock | ||
|
|
||
| def test_okf_agent_id_preservation(tmp_path): | ||
| """Verify that agent_id is preserved through loader and mapper.""" | ||
| okf_content = """--- | ||
| type: fact | ||
| title: Multi-Agent Test | ||
| agent_id: agent_alpha | ||
| tags: [test] | ||
| --- | ||
| This is a memory for Alpha. | ||
| """ | ||
| test_file = tmp_path / "test_memory.md" | ||
| test_file.write_text(okf_content) | ||
|
|
||
| # 1. Test Loader | ||
| bundle = load_okf_bundle(test_file) | ||
| entry = bundle['memories'][0] | ||
| assert entry.get('agent_id') == 'agent_alpha' | ||
|
|
||
| # 2. Test Mapper | ||
| mapped = map_okf(bundle) | ||
| payload = mapped[0] | ||
| assert payload.get('agent_id') == 'agent_alpha' | ||
|
|
||
| def test_run_migration_multi_agent_grouping(): | ||
| """Verify that run_migration groups batches by agent_id correctly.""" | ||
| # Mock data with different agents | ||
| rows = [ | ||
| {"title": "M1", "content": "C1", "agent_id": "agent_1"}, | ||
| {"title": "M2", "content": "C2", "agent_id": "agent_1"}, | ||
| {"title": "M3", "content": "C3", "agent_id": "agent_2"}, | ||
| ] | ||
|
|
||
| mock_client = MagicMock() | ||
| mock_client.batch_remember.return_value = {"results": [], "successful": 1, "failed": 0} | ||
|
|
||
| # We need to mock map_export to return our custom rows | ||
| import memanto.cli.migrate.runner as runner | ||
| original_map_export = runner.map_export | ||
| runner.map_export = MagicMock(return_value=rows) | ||
|
|
||
| try: | ||
| summary, _ = run_migration( | ||
| provider="okf", | ||
| export={}, | ||
| client=mock_client, | ||
| agent_id="default_agent", # This should be overridden by row['agent_id'] | ||
| dry_run=False | ||
| ) | ||
|
|
||
| # Verify batch_remember was called twice (once per agent) | ||
| assert mock_client.batch_remember.call_count == 2 | ||
|
|
||
| # Verify first call was for agent_1 | ||
| args, kwargs = mock_client.batch_remember.call_args_list[0] | ||
| assert kwargs['agent_id'] == 'agent_1' | ||
| assert len(kwargs['memories']) == 2 | ||
|
|
||
| # Verify second call was for agent_2 | ||
| args, kwargs = mock_client.batch_remember.call_args_list[1] | ||
| assert kwargs['agent_id'] == 'agent_2' | ||
| assert len(kwargs['memories']) == 1 | ||
|
|
||
| finally: | ||
| runner.map_export = original_map_export |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.