Skip to content

Commit 195ca42

Browse files
committed
use WorktreeManager
1 parent f696030 commit 195ca42

2 files changed

Lines changed: 91 additions & 69 deletions

File tree

python/examples/langgraph_multi_agent_branching.py

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
from langgraph.store.base import BaseStore
7070

7171
# ProllyTree imports
72-
from prollytree import VersionedKvStore, ConflictResolution
72+
from prollytree import VersionedKvStore, ConflictResolution, WorktreeManager, WorktreeVersionedKvStore
7373

7474
# ============================================================================
7575
# Agent Types and Data Models
@@ -139,11 +139,11 @@ class MultiAgentState(MessagesState):
139139
# ============================================================================
140140

141141
class ProllyVersionedMemoryStore(BaseStore):
142-
"""VersionedKvStore-backed memory store with branch isolation for multi-agent systems.
142+
"""WorktreeManager-backed memory store with true parallel execution for multi-agent systems.
143143
144144
This store provides:
145145
1. Standard BaseStore interface for LangGraph integration
146-
2. Git-like branching for agent isolation using VersionedKvStore
146+
2. Git worktree isolation for true parallel agent execution
147147
3. Intelligent conflict resolution during merge operations
148148
4. Complete audit trail of all agent operations
149149
"""
@@ -175,11 +175,16 @@ def __init__(self, store_path: str):
175175

176176
# Agent branch tracking
177177
self.main_branch = "main"
178-
self.agent_stores = {} # agent_name -> VersionedKvStore (on their branch)
178+
# For WorktreeManager compatibility
179+
self.agent_worktrees = {} # agent_name -> WorktreeVersionedKvStore
180+
self.agent_stores = {} # agent_name -> VersionedKvStore (for backwards compatibility)
179181
self.agent_branches = {} # agent_name -> branch_name
182+
183+
# Initialize WorktreeManager for parallel execution
184+
self.worktree_manager = WorktreeManager(store_path)
180185
self.branch_metadata = {}
181186

182-
print(f"✅ Initialized VersionedKvStore-backed store at {store_path}")
187+
print(f"✅ Initialized WorktreeManager-backed store at {store_path}")
183188

184189
def _encode_value(self, value: Any) -> bytes:
185190
"""Encode any value to bytes for storage."""
@@ -296,16 +301,24 @@ def delete(self, namespace: tuple, key: str) -> None:
296301
print(f" ❌ Deleted: {full_key}")
297302

298303
# Branch management methods
299-
def create_agent_branch(self, agent_name: str, session_id: str) -> str:
300-
"""Create an isolated Git branch with dedicated VersionedKvStore for a specific agent"""
304+
def create_agent_worktree(self, agent_name: str, session_id: str) -> str:
305+
"""Create an isolated Git worktree with dedicated WorktreeVersionedKvStore for parallel execution"""
301306
branch_name = f"{session_id}-{agent_name}-{uuid.uuid4().hex[:8]}"
302307

303-
# Create Git branch using main VersionedKvStore
304-
self.main_store.create_branch(branch_name)
308+
# Create Git worktree using WorktreeManager
309+
worktree_path = os.path.join(self.store_path, f"{agent_name}_workspace")
310+
self.worktree_manager.add_worktree(str(worktree_path), branch_name, True)
311+
312+
# Create WorktreeVersionedKvStore for the agent
313+
agent_data_path = os.path.join(worktree_path, "data")
314+
os.makedirs(agent_data_path, exist_ok=True)
305315

306-
# Create dedicated VersionedKvStore instance for the agent
307-
agent_store = VersionedKvStore(self.data_dir)
308-
agent_store.checkout(branch_name)
316+
agent_worktree_store = WorktreeVersionedKvStore.from_worktree(
317+
str(worktree_path),
318+
f"worktree-{agent_name}",
319+
branch_name,
320+
self.worktree_manager
321+
)
309322

310323
# Store branch metadata
311324
self.branch_metadata[branch_name] = {
@@ -316,17 +329,18 @@ def create_agent_branch(self, agent_name: str, session_id: str) -> str:
316329
}
317330

318331
# Track agent mappings
319-
self.agent_stores[agent_name] = agent_store
332+
self.agent_worktrees[agent_name] = agent_worktree_store
320333
self.agent_branches[agent_name] = branch_name
321334

322-
# Store metadata in the agent's branch
335+
# Store metadata in the agent's worktree
323336
metadata_key = f"metadata:agent:{agent_name}".encode('utf-8')
324337
metadata_value = self._encode_value(self.branch_metadata[branch_name])
325-
agent_store.insert(metadata_key, metadata_value)
326-
agent_store.commit(f"Initialize {agent_name} agent branch with metadata")
338+
agent_worktree_store.insert(metadata_key, metadata_value)
339+
agent_worktree_store.commit(f"Initialize {agent_name} agent worktree with metadata")
327340

328-
print(f"🌿 Created Git branch '{branch_name}' with VersionedKvStore for {agent_name}")
329-
print(f" 📊 Agent store branch: {agent_store.current_branch()}")
341+
print(f"🌿 Created Git worktree '{branch_name}' with WorktreeVersionedKvStore for {agent_name}")
342+
print(f" 📁 Worktree path: {worktree_path}")
343+
print(f" 📊 Agent worktree branch: {agent_worktree_store.current_branch()}")
330344
return branch_name
331345

332346
def get_agent_store(self, agent_name: str) -> Optional[VersionedKvStore]:
@@ -339,29 +353,29 @@ def get_main_store(self) -> VersionedKvStore:
339353

340354
def store_agent_analysis(self, agent_name: str, analysis_type: str, data: Dict[str, Any]):
341355
"""Store agent analysis data using their dedicated VersionedKvStore"""
342-
if agent_name not in self.agent_stores:
343-
raise ValueError(f"No dedicated store exists for agent {agent_name}")
356+
if agent_name not in self.agent_worktrees:
357+
raise ValueError(f"No dedicated worktree exists for agent {agent_name}")
344358

345-
# Get the agent's VersionedKvStore instance
346-
agent_store = self.agent_stores[agent_name]
359+
# Get the agent's WorktreeVersionedKvStore instance
360+
agent_worktree = self.agent_worktrees[agent_name]
347361

348362
# Store analysis data directly in the agent's store
349363
full_key = f"analysis:{analysis_type}"
350364
key_bytes = full_key.encode('utf-8')
351365
value_bytes = self._encode_value(data)
352366

353367
# Check if key exists to decide between insert/update
354-
existing = agent_store.get(key_bytes)
368+
existing = agent_worktree.get(key_bytes)
355369
if existing:
356-
agent_store.update(key_bytes, value_bytes)
357-
print(f" 📝 {agent_name} updated: {full_key} using dedicated store")
370+
agent_worktree.update(key_bytes, value_bytes)
371+
print(f" 📝 {agent_name} updated: {full_key} using dedicated worktree")
358372
else:
359-
agent_store.insert(key_bytes, value_bytes)
360-
print(f" ➕ {agent_name} inserted: {full_key} using dedicated store")
373+
agent_worktree.insert(key_bytes, value_bytes)
374+
print(f" ➕ {agent_name} inserted: {full_key} using dedicated worktree")
361375

362-
# Commit using the agent's store
363-
agent_store.commit(f"{agent_name}: Stored {analysis_type}")
364-
print(f" 💾 {agent_name} committed: {analysis_type} on branch {agent_store.current_branch()}")
376+
# Commit using the agent's worktree
377+
agent_worktree.commit(f"{agent_name}: Stored {analysis_type}")
378+
print(f" 💾 {agent_name} committed: {analysis_type} on worktree {agent_worktree.current_branch()}")
365379

366380
def get_agent_analysis(self, agent_name: str, analysis_type: str) -> Optional[Dict[str, Any]]:
367381
"""Get agent analysis data using their dedicated VersionedKvStore"""
@@ -568,7 +582,7 @@ def troubleshooting_agent_node(state, store: ProllyVersionedMemoryStore):
568582
agent_name = "troubleshooting"
569583

570584
# Create isolated worktree if not exists
571-
if agent_name not in store.agent_stores:
585+
if agent_name not in store.agent_worktrees:
572586
branch_name = store.create_agent_worktree(agent_name, state["session_id"])
573587

574588
# Simulate agent analysis
@@ -611,7 +625,7 @@ def billing_agent_node(state, store: ProllyVersionedMemoryStore):
611625
agent_name = "billing"
612626

613627
# Create isolated worktree if not exists
614-
if agent_name not in store.agent_stores:
628+
if agent_name not in store.agent_worktrees:
615629
branch_name = store.create_agent_worktree(agent_name, state["session_id"])
616630

617631
customer = state["customer_context"]
@@ -670,7 +684,7 @@ def customer_history_agent_node(state, store: ProllyVersionedMemoryStore):
670684
agent_name = "customer_history"
671685

672686
# Create isolated worktree if not exists
673-
if agent_name not in store.agent_stores:
687+
if agent_name not in store.agent_worktrees:
674688
branch_name = store.create_agent_worktree(agent_name, state["session_id"])
675689

676690
customer = state["customer_context"]
@@ -1070,21 +1084,22 @@ def demonstrate_supervisor_pattern():
10701084
print(f"\n✅ LangGraph Supervisor Pattern:")
10711085
print(f" • Function-based nodes with proper state management")
10721086
print(f" • Conditional routing based on issue classification")
1073-
print(f" • VersionedKvStore-based ProllyVersionedMemoryStore as external long-term store")
1087+
print(f" • WorktreeManager-based ProllyVersionedMemoryStore as external long-term store")
10741088
print(f" • Supervisor validates and routes intelligently")
10751089

1076-
print(f"\nVersionedKvStore Integration:")
1090+
print(f"\nWorktreeManager Integration:")
10771091
print(f" • Proper LangGraph external store interface")
1078-
print(f" • Git-like branching for complete agent isolation")
1092+
print(f" • Git worktree isolation for true parallel agent execution")
1093+
print(f" • WorktreeVersionedKvStore instances for independent operation")
10791094
print(f" • Intelligent conflict resolution with multiple strategies")
1080-
print(f" • merge_ignore_conflicts and try_merge capabilities")
10811095
print(f" • Complete audit trail with versioned commits")
10821096

10831097
print(f"\n✅ Context Bleeding Prevention:")
1084-
print(f" • Each agent operates in isolated branch with dedicated VersionedKvStore")
1098+
print(f" • Each agent operates in isolated worktree with dedicated WorktreeVersionedKvStore")
1099+
print(f" • True parallel execution without conflicts or race conditions")
10851100
print(f" • No cross-contamination between agent domains")
10861101
print(f" • Domain validation prevents inappropriate recommendations")
1087-
print(f" • Shared long-term memory with complete branch-level isolation")
1102+
print(f" • Shared long-term memory with complete worktree-level isolation")
10881103

10891104
def main():
10901105
"""Run the LangGraph supervisor demonstration"""
@@ -1095,11 +1110,11 @@ def main():
10951110
print("="*80)
10961111

10971112
print("\n🎯 Key Features Demonstrated:")
1098-
print(" • LangGraph supervisor pattern with VersionedKvStore-backed storage")
1099-
print(" • Complete branch isolation using dedicated VersionedKvStore instances")
1113+
print(" • LangGraph supervisor pattern with WorktreeManager-backed storage")
1114+
print(" • Complete worktree isolation using WorktreeVersionedKvStore instances")
1115+
print(" • True parallel execution capability for multi-agent systems")
11001116
print(" • Intelligent conflict resolution with multiple strategies")
11011117
print(" • ConflictResolution enum for merge strategy selection")
1102-
print(" • merge_ignore_conflicts and try_merge for conflict handling")
11031118
print(" • Domain validation preventing context bleeding")
11041119
print(" • Complete Git audit trail of all agent activities")
11051120

@@ -1110,12 +1125,12 @@ def main():
11101125
print("✅ LangGraph Supervisor Demonstration Complete!")
11111126
print("="*80)
11121127
print("\nKey Architectural Patterns Shown:")
1113-
print(" 1. LangGraph supervisor with VersionedKvStore for intelligent delegation")
1114-
print(" 2. Complete branch isolation prevents context bleeding")
1115-
print(" 3. Multi-strategy conflict resolution (ignore_conflicts, try_merge)")
1116-
print(" 4. Dedicated VersionedKvStore instances for agent-specific operations")
1128+
print(" 1. LangGraph supervisor with WorktreeManager for intelligent delegation")
1129+
print(" 2. Complete worktree isolation prevents context bleeding and enables parallelism")
1130+
print(" 3. WorktreeVersionedKvStore instances for true parallel agent execution")
1131+
print(" 4. Multi-strategy conflict resolution (ignore_conflicts, try_merge)")
11171132
print(" 5. Domain validation ensures appropriate recommendations")
1118-
print(" 6. Git branch history provides complete audit trail")
1133+
print(" 6. Git worktree history provides complete audit trail")
11191134
print(" 7. Intelligent merge operations with conflict detection")
11201135

11211136
except ImportError as e:

python/prollytree/__init__.py

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,37 @@
2929
)
3030

3131
# Try to import SQL functionality if available
32+
sql_available = False
3233
try:
3334
from .prollytree import ProllySQLStore
34-
__all__ = [
35-
"ProllyTree",
36-
"TreeConfig",
37-
"AgentMemorySystem",
38-
"MemoryType",
39-
"VersionedKvStore",
40-
"StorageBackend",
41-
"MergeConflict",
42-
"ConflictResolution",
43-
"ProllySQLStore"
44-
]
35+
sql_available = True
4536
except ImportError:
46-
# SQL feature not available
47-
__all__ = [
48-
"ProllyTree",
49-
"TreeConfig",
50-
"AgentMemorySystem",
51-
"MemoryType",
52-
"VersionedKvStore",
53-
"StorageBackend",
54-
"MergeConflict",
55-
"ConflictResolution"
56-
]
37+
pass
38+
39+
# Try to import Git functionality if available
40+
git_available = False
41+
try:
42+
from .prollytree import WorktreeManager, WorktreeVersionedKvStore
43+
git_available = True
44+
except ImportError:
45+
pass
46+
47+
# Build __all__ based on available features
48+
__all__ = [
49+
"ProllyTree",
50+
"TreeConfig",
51+
"AgentMemorySystem",
52+
"MemoryType",
53+
"VersionedKvStore",
54+
"StorageBackend",
55+
"MergeConflict",
56+
"ConflictResolution"
57+
]
58+
59+
if sql_available:
60+
__all__.append("ProllySQLStore")
61+
62+
if git_available:
63+
__all__.extend(["WorktreeManager", "WorktreeVersionedKvStore"])
5764

5865
__version__ = "0.2.1"

0 commit comments

Comments
 (0)