Skip to main content

#!/usr/bin/env python3 """Memory System Integration for MoE Classification."""

import sqlite3 import json import logging import re from dataclasses import dataclass, field from datetime import datetime from pathlib import Path from typing import Dict, List, Optional from contextlib import contextmanager

logger = logging.getLogger(name)

ADR-114 & ADR-118: Use centralized path discovery

_user_data = Path.home() / "PROJECTS" / ".coditect-data" / "context-storage" _default_db = str(_user_data / "sessions.db") if _user_data.exists() else "context-storage/sessions.db"

@dataclass class MemoryConfig: context_db_path: str = _default_db # ADR-118: Sessions/messages are in sessions.db (Tier 3) max_similar_docs: int = 10

@dataclass class SimilarDocument: path: str content_preview: str document_type: Optional[str] similarity_reason: str

@dataclass class ProjectConvention: pattern: str document_type: str confidence: float occurrences: int

class MemoryEnhancedClassifier: def init(self, config: Optional[MemoryConfig] = None): self.config = config or MemoryConfig() self._db_available = self._check_database()

def _check_database(self) -> bool:
"""Check if the sessions database exists (ADR-118 Tier 3)."""
# ADR-118: context.db is DELETED. Only check sessions.db paths.
for path in [self.config.context_db_path, str(_user_data / "sessions.db")]:
if Path(path).exists():
self.config.context_db_path = path
return True
return False

@contextmanager
def _get_connection(self):
if not self._db_available:
yield None
return
conn = sqlite3.connect(self.config.context_db_path)
conn.row_factory = sqlite3.Row
try:
yield conn
finally:
conn.close()

def find_similar_documents(self, content: str, path: Optional[str] = None, limit: int = 5) -> List[SimilarDocument]:
if not self._db_available:
return []
similar = []
with self._get_connection() as conn:
if conn and path:
parent = str(Path(path).parent)
try:
cursor = conn.execute(
"SELECT content FROM unified_messages WHERE content LIKE ? LIMIT ?",
(f"%{parent}%", limit * 2))
for row in cursor.fetchall():
doc_type = self._extract_type(row['content'][:500])
similar.append(SimilarDocument(parent, row['content'][:200], doc_type, "Same directory"))
except sqlite3.OperationalError:
pass
return similar[:limit]

def _extract_type(self, content: str) -> Optional[str]:
for pattern in [r'component_type:\s*(\w+)', r'type:\s*(\w+)']:
match = re.search(pattern, content.lower())
if match and match.group(1) in {'agent', 'command', 'skill', 'guide', 'reference', 'workflow', 'adr'}:
return match.group(1)
return None

def get_project_conventions(self) -> List[ProjectConvention]:
if not self._db_available:
return []
conventions = []
with self._get_connection() as conn:
if conn:
try:
cursor = conn.execute(
"SELECT content FROM unified_messages WHERE content LIKE '%component_type%' LIMIT 500")
dir_types: Dict[str, Dict[str, int]] = {}
for row in cursor.fetchall():
path_m = re.search(r'([a-zA-Z_-]+)/[^/]+\.md', row['content'])
type_m = re.search(r'component_type:\s*(\w+)', row['content'].lower())
if path_m and type_m:
d, t = path_m.group(1), type_m.group(1)
dir_types.setdefault(d, {})[t] = dir_types.get(d, {}).get(t, 0) + 1
for d, types in dir_types.items():
total = sum(types.values())
best = max(types, key=types.get)
conf = types[best] / total
if conf >= 0.7 and total >= 3:
conventions.append(ProjectConvention(f"{d}/", best, conf, total))
except sqlite3.OperationalError:
pass
return conventions

def get_classification_hints(self, document_path: str, content: str) -> Dict:
hints = {'similar_documents': [], 'conventions': [], 'suggested_type': None, 'confidence_boost': 0.0}
similar = self.find_similar_documents(content, document_path)
hints['similar_documents'] = [{'type': s.document_type, 'reason': s.similarity_reason} for s in similar if s.document_type]
conventions = self.get_project_conventions()
for conv in conventions:
if conv.pattern.lower() in document_path.lower():
hints['conventions'].append({'pattern': conv.pattern, 'type': conv.document_type, 'confidence': conv.confidence})
type_votes = {}
for sim in hints['similar_documents']:
if sim['type']:
type_votes[sim['type']] = type_votes.get(sim['type'], 0) + 0.3
for conv in hints['conventions']:
type_votes[conv['type']] = type_votes.get(conv['type'], 0) + conv['confidence']
if type_votes:
hints['suggested_type'] = max(type_votes, key=type_votes.get)
hints['confidence_boost'] = min(0.15, type_votes[hints['suggested_type']] * 0.1)
return hints

def is_available(self) -> bool:
return self._db_available

_memory_classifier: Optional[MemoryEnhancedClassifier] = None

def get_memory_classifier(config: Optional[MemoryConfig] = None) -> MemoryEnhancedClassifier: global _memory_classifier if _memory_classifier is None: _memory_classifier = MemoryEnhancedClassifier(config) return _memory_classifier