feat: Aggiunge miglioramenti per memoria e pianificazione con integrazione MCP

- Aggiunto sistema SimplePersistentMemory per fatti, preferenze e cronologia conversazioni
- Aggiunto EnhancedPlanner con dipendenze task, priorità e tracciamento progresso
- Integrati strumenti di miglioramento nell'agente (remember, recall, preference, planning)
- Aggiunti comandi CLI /memory e /plan per accesso diretto da terminale
- Aggiunto client MCP per connessione a server Model Context Protocol
- Aggiornato README con documentazione completa per tutte le nuove funzionalità
- Corretto parsing comando creazione piano per gestire nomi multi-parola

Nuovi comandi:
- /memory remember <testo> - Ricorda un fatto
- /memory recall <query> - Cerca nella memoria
- /memory preference set/get - Gestisci preferenze
- /memory stats - Statistiche memoria
- /plan create <nome> <task1;task2;...> - Crea un piano
- /plan list - Elenca tutti i piani
- /plan progress <plan_id> - Mostra progresso piano
- /plan start <task_id> - Inizia un task
- /plan complete <task_id> - Completa un task
This commit is contained in:
opencode committed 2026-03-23 08:57:21 +01:00
1 parent 5a8fcda993
commit 56f2819a92
9 files changed
+1889 -1

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
# Enhancements for LangCode Agent based on LangChain Deep Agents analysis
This directory contains planned enhancements to bring LangCode Agent closer to the architecture patterns revealed in LangChain's Deep Agents framework.
## Planned Enhancements
1. **Enhanced Memory Systems**
- Add VectorStoreRetrieverMemory for semantic recall
- Implement SummaryBufferMemory for conversation summarization
- Add persistent memory storage for cross-session learning
2. **Advanced Planning & Reasoning**
- Implement plan-and-execute agent pattern
- Add self-reflection capabilities
- Improve task decomposition and tracking
3. **Production Observability**
- Add LangChain-style callbacks for tracing
- Implement tool usage monitoring
- Add performance metrics collection
4. **Advanced Tool Composition**
- Implement tool chaining capabilities
- Add async tool execution for parallelism
- Enhance input validation and error handling
5. **Agent Hierarchies**
- Develop specialist subagents for specific domains
- Implement agent routing based on task analysis
- Add agent-to-agent communication patterns
## Implementation Approach
Each enhancement will be implemented as a separate module that can be optionally integrated into the main agent, maintaining backward compatibility while extending capabilities.
The enhancements will follow the same patterns already established in the codebase:
- Modular design
- Configuration-driven behavior
- Clear separation of concerns
- Comprehensive error handling
+317
View File
@@ -0,0 +1,317 @@
"""
Enhanced memory systems for LangCode Agent
Implements advanced memory patterns from LangChain Deep Agents
"""
import os
import json
from typing import Dict, Any, List, Optional
# Try to import from langchain_core, fallback to community if needed
try:
from langchain_core.memory import BaseMemory
from langchain_core.vectorstores import VectorStore
from langchain_core.embeddings import Embeddings
from langchain_core.documents import Document
from langchain_core.storage import BaseStore
from langchain_core.retrievers import BaseRetriever
LANGCHAIN_CORE_AVAILABLE = True
except ImportError:
LANGCHAIN_CORE_AVAILABLE = False
# We'll handle this gracefully in the implementation
# Try to import OllamaEmbeddings
try:
from langchain_community.embeddings import OllamaEmbeddings
OLLAMA_AVAILABLE = True
except ImportError:
OLLAMA_AVAILABLE = False
OllamaEmbeddings = None # type: ignore
# Try to import FAISS for vector store
try:
from langchain_community.vectorstores import FAISS
FAISS_AVAILABLE = True
except ImportError:
FAISS_AVAILABLE = False
FAISS = None # type: ignore
# Try to import memory components
try:
from langchain_core.memory import VectorStoreRetrieverMemory, SummaryBufferMemory
MEMORY_COMPONENTS_AVAILABLE = True
except ImportError:
MEMORY_COMPONENTS_AVAILABLE = False
VectorStoreRetrieverMemory = None # type: ignore
SummaryBufferMemory = None # type: ignore
from langchain_core.tools import Tool
class EnhancedMemoryManager:
"""Manages enhanced memory systems for the LangCode Agent"""
def __init__(self,
embedding_model: str = "nomic-embed-text",
ollama_base_url: str = "http://localhost:11434",
persist_directory: str = "./.langcode/memory"):
"""
Initialize the enhanced memory manager
Args:
embedding_model: Name of the embedding model to use
ollama_base_url: URL for Ollama service
persist_directory: Directory to persist memory data
"""
self.embedding_model = embedding_model
self.ollama_base_url = ollama_base_url
self.persist_directory = persist_directory
# Ensure persist directory exists
os.makedirs(persist_directory, exist_ok=True)
# Initialize components if available
self.embeddings = None
self.vectorstore = None
self.summary_memory = None
self.vector_memory = None
self.docstore = None
self._initialize_memory_systems()
def _initialize_memory_systems(self):
"""Initialize all memory systems if dependencies are available"""
if not (OLLAMA_AVAILABLE and FAISS_AVAILABLE and MEMORY_COMPONENTS_AVAILABLE):
missing = []
if not OLLAMA_AVAILABLE:
missing.append("Ollama embeddings")
if not FAISS_AVAILABLE:
missing.append("FAISS vector store")
if not MEMORY_COMPONENTS_AVAILABLE:
missing.append("Memory components")
print(f"Warning: Enhanced memory features limited due to missing: {', '.join(missing)}")
return
try:
# Initialize embeddings
self.embeddings = OllamaEmbeddings(
model=self.embedding_model,
base_url=self.ollama_base_url
)
# Initialize docstore for storing documents
from langchain.storage import InMemoryStore
self.docstore = InMemoryStore()
# Initialize vector store for semantic memory
self.vectorstore = FAISS(
embedding_function=self.embeddings,
index=None,
docstore=self.docstore,
index_to_docstore_id={}
)
# Initialize vector store memory
self.vector_memory = VectorStoreRetrieverMemory(
retriever=self.vectorstore.as_retriever(search_kwargs={"k": 3})
)
# Initialize summary memory for conversation compression
self.summary_memory = SummaryBufferMemory(
max_token_limit=1000
)
except Exception as e:
print(f"Warning: Failed to initialize enhanced memory systems: {e}")
# Reset to None if initialization failed
self.embeddings = None
self.vectorstore = None
self.summary_memory = None
self.vector_memory = None
self.docstore = None
def set_llm_for_summary_memory(self, llm):
"""Set the LLM for summary memory"""
if self.summary_memory and hasattr(self.summary_memory, 'llm'):
self.summary_memory.llm = llm
def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, Any]) -> None:
"""
Save context to all memory systems
Args:
inputs: Input values from the conversation
outputs: Output values from the conversation
"""
try:
# Save to vector memory
if self.vector_memory:
self.vector_memory.save_context(inputs, outputs)
# Save to summary memory
if self.summary_memory:
self.summary_memory.save_context(inputs, outputs)
except Exception as e:
print(f"Warning: Failed to save context to memory: {e}")
def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
"""
Load memory variables from all memory systems
Args:
inputs: Input values for context retrieval
Returns:
Dictionary containing memory variables
"""
result = {}
try:
# Load from vector memory
if self.vector_memory:
vector_result = self.vector_memory.load_memory_variables(inputs)
result.update(vector_result)
# Load from summary memory
if self.summary_memory:
summary_result = self.summary_memory.load_memory_variables(inputs)
result.update(summary_result)
except Exception as e:
print(f"Warning: Failed to load memory variables: {e}")
return result
def add_document(self, content: str, metadata: Optional[Dict[str, Any]] = None) -> None:
"""
Add a document to the vector store for long-term memory
Args:
content: Document content to store
metadata: Optional metadata for the document
"""
if not self.vectorstore:
return
try:
doc = Document(page_content=content, metadata=metadata or {})
self.vectorstore.add_documents([doc])
except Exception as e:
print(f"Warning: Failed to add document to vector store: {e}")
def search_similar(self, query: str, k: int = 3) -> List[Document]:
"""
Search for similar documents in vector store
Args:
query: Search query
k: Number of results to return
Returns:
List of similar documents
"""
if not self.vectorstore:
return []
try:
return self.vectorstore.similarity_search(query, k=k)
except Exception as e:
print(f"Warning: Failed to search vector store: {e}")
return []
def clear(self) -> None:
"""Clear all memory systems"""
try:
if self.vector_memory:
self.vector_memory.clear()
if self.summary_memory:
self.summary_memory.clear()
# Reinitialize vector store
self._initialize_memory_systems()
except Exception as e:
print(f"Warning: Failed to clear memory systems: {e}")
def create_memory_tools(memory_manager: EnhancedMemoryManager) -> List[Tool]:
"""
Create LangChain tools for the enhanced memory system
Args:
memory_manager: Instance of EnhancedMemoryManager
Returns:
List of memory tools
"""
def add_memory_func(content: str, metadata: str = "{}") -> str:
try:
metadata_dict = json.loads(metadata) if metadata.strip() else {}
memory_manager.add_document(content, metadata_dict)
return f"Added content to memory: {content[:50]}{'...' if len(content) > 50 else ''}"
except Exception as e:
return f"Error adding to memory: {str(e)}"
def search_memory_func(query: str, k: str = "3") -> str:
try:
k_int = int(k)
results = memory_manager.search_similar(query, k_int)
if not results:
return "No similar memories found."
result = f"Found {len(results)} similar memories:\n"
for i, doc in enumerate(results, 1):
result += f"{i}. {doc.page_content[:100]}{'...' if len(doc.page_content) > 100 else ''}\n"
if doc.metadata:
result += f" Metadata: {doc.metadata}\n"
return result
except Exception as e:
return f"Error searching memory: {str(e)}"
def clear_memory_func() -> str:
try:
memory_manager.clear()
return "Memory cleared successfully."
except Exception as e:
return f"Error clearing memory: {str(e)}"
def get_memory_stats_func() -> str:
try:
stats = []
if memory_manager.vectorstore:
# Try to get vector count
try:
count = memory_manager.vectorstore.index.ntotal if hasattr(memory_manager.vectorstore.index, 'ntotal') else 'unknown'
stats.append(f"Vector store: {count} vectors")
except:
stats.append("Vector store: active")
if memory_manager.summary_memory:
stats.append("Summary memory: active")
if memory_manager.vector_memory:
stats.append("Vector memory: active")
return "Memory Systems:\n" + "\n".join(f" - {s}" for s in stats)
except Exception as e:
return f"Error getting memory stats: {str(e)}"
return [
Tool(
name="add_to_memory",
description="Add content to long-term memory. Provide content and optional metadata as JSON string.",
func=add_memory_func
),
Tool(
name="search_memory",
description="Search long-term memory for similar content. Provide query and optional k (number of results, default 3).",
func=search_memory_func
),
Tool(
name="clear_memory",
description="Clear all long-term memory.",
func=clear_memory_func
),
Tool(
name="memory_stats",
description="Get statistics about the memory systems.",
func=get_memory_stats_func
)
]
# Global instance for easy access
enhanced_memory_manager = EnhancedMemoryManager()
+364
View File
@@ -0,0 +1,364 @@
"""
Enhanced planning systems for LangCode Agent
Implements advanced planning patterns from LangChain Deep Agents
"""
import json
import uuid
from datetime import datetime
from typing import Dict, List, Any, Optional, Tuple
from enum import Enum
from dataclasses import dataclass, asdict, field
from langchain_core.tools import Tool
class TaskStatus(Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
CANCELLED = "cancelled"
FAILED = "failed"
class TaskPriority(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
URGENT = "urgent"
@dataclass
class Task:
"""Represents a task in the planning system"""
id: str
description: str
status: TaskStatus
priority: TaskPriority
created_at: datetime
dependencies: List[str] = field(default_factory=list) # List of task IDs that must be completed first
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
result: Optional[Any] = None
error: Optional[str] = None
subtasks: List[str] = field(default_factory=list) # List of subtask IDs
class EnhancedPlanner:
"""Enhanced planning system for LangCode Agent"""
def __init__(self):
self.tasks: Dict[str, Task] = {}
self.task_counter = 0
self.plans: Dict[str, List[str]] = {} # plan_id -> list of task IDs
def create_task(self,
description: str,
priority: TaskPriority = TaskPriority.MEDIUM,
dependencies: Optional[List[str]] = None) -> str:
"""
Create a new task
Args:
description: Task description
priority: Task priority level
dependencies: List of task IDs that must be completed first
Returns:
Task ID
"""
if dependencies is None:
dependencies = []
task_id = f"task_{self.task_counter:04d}"
self.task_counter += 1
task = Task(
id=task_id,
description=description,
status=TaskStatus.PENDING,
priority=priority,
dependencies=dependencies,
created_at=datetime.now()
)
self.tasks[task_id] = task
return task_id
def create_plan(self, name: str, task_descriptions: List[str]) -> str:
"""
Create a plan with multiple tasks
Args:
name: Plan name
task_descriptions: List of task descriptions
Returns:
Plan ID
"""
plan_id = f"plan_{len(self.plans):04d}"
task_ids = []
for desc in task_descriptions:
task_id = self.create_task(desc)
task_ids.append(task_id)
self.plans[plan_id] = task_ids
return plan_id
def start_task(self, task_id: str) -> bool:
"""
Start working on a task
Args:
task_id: ID of the task to start
Returns:
True if task started successfully, False otherwise
"""
if task_id not in self.tasks:
return False
task = self.tasks[task_id]
# Check if dependencies are completed
for dep_id in task.dependencies:
if dep_id not in self.tasks or self.tasks[dep_id].status != TaskStatus.COMPLETED:
return False # Dependencies not met
task.status = TaskStatus.IN_PROGRESS
task.started_at = datetime.now()
return True
def complete_task(self, task_id: str, result: Any = None) -> bool:
"""
Mark a task as completed
Args:
task_id: ID of the task to complete
result: Result of the task execution
Returns:
True if task completed successfully, False otherwise
"""
if task_id not in self.tasks:
return False
task = self.tasks[task_id]
if task.status != TaskStatus.IN_PROGRESS:
return False
task.status = TaskStatus.COMPLETED
task.completed_at = datetime.now()
task.result = result
return True
def fail_task(self, task_id: str, error: str) -> bool:
"""
Mark a task as failed
Args:
task_id: ID of the task to fail
error: Error message
Returns:
True if task marked as failed, False otherwise
"""
if task_id not in self.tasks:
return False
task = self.tasks[task_id]
if task.status != TaskStatus.IN_PROGRESS:
return False
task.status = TaskStatus.FAILED
task.completed_at = datetime.now()
task.error = error
return True
def get_ready_tasks(self) -> List[Task]:
"""
Get tasks that are ready to be started (dependencies met)
Returns:
List of ready tasks
"""
ready_tasks = []
for task in self.tasks.values():
if task.status == TaskStatus.PENDING:
# Check if all dependencies are completed
deps_met = True
for dep_id in task.dependencies:
if dep_id not in self.tasks or self.tasks[dep_id].status != TaskStatus.COMPLETED:
deps_met = False
break
if deps_met:
ready_tasks.append(task)
return ready_tasks
def get_task_status(self, task_id: str) -> Optional[TaskStatus]:
"""
Get the status of a task
Args:
task_id: ID of the task
Returns:
Task status or None if task not found
"""
if task_id in self.tasks:
return self.tasks[task_id].status
return None
def get_plan_progress(self, plan_id: str) -> Dict[str, Any]:
"""
Get progress information for a plan
Args:
plan_id: ID of the plan
Returns:
Dictionary with progress information
"""
if plan_id not in self.plans:
return {"error": "Plan not found"}
task_ids = self.plans[plan_id]
total_tasks = len(task_ids)
completed_tasks = 0
failed_tasks = 0
in_progress_tasks = 0
for task_id in task_ids:
if task_id in self.tasks:
task = self.tasks[task_id]
if task.status == TaskStatus.COMPLETED:
completed_tasks += 1
elif task.status == TaskStatus.FAILED:
failed_tasks += 1
elif task.status == TaskStatus.IN_PROGRESS:
in_progress_tasks += 1
return {
"plan_id": plan_id,
"total_tasks": total_tasks,
"completed_tasks": completed_tasks,
"failed_tasks": failed_tasks,
"in_progress_tasks": in_progress_tasks,
"pending_tasks": total_tasks - completed_tasks - failed_tasks - in_progress_tasks,
"progress_percentage": (completed_tasks / total_tasks * 100) if total_tasks > 0 else 0
}
def create_planning_tools(self) -> List[Tool]:
"""
Create LangChain tools for the planning system
Returns:
List of planning tools
"""
def create_task_func(description: str, priority: str = "medium", dependencies: str = "") -> str:
try:
priority_enum = TaskPriority(priority.lower())
dep_list = [d.strip() for d in dependencies.split(",") if d.strip()] if dependencies else []
task_id = self.create_task(description, priority_enum, dep_list)
return f"Created task '{description}' with ID: {task_id}"
except Exception as e:
return f"Error creating task: {str(e)}"
def start_task_func(task_id: str) -> str:
try:
if self.start_task(task_id):
return f"Started task {task_id}"
else:
return f"Failed to start task {task_id} - check dependencies or task ID"
except Exception as e:
return f"Error starting task: {str(e)}"
def complete_task_func(task_id: str, result: str = "") -> str:
try:
if self.complete_task(task_id, result if result else None):
return f"Completed task {task_id}"
else:
return f"Failed to complete task {task_id} - check task ID or status"
except Exception as e:
return f"Error completing task: {str(e)}"
def list_tasks_func(status: str = "") -> str:
try:
if status:
status_enum = TaskStatus(status.lower())
filtered_tasks = [t for t in self.tasks.values() if t.status == status_enum]
else:
filtered_tasks = list(self.tasks.values())
if not filtered_tasks:
return "No tasks found"
result = f"Tasks ({len(filtered_tasks)}):\n"
for task in sorted(filtered_tasks, key=lambda t: t.created_at):
result += f"- [{task.status.value}] {task.id}: {task.description}\n"
if task.dependencies:
result += f" Dependencies: {', '.join(task.dependencies)}\n"
return result
except Exception as e:
return f"Error listing tasks: {str(e)}"
def get_ready_tasks_func() -> str:
try:
ready_tasks = self.get_ready_tasks()
if not ready_tasks:
return "No tasks ready to start"
result = f"Ready tasks ({len(ready_tasks)}):\n"
for task in ready_tasks:
result += f"- {task.id}: {task.description}\n"
return result
except Exception as e:
return f"Error getting ready tasks: {str(e)}"
def get_plan_progress_func(plan_id: str) -> str:
try:
progress = self.get_plan_progress(plan_id)
if "error" in progress:
return progress["error"]
result = f"Plan {plan_id} Progress:\n"
result += f" Total: {progress['total_tasks']}\n"
result += f" Completed: {progress['completed_tasks']}\n"
result += f" Failed: {progress['failed_tasks']}\n"
result += f" In Progress: {progress['in_progress_tasks']}\n"
result += f" Pending: {progress['pending_tasks']}\n"
result += f" Progress: {progress['progress_percentage']:.1f}%\n"
return result
except Exception as e:
return f"Error getting plan progress: {str(e)}"
return [
Tool(
name="create_task",
description="Create a new task. Provide description, priority (low/medium/high/urgent), and optional dependencies (comma-separated task IDs)",
func=create_task_func
),
Tool(
name="start_task",
description="Start working on a task. Provide task ID",
func=start_task_func
),
Tool(
name="complete_task",
description="Mark a task as completed. Provide task ID and optional result",
func=complete_task_func
),
Tool(
name="list_tasks",
description="List all tasks. Optionally filter by status (pending, in_progress, completed, failed)",
func=list_tasks_func
),
Tool(
name="get_ready_tasks",
description="Get tasks that are ready to be started (dependencies met)",
func=get_ready_tasks_func
),
Tool(
name="get_plan_progress",
description="Get progress information for a plan. Provide plan ID",
func=get_plan_progress_func
)
]
# Global planner instance
enhanced_planner = EnhancedPlanner()
+266
View File
@@ -0,0 +1,266 @@
"""
Simple memory enhancement for LangCode Agent
Implements basic persistent memory without complex dependencies
"""
import json
import os
from typing import Dict, Any, List, Optional
from datetime import datetime
class SimplePersistentMemory:
"""Simple persistent memory system for LangCode Agent"""
def __init__(self, memory_file: str = "./.langcode/simple_memory.json"):
"""
Initialize the simple persistent memory
Args:
memory_file: Path to the memory file
"""
self.memory_file = memory_file
self.memory_data = {
"conversations": [],
"facts": [],
"preferences": {},
"skills_used": [],
"last_updated": None
}
# Ensure directory exists
os.makedirs(os.path.dirname(memory_file), exist_ok=True)
# Load existing memory
self.load()
def load(self) -> None:
"""Load memory from file"""
try:
if os.path.exists(self.memory_file):
with open(self.memory_file, 'r') as f:
self.memory_data = json.load(f)
except Exception as e:
print(f"Warning: Failed to load memory: {e}")
# Reset to default if load fails
self.memory_data = {
"conversations": [],
"facts": [],
"preferences": {},
"skills_used": [],
"last_updated": None
}
def save(self) -> None:
"""Save memory to file"""
try:
self.memory_data["last_updated"] = datetime.now().isoformat()
with open(self.memory_file, 'w') as f:
json.dump(self.memory_data, f, indent=2)
except Exception as e:
print(f"Warning: Failed to save memory: {e}")
def add_conversation(self, user_input: str, agent_response: str) -> None:
"""
Add a conversation exchange to memory
Args:
user_input: User's input
agent_response: Agent's response
"""
self.memory_data["conversations"].append({
"timestamp": datetime.now().isoformat(),
"user": user_input,
"agent": agent_response
})
# Keep only last 100 conversations to prevent file from growing too large
if len(self.memory_data["conversations"]) > 100:
self.memory_data["conversations"] = self.memory_data["conversations"][-100:]
self.save()
def add_fact(self, fact: str, category: str = "general") -> None:
"""
Add a fact to memory
Args:
fact: The fact to remember
category: Category for the fact
"""
self.memory_data["facts"].append({
"timestamp": datetime.now().isoformat(),
"fact": fact,
"category": category
})
# Keep only last 50 facts
if len(self.memory_data["facts"]) > 50:
self.memory_data["facts"] = self.memory_data["facts"][-50:]
self.save()
def set_preference(self, key: str, value: Any) -> None:
"""
Set a user preference
Args:
key: Preference key
value: Preference value
"""
self.memory_data["preferences"][key] = value
self.save()
def get_preference(self, key: str, default: Any = None) -> Any:
"""
Get a user preference
Args:
key: Preference key
default: Default value if key not found
Returns:
Preference value or default
"""
return self.memory_data["preferences"].get(key, default)
def add_skill_used(self, skill_name: str) -> None:
"""
Record that a skill was used
Args:
skill_name: Name of the skill used
"""
if skill_name not in self.memory_data["skills_used"]:
self.memory_data["skills_used"].append(skill_name)
self.save()
def get_recent_conversations(self, limit: int = 5) -> List[Dict[str, Any]]:
"""
Get recent conversations
Args:
limit: Maximum number of conversations to return
Returns:
List of recent conversations
"""
return self.memory_data["conversations"][-limit:] if self.memory_data["conversations"] else []
def search_facts(self, query: str) -> List[Dict[str, Any]]:
"""
Search facts by query
Args:
query: Search query
Returns:
List of matching facts
"""
query_lower = query.lower()
return [
fact for fact in self.memory_data["facts"]
if query_lower in fact["fact"].lower()
]
def get_memory_stats(self) -> Dict[str, Any]:
"""
Get memory statistics
Returns:
Dictionary with memory statistics
"""
return {
"total_conversations": len(self.memory_data["conversations"]),
"total_facts": len(self.memory_data["facts"]),
"total_preferences": len(self.memory_data["preferences"]),
"total_skills_used": len(self.memory_data["skills_used"]),
"last_updated": self.memory_data["last_updated"]
}
def create_simple_memory_tools(memory: SimplePersistentMemory) -> List:
"""
Create simple tools for the persistent memory system
Args:
memory: Instance of SimplePersistentMemory
Returns:
List of memory tools
"""
from langchain_core.tools import Tool
def remember_func(fact: str, category: str = "general") -> str:
try:
memory.add_fact(fact, category)
return f"Remembered: {fact}"
except Exception as e:
return f"Error remembering: {str(e)}"
def recall_func(query: str) -> str:
try:
results = memory.search_facts(query)
if not results:
return f"No memories found for: {query}"
result = f"Found {len(results)} memories:\n"
for i, fact in enumerate(results[:5], 1): # Limit to 5 results
result += f"{i}. {fact['fact']} ({fact['category']})\n"
return result
except Exception as e:
return f"Error recalling: {str(e)}"
def preference_func(action: str, key: str = "", value: str = "") -> str:
try:
if action.lower() == "set" and key and value:
# Try to parse value as JSON for complex types
try:
parsed_value = json.loads(value)
except:
parsed_value = value
memory.set_preference(key, parsed_value)
return f"Set preference '{key}' = {parsed_value}"
elif action.lower() == "get" and key:
val = memory.get_preference(key)
return f"Preference '{key}': {val}"
else:
return "Usage: preference set <key> <value> or preference get <key>"
except Exception as e:
return f"Error with preference: {str(e)}"
def stats_func() -> str:
try:
stats = memory.get_memory_stats()
result = "Memory Statistics:\n"
result += f" Conversations: {stats['total_conversations']}\n"
result += f" Facts: {stats['total_facts']}\n"
result += f" Preferences: {stats['total_preferences']}\n"
result += f" Skills Used: {stats['total_skills_used']}\n"
result += f" Last Updated: {stats['last_updated'] or 'Never'}\n"
return result
except Exception as e:
return f"Error getting memory stats: {str(e)}"
return [
Tool(
name="remember",
description="Remember a fact for future reference. Provide fact and optional category.",
func=remember_func
),
Tool(
name="recall",
description="Recall facts by searching memory. Provide search query.",
func=recall_func
),
Tool(
name="preference",
description="Get or set user preferences. Usage: preference set <key> <value> or preference get <key>",
func=preference_func
),
Tool(
name="memory_stats",
description="Get statistics about the memory system.",
func=stats_func
)
]
# Global instance
simple_memory = SimplePersistentMemory()
+193
View File
@@ -0,0 +1,193 @@
"""
Test integration of enhancements with LangCode Agent
"""
import sys
import os
sys.path.insert(0, '.')
def test_enhancements_import():
"""Test that our enhancements can be imported"""
print("Testing enhancement imports...")
try:
from enhancements.simple_memory import simple_memory, create_simple_memory_tools
print("✓ Simple memory enhancement imported successfully")
except Exception as e:
print(f"✗ Failed to import simple memory enhancement: {e}")
return False
try:
from enhancements.planning_enhancement import enhanced_planner
print("✓ Planning enhancement imported successfully")
except Exception as e:
print(f"✗ Failed to import planning enhancement: {e}")
return False
return True
def test_simple_memory():
"""Test simple memory functionality"""
print("\nTesting simple memory...")
try:
from enhancements.simple_memory import simple_memory
# Test adding a fact
simple_memory.add_fact("LangCode Agent supports MCP integration", "features")
print("✓ Added fact to memory")
# Test recalling
results = simple_memory.search_facts("MCP")
if results:
print(f"✓ Recalled {len(results)} facts about MCP")
else:
print("✗ Failed to recall facts about MCP")
return False
# Test preference
simple_memory.set_preference("model_provider", "ollama")
provider = simple_memory.get_preference("model_provider")
if provider == "ollama":
print("✓ Preference system works")
else:
print(f"✗ Preference system failed: got {provider}")
return False
# Test stats
stats = simple_memory.get_memory_stats()
if isinstance(stats, dict) and "total_facts" in stats:
print(f"✓ Memory stats work: {stats['total_facts']} facts")
else:
print("✗ Memory stats failed")
return False
except Exception as e:
print(f"✗ Simple memory test failed: {e}")
return False
return True
def test_planning():
"""Test planning functionality"""
print("\nTesting planning...")
try:
from enhancements.planning_enhancement import enhanced_planner, TaskPriority
# Test creating a task
task_id = enhanced_planner.create_task("Test task", TaskPriority.HIGH)
if task_id:
print(f"✓ Created task: {task_id}")
else:
print("✗ Failed to create task")
return False
# Test starting task
if enhanced_planner.start_task(task_id):
print("✓ Started task successfully")
else:
print("✗ Failed to start task")
return False
# Test completing task
if enhanced_planner.complete_task(task_id, "Task completed successfully"):
print("✓ Completed task successfully")
else:
print("✗ Failed to complete task")
return False
# Test listing tasks by getting the tool and calling it
planning_tools = enhanced_planner.create_planning_tools()
list_tasks_tool = None
for tool in planning_tools:
if tool.name == "list_tasks":
list_tasks_tool = tool
break
if list_tasks_tool and list_tasks_tool.func:
tasks_output = list_tasks_tool.func("")
if "Test task" in tasks_output and "completed" in tasks_output:
print("✓ Task listing works")
else:
print("✗ Task listing failed")
print(f" Output: {tasks_output}")
return False
else:
print("✗ Could not find or call list_tasks tool")
return False
except Exception as e:
print(f"✗ Planning test failed: {e}")
import traceback
traceback.print_exc()
return False
return True
def test_tool_creation():
"""Test that our enhancements create valid LangChain tools"""
print("\nTesting tool creation...")
try:
from enhancements.simple_memory import create_simple_memory_tools, simple_memory
from enhancements.planning_enhancement import enhanced_planner
# Test simple memory tools
memory_tools = create_simple_memory_tools(simple_memory)
if len(memory_tools) == 4:
print(f"✓ Simple memory tools created: {len(memory_tools)} tools")
tool_names = [tool.name for tool in memory_tools]
expected = ["remember", "recall", "preference", "memory_stats"]
if all(name in tool_names for name in expected):
print("✓ All expected memory tools present")
else:
print(f"✗ Missing some memory tools. Got: {tool_names}")
return False
else:
print(f"✗ Expected 4 memory tools, got {len(memory_tools)}")
return False
# Test planning tools
planning_tools = enhanced_planner.create_planning_tools()
if len(planning_tools) == 6:
print(f"✓ Planning tools created: {len(planning_tools)} tools")
tool_names = [tool.name for tool in planning_tools]
expected = ["create_task", "start_task", "complete_task", "list_tasks", "get_ready_tasks", "get_plan_progress"]
if all(name in tool_names for name in expected):
print("✓ All expected planning tools present")
else:
print(f"✗ Missing some planning tools. Got: {tool_names}")
return False
else:
print(f"✗ Expected 6 planning tools, got {len(planning_tools)}")
return False
except Exception as e:
print(f"✗ Tool creation test failed: {e}")
import traceback
traceback.print_exc()
return False
return True
def main():
"""Run all tests"""
print("=== LangCode Agent Enhancements Integration Test ===\n")
all_passed = True
all_passed &= test_enhancements_import()
all_passed &= test_simple_memory()
all_passed &= test_planning()
all_passed &= test_tool_creation()
print("\n=== Test Results ===")
if all_passed:
print("✓ All tests passed! Enhancements are ready for integration.")
return 0
else:
print("✗ Some tests failed. Please review the implementation.")
return 1
if __name__ == "__main__":
sys.exit(main())