chore(memory,seedgo): memory modernization + seedgo audit fix (#72)
Memory: 30 files — symbolic orchestrator, handlers, modules cleanup Seedgo: branch_audit.py fix Co-authored-by: @devpulse <devpulse@aipass>
This commit is contained in:
@@ -25,6 +25,7 @@ from datetime import datetime
|
||||
from typing import Dict, Any, List
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -271,6 +272,8 @@ def check_for_new_files() -> Dict[str, Any]:
|
||||
|
||||
save_index(index)
|
||||
|
||||
log_operation("index_sync", {"new_files": len(new_files), "deleted_files": len(deleted_files), "success": True})
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'new_files': len(new_files),
|
||||
|
||||
@@ -24,6 +24,7 @@ from datetime import datetime
|
||||
from typing import Dict, Any
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -259,6 +260,8 @@ def update_central(verbose: bool = False) -> Dict[str, Any]:
|
||||
if verbose:
|
||||
result["stats"] = stats
|
||||
|
||||
log_operation("update_central", {"vectors": stats.get("total_vectors", 0), "success": True})
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -26,6 +26,7 @@ from datetime import datetime
|
||||
from typing import Dict, Any, List
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -430,6 +431,8 @@ def push_memory_bank_dashboard() -> bool:
|
||||
"memory_bank", section_data, branch_paths
|
||||
)
|
||||
|
||||
log_operation("dashboard_push", {"branches_updated": success_count, "success": success_count > 0})
|
||||
|
||||
return success_count > 0
|
||||
|
||||
except Exception:
|
||||
|
||||
@@ -27,6 +27,7 @@ from datetime import datetime
|
||||
from typing import Dict, Any, List
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# Subprocess scripts
|
||||
_HANDLERS_DIR = Path(__file__).resolve().parent.parent
|
||||
@@ -321,4 +322,6 @@ def process_plans() -> Dict[str, Any]:
|
||||
if errors:
|
||||
result['errors'] = errors
|
||||
|
||||
log_operation("process_plans", {"files_processed": files_processed, "total_chunks": total_chunks, "success": result['success']})
|
||||
|
||||
return result
|
||||
|
||||
@@ -34,6 +34,8 @@ from pathlib import Path
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Any
|
||||
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# Paths
|
||||
_MEMORY_ROOT = Path(__file__).resolve().parent.parent.parent.parent # handlers/intake/ → handlers/ → apps/ → memory/
|
||||
CONFIG_PATH = _MEMORY_ROOT / "config" / "memory_bank.config.json"
|
||||
@@ -446,6 +448,8 @@ def process_memory_pool() -> dict:
|
||||
if results['files_processed'] > 0:
|
||||
_update_central_and_dashboard()
|
||||
|
||||
log_operation("process_memory_pool", {"files_processed": results['files_processed'], "total_chunks": results['total_chunks'], "success": results['success']})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ from typing import Dict, Any, Optional
|
||||
from datetime import datetime
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -151,6 +152,8 @@ def write_memory_file(file_path: Path, data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
# Atomic rename (overwrites original)
|
||||
Path(temp_path).rename(file_path)
|
||||
|
||||
log_operation("write_memory_file", {"file": file_path.name, "success": True})
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'file': str(file_path)
|
||||
|
||||
@@ -34,11 +34,12 @@ from typing import Dict, Any, List, Tuple
|
||||
from datetime import datetime
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
# Handler imports (relative within package)
|
||||
from aipass.memory.apps.handlers.json.json_handler import (
|
||||
from aipass.memory.apps.handlers.json.memory_files import (
|
||||
read_memory_file_data,
|
||||
write_memory_file_simple
|
||||
)
|
||||
@@ -493,6 +494,8 @@ def ensure_timestamps(file_path: Path) -> Dict[str, Any]:
|
||||
|
||||
try:
|
||||
data = read_memory_file_data(file_path)
|
||||
if data is None:
|
||||
return {'success': False, 'error': f'Failed to parse file: {file_path.name}'}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'Failed to read file: {e}'}
|
||||
|
||||
@@ -543,6 +546,8 @@ def enforce_limit(file_path: Path) -> Dict[str, Any]:
|
||||
|
||||
try:
|
||||
data = read_memory_file_data(file_path)
|
||||
if data is None:
|
||||
return {'success': False, 'error': f'Failed to parse file: {file_path.name}'}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'Failed to read file: {e}'}
|
||||
|
||||
@@ -592,6 +597,8 @@ def enforce_limit(file_path: Path) -> Dict[str, Any]:
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'Failed to write file: {e}'}
|
||||
|
||||
log_operation("enforce_limit", {"removed": to_remove_count, "remaining": len(to_keep), "success": True})
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'removed': to_remove_count,
|
||||
@@ -623,6 +630,8 @@ def ensure_timestamps_completed(file_path: Path) -> Dict[str, Any]:
|
||||
|
||||
try:
|
||||
data = read_memory_file_data(file_path)
|
||||
if data is None:
|
||||
return {'success': False, 'error': f'Failed to parse file: {file_path.name}'}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'Failed to read file: {e}'}
|
||||
|
||||
@@ -676,6 +685,8 @@ def enforce_limit_completed(file_path: Path) -> Dict[str, Any]:
|
||||
|
||||
try:
|
||||
data = read_memory_file_data(file_path)
|
||||
if data is None:
|
||||
return {'success': False, 'error': f'Failed to parse file: {file_path.name}'}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'Failed to read file: {e}'}
|
||||
|
||||
@@ -756,6 +767,8 @@ def add_learning(
|
||||
|
||||
try:
|
||||
data = read_memory_file_data(file_path)
|
||||
if data is None:
|
||||
return {'success': False, 'error': f'Failed to parse file: {file_path.name}'}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'Failed to read file: {e}'}
|
||||
|
||||
@@ -778,6 +791,8 @@ def add_learning(
|
||||
# Enforce limit after adding
|
||||
enforce_result = enforce_limit(file_path)
|
||||
|
||||
log_operation("add_learning", {"key": key, "action": "updated" if is_update else "added", "success": True})
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'action': 'updated' if is_update else 'added',
|
||||
@@ -808,6 +823,8 @@ def update_status_counts(file_path: Path) -> Dict[str, Any]:
|
||||
|
||||
try:
|
||||
data = read_memory_file_data(file_path)
|
||||
if data is None:
|
||||
return {'success': False, 'error': f'Failed to parse file: {file_path.name}'}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f'Failed to read file: {e}'}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from typing import List, Dict, Any
|
||||
from dataclasses import dataclass
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
|
||||
@@ -24,7 +24,11 @@ Independence:
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import TYPE_CHECKING, Optional, Dict, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from watchdog.observers import Observer as _ObserverType
|
||||
from watchdog.events import FileSystemEventHandler as _HandlerType
|
||||
|
||||
try:
|
||||
from watchdog.observers import Observer
|
||||
@@ -33,12 +37,13 @@ try:
|
||||
except ImportError:
|
||||
WATCHDOG_AVAILABLE = False
|
||||
Observer = None
|
||||
FileSystemEventHandler = object
|
||||
FileSystemEventHandler = object # type: ignore[assignment,misc]
|
||||
|
||||
# Handler imports (relative within package)
|
||||
from aipass.memory.apps.handlers.tracking.line_counter import update_line_count
|
||||
from aipass.memory.apps.handlers.monitor.detector import check_single_file
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -46,7 +51,7 @@ logger = get_system_logger()
|
||||
_MEMORY_ROOT = Path(__file__).resolve().parents[3]
|
||||
|
||||
# Global observer instance
|
||||
_observer: Optional[Observer] = None
|
||||
_observer: Any = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -222,6 +227,8 @@ def check_and_rollover() -> Dict[str, Any]:
|
||||
# Check code_archive for new files to index
|
||||
results['code_archive'] = _check_code_archive()
|
||||
|
||||
log_operation("check_and_rollover", {"files_checked": results.get('files_checked', 0), "rollover_triggered": results.get('rollover_triggered', False)})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@@ -425,7 +432,7 @@ def _is_memory_file(file_path: Path) -> bool:
|
||||
# FILE SYSTEM EVENT HANDLER
|
||||
# =============================================================================
|
||||
|
||||
class MemoryFileWatcher(FileSystemEventHandler):
|
||||
class MemoryFileWatcher(FileSystemEventHandler): # type: ignore[misc]
|
||||
"""Watch for memory file modifications"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -525,7 +532,7 @@ def start_memory_watcher() -> Dict[str, Any]:
|
||||
watcher = MemoryFileWatcher()
|
||||
|
||||
# Create observer
|
||||
new_observer = Observer()
|
||||
new_observer = Observer() # type: ignore[misc]
|
||||
|
||||
# Schedule watcher for each branch path (silent - no logging during startup)
|
||||
watched_paths = []
|
||||
|
||||
@@ -33,7 +33,8 @@ from typing import Dict, List, Any, Tuple
|
||||
from datetime import datetime
|
||||
|
||||
# Handler imports (relative within package)
|
||||
from aipass.memory.apps.handlers.json.json_handler import read_memory_file_data, write_memory_file_simple
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
from aipass.memory.apps.handlers.json.memory_files import read_memory_file_data, write_memory_file_simple
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
|
||||
logger = get_system_logger()
|
||||
@@ -125,8 +126,8 @@ def restore_from_backup(file_path: Path) -> Dict[str, Any]:
|
||||
# FILE OPERATIONS
|
||||
# =============================================================================
|
||||
|
||||
def _read_memory_file(file_path: Path) -> Dict[str, Any]:
|
||||
"""Read memory JSON file using json_handler"""
|
||||
def _read_memory_file(file_path: Path) -> Dict[str, Any] | None:
|
||||
"""Read memory JSON file using memory_files handler."""
|
||||
return read_memory_file_data(file_path)
|
||||
|
||||
|
||||
@@ -380,6 +381,11 @@ def extract_items(
|
||||
# Read file
|
||||
try:
|
||||
data = _read_memory_file(file_path)
|
||||
if data is None:
|
||||
return {
|
||||
'success': False,
|
||||
'error': f"Failed to parse memory file: {file_path.name}"
|
||||
}
|
||||
current_lines = _count_file_lines(file_path)
|
||||
except Exception as e:
|
||||
return {
|
||||
|
||||
@@ -26,6 +26,7 @@ from pathlib import Path
|
||||
from typing import List, Dict, Any
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# logger imported from aipass.prax
|
||||
|
||||
@@ -502,6 +503,13 @@ def execute_rollover() -> Dict[str, Any]:
|
||||
except Exception as e:
|
||||
logger.info(f"[rollover] Memory pool check: {e}")
|
||||
|
||||
log_operation("rollover_execute", {
|
||||
"triggers": len(triggers),
|
||||
"success_count": success_count,
|
||||
"failed_count": len(failed),
|
||||
"success": success_count > 0 or len(triggers) == 0,
|
||||
})
|
||||
|
||||
return {
|
||||
'success': success_count > 0 or len(triggers) == 0,
|
||||
'triggers_count': len(triggers),
|
||||
|
||||
@@ -28,6 +28,7 @@ from typing import Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -124,6 +125,8 @@ def normalize_memory_file(file_path: Path, dry_run: bool = False) -> Dict[str, A
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': f"Failed to write: {e}"}
|
||||
|
||||
log_operation("normalize_memory_file", {"file": file_path.name, "changes": len(changes), "success": True})
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'file': str(file_path),
|
||||
|
||||
@@ -26,6 +26,7 @@ from pathlib import Path
|
||||
from typing import Dict, Any
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# Subprocess scripts for ML operations (run in memory venv)
|
||||
_HANDLERS_DIR = Path(__file__).resolve().parent.parent
|
||||
@@ -274,6 +275,7 @@ def execute_search(
|
||||
|
||||
logger.info(f"[search] Filtered to {len(filtered_results)} relevant results")
|
||||
|
||||
log_operation("search_execute", {"query_len": len(query), "results": len(filtered_results), "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'query': query,
|
||||
|
||||
@@ -33,6 +33,7 @@ from typing import List, Dict, Any
|
||||
from pathlib import Path
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -317,6 +318,7 @@ def search_collection(
|
||||
'collection': collection_name
|
||||
}
|
||||
|
||||
log_operation("vector_search_collection", {"collection": collection_name, "count": result.get('count', 0), "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
**result
|
||||
@@ -485,11 +487,13 @@ def search_all_collections(
|
||||
'count': coll_result['count']
|
||||
}
|
||||
|
||||
total = sum(r['count'] for r in results.values())
|
||||
log_operation("vector_search_all", {"collections": len(results), "total_results": total, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'results': results,
|
||||
'collections_searched': len(results),
|
||||
'total_results': sum(r['count'] for r in results.values())
|
||||
'total_results': total
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -33,6 +33,7 @@ from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -315,6 +316,7 @@ def store_vectors(
|
||||
service = _get_service(db_path)
|
||||
result = service.store_vectors(branch, memory_type, embeddings, documents, metadatas)
|
||||
|
||||
log_operation("chroma_store_vectors", {"collection": result.get('collection'), "count": result.get('count', 0), "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
**result
|
||||
@@ -492,6 +494,7 @@ def search_vectors(
|
||||
# Sort by distance (lower is better)
|
||||
all_results.sort(key=lambda x: x['distance'] if x['distance'] is not None else float('inf'))
|
||||
|
||||
log_operation("chroma_search_vectors", {"collections": len(collection_names), "results": len(all_results), "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'results': all_results[:n_results * len(collection_names)] if all_results else [],
|
||||
|
||||
@@ -34,6 +34,8 @@ Key Functions:
|
||||
from typing import Dict, Any
|
||||
from pathlib import Path
|
||||
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CONSTANTS
|
||||
@@ -128,6 +130,7 @@ def get_collection(
|
||||
embedding_function=None
|
||||
)
|
||||
|
||||
log_operation("chroma_get_collection", {"collection": collection_name, "create": create, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'collection': collection
|
||||
|
||||
@@ -32,6 +32,8 @@ import re
|
||||
from typing import Dict, List, Any, Optional
|
||||
from pathlib import Path
|
||||
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# memory/ root resolved from symbolic/deduplicator.py
|
||||
_MEMORY_ROOT = Path(__file__).resolve().parent.parent.parent.parent
|
||||
|
||||
@@ -189,6 +191,8 @@ def deduplicate_fragment(
|
||||
action = parsed['action']
|
||||
reason = parsed.get('reason', 'No reason provided')
|
||||
|
||||
log_operation("symbolic_dedup", {"action": action, "existing_count": len(existing_fragments), "success": True})
|
||||
|
||||
# Apply action to fragment
|
||||
if action == 'UPDATE':
|
||||
# Merge content from LLM response into fragment
|
||||
|
||||
@@ -31,6 +31,8 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# memory/ root resolved from symbolic/extractor.py
|
||||
_MEMORY_ROOT = Path(__file__).resolve().parent.parent.parent.parent
|
||||
|
||||
@@ -450,7 +452,7 @@ def analyze_conversation_llm(chat_history: List[Dict[str, Any]]) -> Dict[str, An
|
||||
'message_count': 0, 'error': None}
|
||||
llm = extract_fragments_llm(chat_history)
|
||||
reg = analyze_conversation(chat_history)
|
||||
return {
|
||||
result = {
|
||||
'success': llm.get('success', False),
|
||||
'fragments': llm.get('fragments', []),
|
||||
'metadata': {
|
||||
@@ -462,3 +464,5 @@ def analyze_conversation_llm(chat_history: List[Dict[str, Any]]) -> Dict[str, An
|
||||
'chunk_count': llm.get('chunk_count', 0)},
|
||||
'message_count': reg.get('message_count', len(chat_history)),
|
||||
'error': llm.get('error')}
|
||||
log_operation("symbolic_extract", {"fragments": len(result['fragments']), "messages": result['message_count'], "success": True})
|
||||
return result
|
||||
|
||||
@@ -36,7 +36,8 @@ from typing import Dict, List, Any, Tuple
|
||||
|
||||
# Handler imports (domain-organized, no modules)
|
||||
from aipass.memory.apps.handlers.symbolic import retriever
|
||||
from aipass.memory.apps.handlers.json import json_handler
|
||||
from aipass.memory.apps.handlers.json import memory_files
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# memory/ root resolved from symbolic/hook.py
|
||||
_MEMORY_ROOT = Path(__file__).resolve().parent.parent.parent.parent
|
||||
@@ -82,7 +83,7 @@ def load_config(config_path: Path | None = None) -> Dict[str, Any]:
|
||||
path = config_path or DEFAULT_CONFIG_PATH
|
||||
|
||||
if path.exists():
|
||||
result = json_handler.read_memory_file(path)
|
||||
result = memory_files.read_memory_file(path)
|
||||
if result.get('success'):
|
||||
config = result.get('data', {})
|
||||
# Merge with defaults for any missing keys
|
||||
@@ -107,7 +108,7 @@ def save_config(config: Dict[str, Any], config_path: Path | None = None) -> Dict
|
||||
# Ensure parent directory exists
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return json_handler.write_memory_file(path, config)
|
||||
return memory_files.write_memory_file(path, config)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -634,6 +635,7 @@ def process_hook(
|
||||
# Record the surface
|
||||
record_surface(fragment)
|
||||
|
||||
log_operation("symbolic_hook", {"fragment_id": fragment.get('id'), "surfaced": True, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'surfaced': True,
|
||||
|
||||
@@ -38,6 +38,7 @@ from pathlib import Path
|
||||
# Handler imports (domain-organized, no modules)
|
||||
from aipass.memory.apps.handlers.vector import embedder
|
||||
from aipass.memory.apps.handlers.symbolic.chroma_client import get_chroma_client
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -402,6 +403,7 @@ def retrieve_fragments(
|
||||
# Return top n_results
|
||||
final_results = ranked[:n_results]
|
||||
|
||||
log_operation("symbolic_retrieve", {"results": len(final_results), "methods": search_methods_used, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'results': final_results,
|
||||
|
||||
@@ -40,6 +40,7 @@ from pathlib import Path
|
||||
|
||||
# Handler imports (domain-organized, no modules)
|
||||
from aipass.memory.apps.handlers.vector import embedder
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -306,11 +307,13 @@ def store_fragment(
|
||||
metadatas=[flat_meta]
|
||||
)
|
||||
|
||||
total = collection.count()
|
||||
log_operation("symbolic_store_fragment", {"fragment_id": frag_id, "total": total, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'fragment_id': frag_id,
|
||||
'collection': COLLECTION_NAME,
|
||||
'total_fragments': collection.count()
|
||||
'total_fragments': total
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -416,11 +419,13 @@ def store_fragments_batch(
|
||||
metadatas=batch_metadatas
|
||||
)
|
||||
|
||||
total = collection.count()
|
||||
log_operation("symbolic_store_batch", {"stored": len(batch_ids), "total": total, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'stored': len(batch_ids),
|
||||
'collection': COLLECTION_NAME,
|
||||
'total_fragments': collection.count()
|
||||
'total_fragments': total
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -534,11 +539,13 @@ def store_llm_fragment(
|
||||
metadatas=[flat_meta]
|
||||
)
|
||||
|
||||
total = collection.count()
|
||||
log_operation("symbolic_store_llm_fragment", {"fragment_id": frag_id, "total": total, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'fragment_id': frag_id,
|
||||
'collection': COLLECTION_NAME,
|
||||
'total_fragments': collection.count()
|
||||
'total_fragments': total
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -660,11 +667,13 @@ def store_llm_fragments_batch(
|
||||
metadatas=frag_metas
|
||||
)
|
||||
|
||||
total = collection.count()
|
||||
log_operation("symbolic_store_llm_batch", {"stored": len(frag_ids), "total": total, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'stored': len(frag_ids),
|
||||
'collection': COLLECTION_NAME,
|
||||
'total_fragments': collection.count()
|
||||
'total_fragments': total
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
@@ -703,10 +712,12 @@ def delete_fragment(
|
||||
|
||||
collection.delete(ids=[fragment_id])
|
||||
|
||||
total = collection.count()
|
||||
log_operation("symbolic_delete_fragment", {"deleted_id": fragment_id, "total": total, "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
'deleted_id': fragment_id,
|
||||
'total_fragments': collection.count()
|
||||
'total_fragments': total
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -28,6 +28,7 @@ from pathlib import Path
|
||||
from typing import Dict, Any, List, Optional
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# =============================================================================
|
||||
# PATH SETUP
|
||||
@@ -65,7 +66,7 @@ def _replace_placeholders(template: dict, branch_name: str) -> dict:
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
result = copy.deepcopy(template)
|
||||
|
||||
def _replace_in_value(val):
|
||||
def _replace_in_value(val: Any) -> Any:
|
||||
if isinstance(val, str):
|
||||
return val.replace("{{BRANCHNAME}}", branch_name).replace("{{DATE}}", today)
|
||||
elif isinstance(val, list):
|
||||
@@ -74,7 +75,9 @@ def _replace_placeholders(template: dict, branch_name: str) -> dict:
|
||||
return {k: _replace_in_value(v) for k, v in val.items()}
|
||||
return val
|
||||
|
||||
return _replace_in_value(result)
|
||||
replaced = _replace_in_value(result)
|
||||
assert isinstance(replaced, dict)
|
||||
return replaced
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -295,6 +298,7 @@ def diff_template_vs_branch(branch_path: str | Path) -> dict:
|
||||
if file_diff["additions"] or file_diff["removals"] or file_diff["modifications"]:
|
||||
result["observations"].append(file_diff)
|
||||
|
||||
log_operation("template_diff", {"branch": branch_name, "local_diffs": len(result["local"]), "obs_diffs": len(result["observations"]), "success": True})
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -30,9 +30,10 @@ from typing import Dict, Any, List, Tuple, Optional
|
||||
from datetime import datetime
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# Handler imports (same-branch allowed per handler boundaries)
|
||||
from aipass.memory.apps.handlers.json.json_handler import (
|
||||
from aipass.memory.apps.handlers.json.memory_files import (
|
||||
read_memory_file_data,
|
||||
write_memory_file_simple
|
||||
)
|
||||
@@ -81,7 +82,7 @@ def _replace_placeholders(template: dict, branch_name: str) -> dict:
|
||||
"""Replace {{BRANCHNAME}} and {{DATE}} in template values. Returns new dict."""
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
|
||||
def _walk(val):
|
||||
def _walk(val: Any) -> Any:
|
||||
if isinstance(val, str):
|
||||
return val.replace("{{BRANCHNAME}}", branch_name).replace("{{DATE}}", today)
|
||||
elif isinstance(val, list):
|
||||
@@ -90,7 +91,9 @@ def _replace_placeholders(template: dict, branch_name: str) -> dict:
|
||||
return {k: _walk(v) for k, v in val.items()}
|
||||
return val
|
||||
|
||||
return _walk(copy.deepcopy(template))
|
||||
result = _walk(copy.deepcopy(template))
|
||||
assert isinstance(result, dict)
|
||||
return result
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -417,6 +420,7 @@ def push_templates(dry_run: bool = False) -> dict:
|
||||
if not _update_version_file(result["branches_list"]):
|
||||
result["errors"].append("Failed to update template version file")
|
||||
|
||||
log_operation("template_push", {"branches": result["branches_updated"], "files": result["files_modified"], "dry_run": dry_run, "success": True})
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# =============================================================================
|
||||
# PATH SETUP
|
||||
@@ -229,6 +230,7 @@ def push_to_spawn_templates(dry_run: bool = False) -> dict:
|
||||
f"{result['files_modified']} files modified"
|
||||
)
|
||||
|
||||
log_operation("spawn_template_push", {"sets_updated": result["template_sets_updated"], "files": result["files_modified"], "dry_run": dry_run, "success": True})
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -26,8 +26,9 @@ from typing import Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
# Handler imports (relative within package)
|
||||
from aipass.memory.apps.handlers.json.json_handler import update_metadata
|
||||
from aipass.memory.apps.handlers.json.memory_files import update_metadata
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -91,6 +92,8 @@ def update_line_count(file_path: Path) -> Dict[str, Any]:
|
||||
'error': f"Failed to update metadata: {result['error']}"
|
||||
}
|
||||
|
||||
log_operation("update_line_count", {"file": file_path.name, "lines": line_count, "success": True})
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'file': str(file_path),
|
||||
|
||||
@@ -33,6 +33,7 @@ from typing import List, Dict, Any
|
||||
from pathlib import Path
|
||||
|
||||
from aipass.prax.apps.modules.logger import get_system_logger
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
logger = get_system_logger()
|
||||
|
||||
@@ -117,11 +118,12 @@ class EmbeddingService:
|
||||
|
||||
# Pre-sort by length (reduces padding waste by 30%)
|
||||
sorted_pairs = sorted(enumerate(texts), key=lambda x: len(x[1]))
|
||||
sorted_indices, sorted_texts = zip(*sorted_pairs)
|
||||
sorted_indices: list[int] = [p[0] for p in sorted_pairs]
|
||||
sorted_text_list: list[str] = [p[1] for p in sorted_pairs]
|
||||
|
||||
# Encode with optimal settings
|
||||
embeddings = self.model.encode(
|
||||
sorted_texts,
|
||||
sorted_text_list,
|
||||
batch_size=self.batch_size,
|
||||
convert_to_tensor=False, # Return numpy for Chroma
|
||||
normalize_embeddings=True, # Critical for L2 distance
|
||||
@@ -195,6 +197,7 @@ def encode_batch(texts: List[str]) -> Dict[str, Any]:
|
||||
service = _get_service()
|
||||
result = service.encode_batch(texts)
|
||||
|
||||
log_operation("vector_encode_batch", {"count": result.get('count', 0), "dimension": result.get('dimension', 0), "success": True})
|
||||
return {
|
||||
'success': True,
|
||||
**result
|
||||
|
||||
@@ -29,6 +29,7 @@ from rich import box
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# =============================================================================
|
||||
# INFRASTRUCTURE SETUP
|
||||
@@ -221,6 +222,7 @@ def run_rollover() -> bool:
|
||||
for fail in failed:
|
||||
error(f"{fail['trigger']} - {fail['stage']}: {fail['error']}")
|
||||
|
||||
log_operation("rollover_execute", {"triggers": triggers_count, "success_count": success_count})
|
||||
return success_count > 0
|
||||
|
||||
|
||||
@@ -253,6 +255,7 @@ def sync_line_counts() -> None:
|
||||
warning(f"{result['failed']} files failed")
|
||||
for branch, mem_type, err_msg in result.get('failures', []):
|
||||
error(f"{branch}.{mem_type}: {err_msg}")
|
||||
log_operation("rollover_sync_lines", {"updated": result['updated'], "failed": result['failed']})
|
||||
else:
|
||||
error("Failed to sync line counts")
|
||||
|
||||
@@ -325,6 +328,8 @@ def show_status() -> None:
|
||||
|
||||
console.print()
|
||||
|
||||
log_operation("rollover_status", {"branches_checked": stats['total_branches'], "files_ready": stats['files_ready']})
|
||||
|
||||
|
||||
def check_triggers() -> None:
|
||||
"""
|
||||
@@ -351,6 +356,7 @@ def check_triggers() -> None:
|
||||
|
||||
if not triggers:
|
||||
console.print("[green]>[/green] No files need rollover")
|
||||
log_operation("rollover_check", {"files_needing_rollover": 0})
|
||||
return
|
||||
|
||||
console.print(f"[bold cyan]Found {len(triggers)} files ready for rollover:[/bold cyan]")
|
||||
@@ -362,6 +368,7 @@ def check_triggers() -> None:
|
||||
console.print()
|
||||
console.print("[dim]Run 'drone @memory rollover' to process these files[/dim]")
|
||||
console.print()
|
||||
log_operation("rollover_check", {"files_needing_rollover": len(triggers)})
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -28,6 +28,7 @@ from rich import box
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# =============================================================================
|
||||
# INFRASTRUCTURE SETUP
|
||||
@@ -256,6 +257,7 @@ def show_search_results(
|
||||
))
|
||||
|
||||
console.print()
|
||||
log_operation("search_query", {"query": query, "results": len(filtered_results)})
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ from typing import List, Dict, Any
|
||||
# Service imports
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console, header
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# Handler imports (domain-organized)
|
||||
from aipass.memory.apps.handlers.symbolic import extractor
|
||||
@@ -980,6 +981,7 @@ def run_demo() -> None:
|
||||
|
||||
console.print("[dim]Note: Run 'symbolic extract <file>' to use the real LLM pipeline[/dim]")
|
||||
console.print()
|
||||
log_operation("symbolic_demo", {"success": True})
|
||||
|
||||
|
||||
def search_fragments_cli(args: List[str]) -> None:
|
||||
@@ -1137,6 +1139,7 @@ def search_fragments_cli(args: List[str]) -> None:
|
||||
|
||||
console.print()
|
||||
logger.info(f"[symbolic] Displayed {len(results)} fragment results")
|
||||
log_operation("symbolic_fragments", {"query": query or "", "results": len(results)})
|
||||
|
||||
|
||||
def run_hook_test(args: List[str]) -> None:
|
||||
@@ -1267,18 +1270,19 @@ def run_hook_test(args: List[str]) -> None:
|
||||
console.print(f" [dim]Fragments surfaced:[/dim] {state.get('fragments_surfaced', 0)}")
|
||||
console.print(f" [dim]Messages since last:[/dim] {state.get('messages_since_last', 0)}")
|
||||
console.print()
|
||||
log_operation("symbolic_hook_test", {"surfaced": result.get('surfaced', False), "success": result.get('success', False)})
|
||||
|
||||
|
||||
def analyze_file(file_path: str) -> None:
|
||||
"""Analyze a conversation JSON file"""
|
||||
from aipass.memory.apps.handlers.json import json_handler
|
||||
from aipass.memory.apps.handlers.json import memory_files
|
||||
|
||||
path = Path(file_path)
|
||||
if not path.exists():
|
||||
console.print(f"[red]Error:[/red] File not found: {file_path}")
|
||||
return
|
||||
|
||||
read_result = json_handler.read_memory_file(path)
|
||||
read_result = memory_files.read_memory_file(path)
|
||||
if not read_result.get('success'):
|
||||
console.print(f"[red]Error:[/red] {read_result.get('error', 'Failed to read JSON')}")
|
||||
return
|
||||
@@ -1315,6 +1319,7 @@ def analyze_file(file_path: str) -> None:
|
||||
console.print(f" [dim]Words:[/dim] {meta.get('total_words', 0)}")
|
||||
console.print(f" [dim]Depth:[/dim] {meta.get('depth', 'unknown')}")
|
||||
console.print()
|
||||
log_operation("symbolic_analyze", {"file": path.name, "messages": result['message_count']})
|
||||
else:
|
||||
console.print(f"[red]✗[/red] Analysis failed: {result.get('error', 'Unknown error')}")
|
||||
|
||||
@@ -1330,14 +1335,14 @@ def extract_file(file_path: str, source_branch: str | None = None) -> None:
|
||||
file_path: Path to JSON conversation file
|
||||
source_branch: Optional branch name tag for stored fragments
|
||||
"""
|
||||
from aipass.memory.apps.handlers.json import json_handler
|
||||
from aipass.memory.apps.handlers.json import memory_files
|
||||
|
||||
path = Path(file_path)
|
||||
if not path.exists():
|
||||
console.print(f"[red]Error:[/red] File not found: {file_path}")
|
||||
return
|
||||
|
||||
read_result = json_handler.read_memory_file(path)
|
||||
read_result = memory_files.read_memory_file(path)
|
||||
if not read_result.get('success'):
|
||||
console.print(f"[red]Error:[/red] {read_result.get('error', 'Failed to read JSON')}")
|
||||
return
|
||||
@@ -1380,6 +1385,8 @@ def extract_file(file_path: str, source_branch: str | None = None) -> None:
|
||||
|
||||
console.print()
|
||||
logger.info(f"[symbolic] extract_file complete: {result}")
|
||||
if result.get('success'):
|
||||
log_operation("symbolic_extract", {"file": path.name, "added": result.get('added', 0), "updated": result.get('updated', 0)})
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -1650,6 +1657,7 @@ def bootstrap_from_jsonl(max_sessions: int = 8) -> None:
|
||||
f"{total_added} added, {total_updated} updated, "
|
||||
f"{total_skipped} skipped, {total_errors} errors"
|
||||
)
|
||||
log_operation("symbolic_bootstrap", {"sessions": processed_count, "added": total_added, "errors": total_errors})
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -28,6 +28,7 @@ from rich import box
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
from aipass.memory.apps.handlers.json.json_handler import log_operation
|
||||
|
||||
# =============================================================================
|
||||
# INFRASTRUCTURE SETUP
|
||||
@@ -37,7 +38,7 @@ from aipass.cli.apps.modules import console, error, warning
|
||||
from aipass.memory.apps.handlers.templates.pusher import push_templates, get_template_status
|
||||
from aipass.memory.apps.handlers.templates.differ import diff_template_vs_branch
|
||||
from aipass.memory.apps.handlers.templates.spawn_pusher import push_to_spawn_templates
|
||||
from aipass.memory.apps.handlers.json.json_handler import read_memory_file_data
|
||||
from aipass.memory.apps.handlers.json.memory_files import read_memory_file_data
|
||||
|
||||
|
||||
def _find_repo_root() -> Path:
|
||||
@@ -286,6 +287,7 @@ def _display_push_results(result: dict, dry_run: bool) -> None:
|
||||
f"{result['branches_updated']}/{result['branches_scanned']} branches, "
|
||||
f"{result['files_modified']} files"
|
||||
)
|
||||
log_operation("templates_push", {"branches_updated": result['branches_updated'], "files_modified": result['files_modified']})
|
||||
console.print()
|
||||
|
||||
|
||||
@@ -424,6 +426,7 @@ def _display_diff_results(branch_name: str | None = None) -> None:
|
||||
console.print(f"[red]{total_errors} errors encountered[/red]")
|
||||
|
||||
logger.info(f"[templates] Diff complete: {total_diffs} branches with diffs, {total_errors} errors")
|
||||
log_operation("templates_diff", {"branches_compared": len(branches), "branches_with_diffs": total_diffs})
|
||||
console.print()
|
||||
|
||||
|
||||
@@ -481,6 +484,7 @@ def _display_status(status: dict) -> None:
|
||||
console.print("[cyan]Branches pushed:[/cyan] none")
|
||||
|
||||
logger.info(f"[templates] Status checked - version: {version}, last push: {last_push}")
|
||||
log_operation("templates_status", {"version": version, "branches_pushed": len(pushed)})
|
||||
console.print()
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
{
|
||||
"FPLAN-0025_build_status_board_per_branch_statusloca_2026-03-10.md": "2026-03-12T16:49:45.098626"
|
||||
"FPLAN-0025_build_status_board_per_branch_statusloca_2026-03-10.md": "2026-03-12T16:49:45.098626",
|
||||
"FPLAN-0033_cli_stderr_migration_phase_3_remaining_1_2026-03-13.md": "2026-03-17T17:03:38.975570",
|
||||
"FPLAN-0040_phase_1_dashboard_cleanup_remove_devpuls_2026-03-14.md": "2026-03-17T17:03:49.794023",
|
||||
"FPLAN-0049_memory_search_introspection_2026-03-15.md": "2026-03-17T17:04:00.894613",
|
||||
"FPLAN-0041_migrate_init_projectpy_to_cli_branch_2026-03-14.md": "2026-03-17T17:04:11.390219",
|
||||
"FPLAN-0032_cli_stderr_standardization_phase_1_add_e_2026-03-13.md": "2026-03-17T17:04:20.978306",
|
||||
"FPLAN-0034_fplan_0033_wave_1_stderr_migration_api_s_2026-03-14.md": "2026-03-17T17:04:31.760541",
|
||||
"FPLAN-0031_drone_stderr_and_error_propagation_inves_2026-03-13.md": "2026-03-17T17:04:42.782966",
|
||||
"FPLAN-0047_memory_rollover_introspection_2026-03-15.md": "2026-03-17T17:04:54.278205"
|
||||
}
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
from typing import Any, Dict, List
|
||||
from aipass.seedgo.apps.handlers.bypass import ignore_handler
|
||||
from aipass.seedgo.apps.handlers.json import json_handler
|
||||
|
||||
def discover_checkers(pack_path: Path = None) -> Dict[str, object]:
|
||||
def discover_checkers(pack_path: Path | None = None) -> Dict[str, Any]:
|
||||
"""Auto-discover all *_check.py modules from a pack directory.
|
||||
|
||||
Args:
|
||||
@@ -76,35 +76,14 @@ def _log_structure_post_checks(branch_path: Path) -> tuple:
|
||||
- ``system_logs/`` at repo root is managed by prax (runtime dispatch).
|
||||
Having many system logs and few local logs is *normal*.
|
||||
- ``logs/`` at branch root holds local-only logs. Flat placement is
|
||||
fine -- hierarchical sub-directories are only expected when there
|
||||
are enough logs to warrant organisation (>5).
|
||||
fine — the standard does not prescribe internal organisation.
|
||||
"""
|
||||
violations: list[dict] = []
|
||||
scores: list[int] = []
|
||||
|
||||
root_logs_dir = branch_path / "logs"
|
||||
in_dirs = [f for f in branch_path.rglob("*.log") if f.parent.name == "logs"]
|
||||
|
||||
# Check 1: If the branch has a logs/ directory, verify it is not empty
|
||||
# and only flag flat placement when there are enough files to warrant
|
||||
# hierarchical organisation.
|
||||
if root_logs_dir.is_dir():
|
||||
root_only = [f for f in in_dirs if f.parent == root_logs_dir]
|
||||
has_subdirs = any(f.parent != root_logs_dir for f in in_dirs)
|
||||
if not root_only and not has_subdirs:
|
||||
# logs/ exists but is empty -- acceptable (no violation)
|
||||
scores.append(100)
|
||||
elif len(root_only) > 5 and not has_subdirs:
|
||||
# Many logs piled at root with no subdirectories
|
||||
scores.append(50)
|
||||
violations.append({
|
||||
"file": "(branch-level)", "path": str(root_logs_dir), "score": 50,
|
||||
"issues": [f"{len(root_only)} logs at branch root with no sub-directories — consider hierarchical placement"],
|
||||
})
|
||||
else:
|
||||
scores.append(100)
|
||||
|
||||
# Check 2: Verify system_logs/ exists when the branch produces logs.
|
||||
# Check: Verify system_logs/ exists when the branch produces logs.
|
||||
# The two-tier model expects prax to dispatch runtime logs to
|
||||
# system_logs/. A mismatch only matters when the branch has NO
|
||||
# system logs at all despite having local logs (potential prax
|
||||
@@ -146,7 +125,7 @@ def _load_diagnostics_checker():
|
||||
return mod
|
||||
|
||||
|
||||
def audit_branch(branch: Dict[str, str], bypass_rules: list, pack_path: Path = None) -> Dict:
|
||||
def audit_branch(branch: Dict[str, str], bypass_rules: list, pack_path: Path | None = None) -> Dict:
|
||||
"""Audit a branch for standards compliance. Returns backward-compatible dict."""
|
||||
entry_file, branch_path = branch["entry_file"], Path(branch["path"])
|
||||
checkers, all_files = discover_checkers(pack_path), _collect_py_files(branch_path)
|
||||
|
||||
Reference in New Issue
Block a user