diff --git a/.langcode/simple_memory.json b/.langcode/simple_memory.json new file mode 100644 index 0000000..f7e07ba --- /dev/null +++ b/.langcode/simple_memory.json @@ -0,0 +1,50 @@ +{ + "conversations": [], + "facts": [ + { + "timestamp": "2026-03-22T20:18:14.550293", + "fact": "LangCode Agent supports MCP integration", + "category": "features" + }, + { + "timestamp": "2026-03-22T20:27:22.386697", + "fact": "LangCode Agent supports MCP integration", + "category": "features" + }, + { + "timestamp": "2026-03-22T21:16:16.644866", + "fact": "\"LangCode Agent supports MCP integration\"", + "category": "general" + }, + { + "timestamp": "2026-03-22T21:19:31.649087", + "fact": "\"LangCode Agent supports MCP integration\"", + "category": "general" + }, + { + "timestamp": "2026-03-22T21:25:33.013155", + "fact": "\"LangCode Agent supports MCP integration\"", + "category": "general" + }, + { + "timestamp": "2026-03-22T21:30:17.895357", + "fact": "\"LangCode Agent supports MCP integration\"", + "category": "general" + }, + { + "timestamp": "2026-03-22T22:34:46.997791", + "fact": "\"LangCode Agent supports MCP integration\"", + "category": "general" + }, + { + "timestamp": "2026-03-23T07:14:33.532609", + "fact": "\"LangCode Agent supports MCP integration\"", + "category": "general" + } + ], + "preferences": { + "model_provider": "ollama" + }, + "skills_used": [], + "last_updated": "2026-03-23T07:14:33.532819" +} \ No newline at end of file diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..7d5db7c --- /dev/null +++ b/IMPLEMENTATION_PLAN.md @@ -0,0 +1,199 @@ +# LangCode Agent Enhancement Implementation Plan +Based on LangChain Deep Agents Architecture Analysis + +## Overview + +This plan outlines the integration of enhancements inspired by LangChain's Deep Agents architecture into the LangCode Agent. The enhancements focus on: + +1. **Advanced Memory Systems** - Persistent, semantic, and summary-based memory +2. **Enhanced Planning & Reasoning** - Sophisticated task planning and execution tracking +3. **Production Observability** - Monitoring, tracing, and debugging capabilities +4. **Agent Hierarchies** - Specialist agents and routing capabilities + +## Phase 1: Foundation Enhancements (Immediate Integration) + +### 1.1 Simple Persistent Memory System +**File:** `enhancements/simple_memory.py` +**Status:** Implemented and tested +**Integration Points:** +- Import in `main.py`: `from enhancements.simple_memory import simple_memory, create_simple_memory_tools` +- Add to `get_custom_tools()`: Extend with memory tools +- Update system prompt to mention memory capabilities +- Add CLI commands: `/memory remember`, `/memory recall`, `/memory preference`, `/memory stats` + +### 1.2 Enhanced Planning System +**File:** `enhancements/planning_enhancement.py` +**Status:** Implemented and tested +**Integration Points:** +- Import in `main.py`: `from enhancements.planning_enhancement import enhanced_planner` +- Add to `get_custom_tools()`: Extend with planning tools +- Update system prompt to mention planning capabilities +- Enhance existing `/todo` commands or add `/plan` subcommands + +## Phase 2: Advanced Features (Next Iteration) + +### 2.1 Advanced Memory with Vector Storage +**File:** `enhancements/memory_enhancement.py` +**Dependencies:** +- `langchain-community` for OllamaEmbeddings and FAISS +- `sentence-transformers` for embeddings +**Integration Points:** +- Similar to simple memory but with semantic search capabilities +- Add background processing for conversation summarization +- Implement memory consolidation during idle periods + +### 2.2 Agent Hierarchies and Routing +**New File:** `enhancements/agent_hierarchy.py` +**Features:** +- Specialist agents for different domains (coding, research, system admin) +- Intelligent routing based on task analysis +- Agent-to-agent communication patterns +- Load balancing and specialization + +### 2.3 Production Observability +**New File:** `enhancements/observability.py` +**Features:** +- LangChain-style callbacks for tracing +- Tool usage monitoring and metrics +- Performance profiling +- Error tracking and alerting +- Integration with LangSmith or custom monitoring + +## Phase 3: Deep Agents Alignment (Future Work) + +### 3.1 Context Management Enhancements +- Implement automatic summarization at 85% context usage +- Add file-based storage for large tool outputs +- Implement pointer-based context management +- Add context compression techniques + +### 3.2 Subagent Spawning Improvements +- Enhance the `task` tool with better isolation +- Add per-subagent model configuration +- Implement subagent pooling for efficiency +- Add inter-subagent communication mechanisms + +### 3.3 Advanced Planning Features +- Implement plan-and-execute agent pattern +- Add self-reflection and critique mechanisms +- Enable dynamic replanning based on execution results +- Add contingency planning and error recovery + +## Integration Strategy + +### Step-by-Step Integration: + +1. **Start with Simple Enhancements** (Phase 1) + - Integrate `simple_memory.py` and `planning_enhancement.py` + - Test thoroughly with existing functionality + - Gather user feedback + +2. **Update Documentation** + - Update `docs/plans/` with enhancement designs + - Add usage examples to README + - Create tutorial examples + +3. **Gradual Rollout of Advanced Features** (Phase 2) + - Add one enhancement at a time + - Maintain backward compatibility + - Provide migration paths + +4. **Performance Optimization** + - Profile and optimize memory usage + - Implement caching where appropriate + - Add configuration options to enable/disable features + +## CLI Integration Examples + +### Memory Commands: +``` +/memory remember "LangCode uses Ollama for local models" --category architecture +/memory recall "Ollama" +/memory preference set model_provider ollama +/memory preference get model_provider +/memory stats +``` + +### Planning Commands: +``` +/plan create "Implement user authentication" --tasks "Design DB schema" "Create API endpoints" "Write frontend" "Add tests" +/plan start auth-plan +/plan list +/plan progress auth-plan +/task list +/task start task_0000 +/task complete task_0000 "Authentication system implemented" +``` + +## Configuration Options + +Add to configuration system: +```json +{ + "enhancements": { + "memory": { + "type": "simple|advanced", + "persist_directory": "./.langcode/memory", + "max_conversations": 100, + "max_facts": 50 + }, + "planning": { + "enabled": true, + "auto_create_todos": true, + "dependency_tracking": true + }, + "observability": { + "enabled": false, + "trace_level": "info", + "metrics_endpoint": "http://localhost:9090/metrics" + } + } +} +``` + +## Backward Compatibility Strategy + +All enhancements are designed to be: +- **Optional**: Can be enabled/disabled via configuration +- **Additive**: Don't modify existing core functionality +- **Testable**: Include comprehensive test suites +- **Documented**: Clear usage instructions and examples + +## Risk Mitigation + +1. **Dependency Management**: + - Enhancements with heavy dependencies are isolated + - Fallback to simpler implementations when deps missing + - Clear error messages when features unavailable + +2. **Performance Impact**: + - Lazy initialization of heavy components + - Configurable feature flags + - Performance monitoring built-in + +3. **Data Safety**: + - Memory data stored locally by default + - No automatic data transmission + - User control over what gets remembered + +## Success Metrics + +After implementation, measure: +- User adoption of new features +- Reduction in repeated context requests +- Improvement in complex task completion rates +- User satisfaction scores +- System performance impact + +## Next Steps + +1. Review and approve this implementation plan +2. Begin Phase 1 integration (simple memory and planning) +3. Conduct internal testing +4. Gather feedback from pilot users +5. Iterate based on feedback +6. Proceed to Phase 2 enhancements + +--- + +*This plan brings LangCode Agent closer to the production-grade architecture patterns revealed in LangChain's Deep Agents while maintaining the project's core strengths in configurability and modularity.* \ No newline at end of file diff --git a/README.md b/README.md index f8ff3bf..3daf8a8 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Un agente configurabile basato su DeepAgents che supporta più provider LLM. - **Marketplace plugin**: Scopri e installa plugin dal marketplace - **Analytics avanzate**: Analisi dettagliate dell'uso e raccomandazioni - **Comandi integrati**: Comandi speciali con prefisso "/" per gestione rapida +- **MCP Integration**: Connect to MCP servers for external services and tools +- **Skill Management**: Discover, install, and manage agent skills from skills.sh registry +- **Memory Enhancement**: Sistema di memoria persistente per fatti, preferenze e cronologia conversazioni +- **Planning Enhancement**: Pianificazione avanzata dei task con dipendenze, tracciamento progresso e monitoraggio stato - **Monitoraggio**: Sistema di logging e monitoraggio delle performance - **Dashboard performance**: Monitoraggio dettagliato delle performance - **Ottimizzazioni performance**: Caching, connection pooling, lazy loading @@ -235,6 +239,23 @@ langcode analytics # Riepilogo rapido langcode analytics --report # Report completo langcode dashboard # Dashboard performance +# Skills management +langcode skills list # Lista skills installate +langcode skills search "coding" # Cerca skills +langcode skills install "skill-name" # Installa skill +langcode skills details "skill-name" # Dettagli skill + +# Memory +langcode memory remember "fact" # Ricorda un fatto +langcode memory recall "query" # Cerca fatti +langcode memory preference set k v # Imposta preferenza +langcode memory stats # Statistiche + +# Planning +langcode plan create "name" "t1;t2" # Crea piano +langcode plan list # Lista piani +langcode plan progress plan_id # Progresso piano + # Info langcode version langcode --help @@ -266,6 +287,19 @@ Quando interagisci con l'agente, puoi usare comandi speciali preceduti da "/" pe /language - Cambia la lingua dell'interfaccia /performance - Mostra il report delle performance /monitoring - Apre la dashboard di monitoraggio +/skills list - Elenca skills installate +/skills search - Cerca skills nel registry +/skills install - Installa una skill +/skills details - Mostra dettagli di una skill +/memory remember - Ricorda un fatto +/memory recall - Cerca fatti nella memoria +/memory preference - Gestisci preferenze +/memory stats - Statistiche memoria +/plan create - Crea un piano +/plan list - Lista tutti i piani +/plan progress - Mostra progresso piano +/plan start - Inizia un task +/plan complete - Completa un task /exit, /quit - Esci dall'agente ``` @@ -989,6 +1023,174 @@ I dati di performance vengono salvati in `~/.langcode/performance/` e includono: - Utilizzo provider - Statistiche di sistema +## MCP Integration + +LangCode Agent supports the Model Context Protocol (MCP) for connecting to external services and tools. + +### Configurazione MCP + +Per configurare un server MCP: + +```bash +langcode config add -n my-mcp -p mcp -u http://localhost:8080 --api-key your-key +``` + +### Utilizzo MCP + +Una volta configurato, gli strumenti MCP sono automaticamente disponibili nell'agente. + +### Strumenti MCP Disponibili + +- Gli strumenti variano in base al server MCP configurato +- Consulta la documentazione del server MCP per gli strumenti specifici + +## Skill Management + +LangCode Agent può scoprire, installare e gestire skills dal registry skills.sh. + +### Cercare Skills + +```bash +# Cerca skills per parola chiave +langcode skills search "coding" +/skills search "coding" +``` + +### Installare Skills + +```bash +# Installa una skill +langcode skills install "code-review-expert" +/skills install "code-review-expert" +``` + +### Elencare Skills Installate + +```bash +# Lista tutte le skills installate +langcode skills list +/skills list +``` + +### Dettagli Skill + +```bash +# Mostra dettagli di una skill +langcode skills details "code-review-expert" +/skills details "code-review-expert" +``` + +### Disinstallare Skills + +```bash +# Disinstalla una skill +langcode skills uninstall "code-review-expert" +/skills uninstall "code-review-expert" +``` + +### Strumenti Skills nell'Agente + +L'agente ha accesso a strumenti per: +- `search_skills`: Cerca skills nel registry +- `install_skill`: Installa una skill +- `uninstall_skill`: Disinstalla una skill +- `list_installed_skills`: Lista skills installate +- `get_skill_details`: Ottieni dettagli di una skill + +## Memory Enhancement + +LangCode Agent include un sistema di memoria persistente per ricordare fatti, preferenze e cronologia delle conversazioni. + +### Comandi Memory + +```bash +# Ricorda un fatto +/memory remember Il mio colore preferito è il blu + +# Cerca fatti nella memoria +/memory recall colore + +# Imposta una preferenza +/memory preference set tema "dark mode" + +/memory preference set lingua "italiano" + +# Ottieni una preferenza +/memory preference get tema + +# Statistiche memoria +/memory stats +``` + +### Strumenti Memory nell'Agente + +L'agente ha accesso a strumenti per: +- `remember`: Ricorda un fatto +- `recall`: Cerca fatti nella memoria +- `preference`: Gestisci preferenze utente +- `memory_stats`: Ottieni statistiche della memoria + +### Come Funziona + +La memoria persistente: +- Salva automaticamente le conversazioni +- Permette di aggiungere fatti categorizzati +- Mantiene le preferenze dell'utente +- Traccia le skills utilizzate +- Persiste tra le sessioni + +## Planning Enhancement + +LangCode Agent include un sistema avanzato di pianificazione con dipendenze, tracciamento del progresso e monitoraggio dello stato. + +### Creare un Piano + +```bash +# Crea un piano con più task +/plan create "My Project" "Task 1; Task 2; Task 3" +``` + +### Elencare Piani + +```bash +# Lista tutti i piani +/plan list +``` + +### Progresso del Piano + +```bash +# Mostra progresso di un piano +/plan progress plan_0000 +``` + +### Gestire Task + +```bash +# Inizia un task +/plan start task_0000 + +# Completa un task +/plan complete task_0000 +``` + +### Strumenti Planning nell'Agente + +L'agente ha accesso a strumenti per: +- `create_task`: Crea un nuovo task +- `start_task`: Inizia a lavorare su un task +- `complete_task`: Marca un task come completato +- `list_tasks`: Lista tutti i task +- `get_ready_tasks`: Ottieni task pronti per essere iniziati +- `get_plan_progress`: Ottieni progresso di un piano + +### Caratteristiche Avanzate + +- **Priorità**: low, medium, high, urgent +- **Dipendenze**: I task possono dipendere da altri task +- **Stati**: pending, in_progress, completed, failed, cancelled +- **Progresso**: Calcolo automatico della percentuale di completamento + ## Test Esegui la suite di test: diff --git a/enhancements/README.md b/enhancements/README.md new file mode 100644 index 0000000..82b787b --- /dev/null +++ b/enhancements/README.md @@ -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 \ No newline at end of file diff --git a/enhancements/memory_enhancement.py b/enhancements/memory_enhancement.py new file mode 100644 index 0000000..c84bd71 --- /dev/null +++ b/enhancements/memory_enhancement.py @@ -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() \ No newline at end of file diff --git a/enhancements/planning_enhancement.py b/enhancements/planning_enhancement.py new file mode 100644 index 0000000..13c12d5 --- /dev/null +++ b/enhancements/planning_enhancement.py @@ -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() \ No newline at end of file diff --git a/enhancements/simple_memory.py b/enhancements/simple_memory.py new file mode 100644 index 0000000..3996a76 --- /dev/null +++ b/enhancements/simple_memory.py @@ -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 or preference get " + 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 or preference get ", + func=preference_func + ), + Tool( + name="memory_stats", + description="Get statistics about the memory system.", + func=stats_func + ) + ] + +# Global instance +simple_memory = SimplePersistentMemory() \ No newline at end of file diff --git a/enhancements/test_integration.py b/enhancements/test_integration.py new file mode 100644 index 0000000..b522dfc --- /dev/null +++ b/enhancements/test_integration.py @@ -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()) \ No newline at end of file diff --git a/main.py b/main.py index bec2dd2..5a4c31d 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ Enhanced with unified provider tools and custom tools """ import os import sys +import json from typing import Optional, List, Dict, Any from deepagents import create_deep_agent from langchain.chat_models import init_chat_model @@ -37,6 +38,18 @@ except ImportError: SKILL_MANAGER_AVAILABLE = False print("Warning: Skill manager not available.") +# Import enhancement modules +try: + from enhancements.simple_memory import simple_memory, create_simple_memory_tools + from enhancements.planning_enhancement import enhanced_planner + ENHANCEMENTS_AVAILABLE = True +except ImportError: + ENHANCEMENTS_AVAILABLE = False + print("Warning: Enhancement modules not available.") + +# Import json for memory preference handling +import json + def load_model_from_config(): """ @@ -298,6 +311,23 @@ def get_custom_tools() -> List[Tool]: except Exception as e: print(f"⚠️ Could not load skill tools: {e}") + # Add enhancement tools + try: + if ENHANCEMENTS_AVAILABLE: + # Add simple memory tools + memory_tools = create_simple_memory_tools(simple_memory) + custom_tools.extend(memory_tools) + print(f"✅ Added {len(memory_tools)} simple memory tools") + + # Add planning tools + planning_tools = enhanced_planner.create_planning_tools() + custom_tools.extend(planning_tools) + print(f"✅ Added {len(planning_tools)} planning tools") + else: + print("⚠️ Enhancement modules not available, skipping enhancement tools") + except Exception as e: + print(f"⚠️ Could not load enhancement tools: {e}") + return custom_tools @@ -315,7 +345,8 @@ def create_langcode_agent(system_prompt: Optional[str] = None): 2. Custom tools for managing Proxmox virtual machines 3. MCP tools for accessing external services and skills registries (like skills.sh) 4. Skill management tools for discovering, installing, and managing agent skills - 5. Standard DeepAgents tools (planning, filesystem, shell, subagents) + 5. Enhancement tools for persistent memory and advanced planning + 6. Standard DeepAgents tools (planning, filesystem, shell, subagents) Use these tools when asked about: - LLM provider management (listing models, testing connections, setting defaults) @@ -614,6 +645,173 @@ def skills_details(skill_name: str): print(f"Error getting skill details: {str(e)}") +def memory_remember(text: str): + """Remember a fact for future reference""" + from enhancements.simple_memory import simple_memory + try: + # Default category is "general" + simple_memory.add_fact(text, "general") + print(f"✅ Remembered: {text[:100]}{'...' if len(text) > 100 else ''}") + except Exception as e: + print(f"❌ Error remembering: {e}") + +def memory_recall(query: str): + """Recall facts by searching memory""" + from enhancements.simple_memory import simple_memory + try: + results = simple_memory.search_facts(query) + if not results: + print(f"No memories found for: {query}") + return + + print(f"\nFound {len(results)} memories:") + print("=" * 50) + for i, fact in enumerate(results[:10], 1): # Limit to 10 results + print(f"{i}. {fact['fact']}") + print(f" Category: {fact['category']}") + print(f" Time: {fact['timestamp']}") + print() + except Exception as e: + print(f"Error recalling: {e}") + +def memory_set_preference(key: str, value: str): + """Set a user preference""" + from enhancements.simple_memory import simple_memory + try: + # Try to parse value as JSON for complex types + try: + parsed_val = json.loads(value) + except: + parsed_val = value + simple_memory.set_preference(key, parsed_val) + print(f"✅ Set preference '{key}' = {parsed_val}") + except Exception as e: + print(f"Error setting preference: {e}") + +def memory_preference_get(key: str): + """Get a user preference""" + from enhancements.simple_memory import simple_memory + try: + val = simple_memory.get_preference(key) + if val is not None: + print(f"Preference '{key}': {val}") + else: + print(f"Preference '{key}' not found") + except Exception as e: + print(f"Error getting preference: {e}") + +def memory_stats(): + """Get memory statistics""" + from enhancements.simple_memory import simple_memory + try: + stats = simple_memory.get_memory_stats() + print("\nMemory Statistics:") + print("=" * 30) + print(f"Total Conversations: {stats['total_conversations']}") + print(f"Total Facts: {stats['total_facts']}") + print(f"Total Preferences: {stats['total_preferences']}") + print(f"Total Skills Used: {stats['total_skills_used']}") + print(f"Last Updated: {stats['last_updated'] or 'Never'}") + except Exception as e: + print(f"Error getting memory stats: {e}") + +def _strip_quotes(s: str) -> str: + """Strip surrounding quotes from a string""" + s = s.strip() + if len(s) >= 2: + if (s.startswith('"') and s.endswith('"')) or (s.startswith("'") and s.endswith("'")): + return s[1:-1] + return s + +def plan_create(name: str, tasks_string: str): + """Create a plan with multiple tasks""" + from enhancements.planning_enhancement import enhanced_planner + try: + # Strip quotes from name + name = _strip_quotes(name) + # Split tasks by semicolon and clean up + task_descriptions = [_strip_quotes(t.strip()) for t in tasks_string.split(';') if t.strip()] + if not task_descriptions: + print("❌ No tasks provided") + return + + plan_id = enhanced_planner.create_plan(name, task_descriptions) + print(f"✅ Created plan '{name}' with ID: {plan_id}") + print(f" Tasks: {len(task_descriptions)}") + for i, desc in enumerate(task_descriptions, 1): + print(f" {i}. {desc}") + except Exception as e: + print(f"❌ Error creating plan: {e}") + +def plan_list(): + """List all plans""" + from enhancements.planning_enhancement import enhanced_planner + try: + if not enhanced_planner.plans: + print("No plans created yet") + return + + print(f"\nPlans ({len(enhanced_planner.plans)}):") + print("=" * 50) + for plan_id, task_ids in enhanced_planner.plans.items(): + task_count = len(task_ids) + print(f"Plan ID: {plan_id}") + print(f" Tasks: {task_count}") + # Show first few tasks + for i, task_id in enumerate(task_ids[:3], 1): + if task_id in enhanced_planner.tasks: + task = enhanced_planner.tasks[task_id] + print(f" {i}. [{task.status.value}] {task.description}") + else: + print(f" {i}. [unknown] Task {task_id} not found") + if len(task_ids) > 3: + print(f" ... and {len(task_ids) - 3} more tasks") + print() + except Exception as e: + print(f"Error listing plans: {e}") + +def plan_progress(plan_id: str): + """Get progress information for a plan""" + from enhancements.planning_enhancement import enhanced_planner + try: + progress = enhanced_planner.get_plan_progress(plan_id) + if "error" in progress: + print(f"❌ {progress['error']}") + return + + print(f"\nPlan {plan_id} Progress:") + print("=" * 40) + print(f"Total Tasks: {progress['total_tasks']}") + print(f"Completed: {progress['completed_tasks']}") + print(f"Failed: {progress['failed_tasks']}") + print(f"In Progress: {progress['in_progress_tasks']}") + print(f"Pending: {progress['pending_tasks']}") + print(f"Progress: {progress['progress_percentage']:.1f}%") + except Exception as e: + print(f"Error getting plan progress: {e}") + +def plan_start(task_id: str): + """Start working on a task""" + from enhancements.planning_enhancement import enhanced_planner + try: + if enhanced_planner.start_task(task_id): + print(f"✅ Started task {task_id}") + else: + print(f"❌ Failed to start task {task_id} - check dependencies or task ID") + except Exception as e: + print(f"Error starting task: {e}") + +def plan_complete(task_id: str): + """Mark a task as completed""" + from enhancements.planning_enhancement import enhanced_planner + try: + if enhanced_planner.complete_task(task_id): + print(f"✅ Completed task {task_id}") + else: + print(f"❌ Failed to complete task {task_id} - check task ID or status") + except Exception as e: + print(f"Error completing task: {e}") + def process_command(user_input: str) -> bool: """ Process a command starting with "/" @@ -665,6 +863,65 @@ def process_command(user_input: str) -> bool: else: print("❌ Sottocomando sconosciuto o argomenti mancanti") print("Sottocomandi disponibili: search , install , uninstall , list, details ") + elif command == 'memory': + if not args: + print("❌ Specifica un sottocomando: remember, recall, preference, stats") + elif args[0] == 'remember' and len(args) > 1: + memory_remember(' '.join(args[1:])) + elif args[0] == 'recall' and len(args) > 1: + memory_recall(' '.join(args[1:])) + elif args[0] == 'preference': + if len(args) >= 4 and args[1] == 'set': + # Join everything after 'set' as the value (key is args[2], value is args[3:]) + key = args[2] + value = ' '.join(args[3:]) + memory_set_preference(key, value) + elif len(args) == 3 and args[1] == 'get': + key = args[2] + memory_preference_get(key) + else: + print("❌ Usage: preference set or preference get ") + elif args[0] == 'stats': + memory_stats() + else: + print("❌ Sottocomando sconosciuto o argomenti mancanti") + print("Sottocomandi disponibili: remember , recall , preference set , preference get , stats") + elif command == 'plan': + if not args: + print("❌ Specifica un sottocomando: create, list, progress, start, complete") + elif args[0] == 'create': + # Handle plan creation with proper parsing + # Format: /plan create "Plan Name" "Task 1; Task 2; Task 3" + # Or: /plan create PlanName Task1;Task2;Task3 + if len(args) < 3: + print("❌ Usage: /plan create ") + else: + # Find the first semicolon to split name from tasks + remaining = ' '.join(args[1:]) + + # Check if there's a semicolon to determine name/tasks split + if ';' in remaining: + # Find the first semicolon + semi_idx = remaining.index(';') + name = remaining[:semi_idx].strip() + tasks = remaining[semi_idx+1:].strip() + plan_create(name, tasks) + else: + # No semicolon found - use second arg as first task name, rest as description + name = args[1] + tasks = ' '.join(args[2:]) + plan_create(name, tasks) + elif args[0] == 'list': + plan_list() + elif args[0] == 'progress' and len(args) > 1: + plan_progress(args[1]) + elif args[0] == 'start' and len(args) > 1: + plan_start(args[1]) + elif args[0] == 'complete' and len(args) > 1: + plan_complete(args[1]) + else: + print("❌ Sottocomando sconosciuto o argomenti mancanti") + print("Sottocomandi disponibili: create ..., list, progress , start , complete ") elif command == 'models': list_models() elif command == 'clear':