#
tokens: 37937/50000 2/207 files (page 22/45)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 22 of 45. Use http://codebase.md/dicklesworthstone/llm_gateway_mcp_server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .cursorignore
├── .env.example
├── .envrc
├── .gitignore
├── additional_features.md
├── check_api_keys.py
├── completion_support.py
├── comprehensive_test.py
├── docker-compose.yml
├── Dockerfile
├── empirically_measured_model_speeds.json
├── error_handling.py
├── example_structured_tool.py
├── examples
│   ├── __init__.py
│   ├── advanced_agent_flows_using_unified_memory_system_demo.py
│   ├── advanced_extraction_demo.py
│   ├── advanced_unified_memory_system_demo.py
│   ├── advanced_vector_search_demo.py
│   ├── analytics_reporting_demo.py
│   ├── audio_transcription_demo.py
│   ├── basic_completion_demo.py
│   ├── cache_demo.py
│   ├── claude_integration_demo.py
│   ├── compare_synthesize_demo.py
│   ├── cost_optimization.py
│   ├── data
│   │   ├── sample_event.txt
│   │   ├── Steve_Jobs_Introducing_The_iPhone_compressed.md
│   │   └── Steve_Jobs_Introducing_The_iPhone_compressed.mp3
│   ├── docstring_refiner_demo.py
│   ├── document_conversion_and_processing_demo.py
│   ├── entity_relation_graph_demo.py
│   ├── filesystem_operations_demo.py
│   ├── grok_integration_demo.py
│   ├── local_text_tools_demo.py
│   ├── marqo_fused_search_demo.py
│   ├── measure_model_speeds.py
│   ├── meta_api_demo.py
│   ├── multi_provider_demo.py
│   ├── ollama_integration_demo.py
│   ├── prompt_templates_demo.py
│   ├── python_sandbox_demo.py
│   ├── rag_example.py
│   ├── research_workflow_demo.py
│   ├── sample
│   │   ├── article.txt
│   │   ├── backprop_paper.pdf
│   │   ├── buffett.pdf
│   │   ├── contract_link.txt
│   │   ├── legal_contract.txt
│   │   ├── medical_case.txt
│   │   ├── northwind.db
│   │   ├── research_paper.txt
│   │   ├── sample_data.json
│   │   └── text_classification_samples
│   │       ├── email_classification.txt
│   │       ├── news_samples.txt
│   │       ├── product_reviews.txt
│   │       └── support_tickets.txt
│   ├── sample_docs
│   │   └── downloaded
│   │       └── attention_is_all_you_need.pdf
│   ├── sentiment_analysis_demo.py
│   ├── simple_completion_demo.py
│   ├── single_shot_synthesis_demo.py
│   ├── smart_browser_demo.py
│   ├── sql_database_demo.py
│   ├── sse_client_demo.py
│   ├── test_code_extraction.py
│   ├── test_content_detection.py
│   ├── test_ollama.py
│   ├── text_classification_demo.py
│   ├── text_redline_demo.py
│   ├── tool_composition_examples.py
│   ├── tournament_code_demo.py
│   ├── tournament_text_demo.py
│   ├── unified_memory_system_demo.py
│   ├── vector_search_demo.py
│   ├── web_automation_instruction_packs.py
│   └── workflow_delegation_demo.py
├── LICENSE
├── list_models.py
├── marqo_index_config.json.example
├── mcp_protocol_schema_2025-03-25_version.json
├── mcp_python_lib_docs.md
├── mcp_tool_context_estimator.py
├── model_preferences.py
├── pyproject.toml
├── quick_test.py
├── README.md
├── resource_annotations.py
├── run_all_demo_scripts_and_check_for_errors.py
├── storage
│   └── smart_browser_internal
│       ├── locator_cache.db
│       ├── readability.js
│       └── storage_state.enc
├── test_client.py
├── test_connection.py
├── TEST_README.md
├── test_sse_client.py
├── test_stdio_client.py
├── tests
│   ├── __init__.py
│   ├── conftest.py
│   ├── integration
│   │   ├── __init__.py
│   │   └── test_server.py
│   ├── manual
│   │   ├── test_extraction_advanced.py
│   │   └── test_extraction.py
│   └── unit
│       ├── __init__.py
│       ├── test_cache.py
│       ├── test_providers.py
│       └── test_tools.py
├── TODO.md
├── tool_annotations.py
├── tools_list.json
├── ultimate_mcp_banner.webp
├── ultimate_mcp_logo.webp
├── ultimate_mcp_server
│   ├── __init__.py
│   ├── __main__.py
│   ├── cli
│   │   ├── __init__.py
│   │   ├── __main__.py
│   │   ├── commands.py
│   │   ├── helpers.py
│   │   └── typer_cli.py
│   ├── clients
│   │   ├── __init__.py
│   │   ├── completion_client.py
│   │   └── rag_client.py
│   ├── config
│   │   └── examples
│   │       └── filesystem_config.yaml
│   ├── config.py
│   ├── constants.py
│   ├── core
│   │   ├── __init__.py
│   │   ├── evaluation
│   │   │   ├── base.py
│   │   │   └── evaluators.py
│   │   ├── providers
│   │   │   ├── __init__.py
│   │   │   ├── anthropic.py
│   │   │   ├── base.py
│   │   │   ├── deepseek.py
│   │   │   ├── gemini.py
│   │   │   ├── grok.py
│   │   │   ├── ollama.py
│   │   │   ├── openai.py
│   │   │   └── openrouter.py
│   │   ├── server.py
│   │   ├── state_store.py
│   │   ├── tournaments
│   │   │   ├── manager.py
│   │   │   ├── tasks.py
│   │   │   └── utils.py
│   │   └── ums_api
│   │       ├── __init__.py
│   │       ├── ums_database.py
│   │       ├── ums_endpoints.py
│   │       ├── ums_models.py
│   │       └── ums_services.py
│   ├── exceptions.py
│   ├── graceful_shutdown.py
│   ├── services
│   │   ├── __init__.py
│   │   ├── analytics
│   │   │   ├── __init__.py
│   │   │   ├── metrics.py
│   │   │   └── reporting.py
│   │   ├── cache
│   │   │   ├── __init__.py
│   │   │   ├── cache_service.py
│   │   │   ├── persistence.py
│   │   │   ├── strategies.py
│   │   │   └── utils.py
│   │   ├── cache.py
│   │   ├── document.py
│   │   ├── knowledge_base
│   │   │   ├── __init__.py
│   │   │   ├── feedback.py
│   │   │   ├── manager.py
│   │   │   ├── rag_engine.py
│   │   │   ├── retriever.py
│   │   │   └── utils.py
│   │   ├── prompts
│   │   │   ├── __init__.py
│   │   │   ├── repository.py
│   │   │   └── templates.py
│   │   ├── prompts.py
│   │   └── vector
│   │       ├── __init__.py
│   │       ├── embeddings.py
│   │       └── vector_service.py
│   ├── tool_token_counter.py
│   ├── tools
│   │   ├── __init__.py
│   │   ├── audio_transcription.py
│   │   ├── base.py
│   │   ├── completion.py
│   │   ├── docstring_refiner.py
│   │   ├── document_conversion_and_processing.py
│   │   ├── enhanced-ums-lookbook.html
│   │   ├── entity_relation_graph.py
│   │   ├── excel_spreadsheet_automation.py
│   │   ├── extraction.py
│   │   ├── filesystem.py
│   │   ├── html_to_markdown.py
│   │   ├── local_text_tools.py
│   │   ├── marqo_fused_search.py
│   │   ├── meta_api_tool.py
│   │   ├── ocr_tools.py
│   │   ├── optimization.py
│   │   ├── provider.py
│   │   ├── pyodide_boot_template.html
│   │   ├── python_sandbox.py
│   │   ├── rag.py
│   │   ├── redline-compiled.css
│   │   ├── sentiment_analysis.py
│   │   ├── single_shot_synthesis.py
│   │   ├── smart_browser.py
│   │   ├── sql_databases.py
│   │   ├── text_classification.py
│   │   ├── text_redline_tools.py
│   │   ├── tournament.py
│   │   ├── ums_explorer.html
│   │   └── unified_memory_system.py
│   ├── utils
│   │   ├── __init__.py
│   │   ├── async_utils.py
│   │   ├── display.py
│   │   ├── logging
│   │   │   ├── __init__.py
│   │   │   ├── console.py
│   │   │   ├── emojis.py
│   │   │   ├── formatter.py
│   │   │   ├── logger.py
│   │   │   ├── panels.py
│   │   │   ├── progress.py
│   │   │   └── themes.py
│   │   ├── parse_yaml.py
│   │   ├── parsing.py
│   │   ├── security.py
│   │   └── text.py
│   └── working_memory_api.py
├── unified_memory_system_technical_analysis.md
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/examples/advanced_unified_memory_system_demo.py:
--------------------------------------------------------------------------------

```python
   1 | # examples/advanced_unified_memory_system_demo.py
   2 | #!/usr/bin/env python
   3 | import asyncio
   4 | import sys
   5 | import time
   6 | import traceback
   7 | from pathlib import Path
   8 | from typing import Dict
   9 | 
  10 | # --- Project Setup ---
  11 | # Add project root to path for imports when running as script
  12 | # Adjust this path if your script location relative to the project root differs
  13 | try:
  14 |     SCRIPT_DIR = Path(__file__).resolve().parent
  15 |     PROJECT_ROOT = SCRIPT_DIR.parent  # Assuming this script is in examples/
  16 |     if str(PROJECT_ROOT) not in sys.path:
  17 |         sys.path.insert(0, str(PROJECT_ROOT))
  18 |     # Verify path
  19 |     if not (PROJECT_ROOT / "ultimate_mcp_server").is_dir():
  20 |         print(
  21 |             f"Warning: Could not reliably find project root from {SCRIPT_DIR}. Imports might fail.",
  22 |             file=sys.stderr,
  23 |         )
  24 | 
  25 | except Exception as e:
  26 |     print(f"Error setting up sys.path: {e}", file=sys.stderr)
  27 |     sys.exit(1)
  28 | 
  29 | # --- Rich Imports ---
  30 | from rich.console import Console
  31 | from rich.markup import escape
  32 | from rich.panel import Panel
  33 | from rich.pretty import pretty_repr
  34 | from rich.rule import Rule
  35 | from rich.traceback import install as install_rich_traceback
  36 | 
  37 | from ultimate_mcp_server.config import get_config  # Load config for defaults
  38 | 
  39 | # --- Tool Imports (Specific functions needed) ---
  40 | from ultimate_mcp_server.tools.unified_memory_system import (
  41 |     ActionStatus,
  42 |     ActionType,
  43 |     ArtifactType,  # Fixed: Added missing import
  44 |     DBConnection,
  45 |     MemoryLevel,
  46 |     MemoryType,
  47 |     ThoughtType,
  48 |     ToolError,
  49 |     ToolInputError,
  50 |     # Enums & Helpers
  51 |     WorkflowStatus,
  52 |     add_action_dependency,
  53 |     auto_update_focus,
  54 |     consolidate_memories,
  55 |     # Workflows
  56 |     create_workflow,
  57 |     focus_memory,
  58 |     generate_reflection,
  59 |     # Reporting
  60 |     generate_workflow_report,
  61 |     get_memory_by_id,
  62 |     get_working_memory,
  63 |     initialize_memory_system,
  64 |     load_cognitive_state,
  65 |     optimize_working_memory,  # Use the refactored version
  66 |     promote_memory_level,
  67 |     query_memories,
  68 |     record_action_completion,
  69 |     record_action_start,
  70 |     record_artifact,
  71 |     record_thought,
  72 |     # State & Focus
  73 |     save_cognitive_state,
  74 |     search_semantic_memories,
  75 |     store_memory,
  76 |     update_workflow_status,
  77 | )
  78 | 
  79 | # Utilities from the project
  80 | from ultimate_mcp_server.utils import get_logger
  81 | 
  82 | console = Console()
  83 | logger = get_logger("demo.advanced_memory")
  84 | config = get_config()  # Load config
  85 | 
  86 | # Use a dedicated DB file for this advanced demo
  87 | DEMO_DB_FILE_ADVANCED = str(Path("./advanced_demo_memory.db").resolve())
  88 | _current_db_path = None  # Track the active DB path for safe_tool_call
  89 | 
  90 | install_rich_traceback(show_locals=False, width=console.width)
  91 | 
  92 | 
  93 | # --- Safe Tool Call Helper (Adapted) ---
  94 | async def safe_tool_call(func, args: Dict, description: str, suppress_output: bool = False):
  95 |     """Helper to call a tool function, catch errors, and display results."""
  96 |     global _current_db_path  # Use the tracked path
  97 |     display_title = not suppress_output
  98 |     display_args = not suppress_output
  99 |     display_result_panel = not suppress_output
 100 | 
 101 |     if display_title:
 102 |         title = f"ADV_DEMO: {description}"
 103 |         console.print(Rule(f"[bold blue]{escape(title)}[/bold blue]", style="blue"))
 104 |     if display_args:
 105 |         # Filter out db_path if it matches the global demo path
 106 |         args_to_print = {k: v for k, v in args.items() if k != "db_path" or v != _current_db_path}
 107 |         args_repr = pretty_repr(args_to_print, max_length=120, max_string=100)
 108 |         console.print(f"[dim]Calling [bold cyan]{func.__name__}[/] with args:[/]\n{args_repr}")
 109 | 
 110 |     start_time = time.monotonic()
 111 |     result = None
 112 |     try:
 113 |         # Inject the correct db_path if not explicitly provided
 114 |         if "db_path" not in args and _current_db_path:
 115 |             args["db_path"] = _current_db_path
 116 | 
 117 |         result = await func(**args)
 118 |         processing_time = time.monotonic() - start_time
 119 |         logger.debug(f"Tool '{func.__name__}' execution time: {processing_time:.4f}s")
 120 | 
 121 |         if display_result_panel:
 122 |             success = isinstance(result, dict) and result.get("success", False)
 123 |             panel_title = f"[bold {'green' if success else 'yellow'}]Result: {func.__name__} {'✅' if success else '❔'}[/]"
 124 |             panel_border = "green" if success else "yellow"
 125 | 
 126 |             # Simple repr for most results in advanced demo
 127 |             try:
 128 |                 result_repr = pretty_repr(result, max_length=180, max_string=120)
 129 |             except Exception:
 130 |                 result_repr = f"(Could not represent result of type {type(result)} fully)\n{str(result)[:500]}"
 131 | 
 132 |             console.print(
 133 |                 Panel(
 134 |                     escape(result_repr), title=panel_title, border_style=panel_border, expand=False
 135 |                 )
 136 |             )
 137 | 
 138 |         return result
 139 | 
 140 |     except (ToolInputError, ToolError) as e:
 141 |         processing_time = time.monotonic() - start_time
 142 |         logger.error(f"Tool '{func.__name__}' failed: {e}", exc_info=False)
 143 |         if display_result_panel:
 144 |             error_title = f"[bold red]Error: {func.__name__} Failed ❌[/]"
 145 |             error_content = f"[bold red]{type(e).__name__}:[/] {escape(str(e))}"
 146 |             details = getattr(e, "details", None) or getattr(e, "context", None)
 147 |             if details:
 148 |                 error_content += f"\n\n[yellow]Details:[/]\n{escape(pretty_repr(details))}"
 149 |             console.print(Panel(error_content, title=error_title, border_style="red", expand=False))
 150 |         # Ensure the returned error dict matches the structure expected by asserts/checks
 151 |         return {
 152 |             "success": False,
 153 |             "error": str(e),
 154 |             "error_code": getattr(e, "error_code", "TOOL_ERROR"),
 155 |             "error_type": type(e).__name__,
 156 |             "details": details or {},
 157 |             "isError": True,
 158 |         }
 159 |     except Exception as e:
 160 |         processing_time = time.monotonic() - start_time
 161 |         logger.critical(f"Unexpected error calling '{func.__name__}': {e}", exc_info=True)
 162 |         if display_result_panel:
 163 |             console.print(f"\n[bold red]CRITICAL UNEXPECTED ERROR in {func.__name__}:[/bold red]")
 164 |             console.print_exception(show_locals=False)
 165 |         return {
 166 |             "success": False,
 167 |             "error": f"Unexpected: {str(e)}",
 168 |             "error_code": "UNEXPECTED_ERROR",
 169 |             "error_type": type(e).__name__,
 170 |             "details": {"traceback": traceback.format_exc()},
 171 |             "isError": True,
 172 |         }
 173 |     finally:
 174 |         if display_title:
 175 |             console.print()
 176 | 
 177 | 
 178 | # --- Demo Setup & Teardown (Using new DB file) ---
 179 | async def setup_advanced_demo():
 180 |     """Initialize the memory system using the ADVANCED demo database file."""
 181 |     global _current_db_path
 182 |     _current_db_path = DEMO_DB_FILE_ADVANCED
 183 |     logger.info(f"Using dedicated database for advanced demo: {_current_db_path}")
 184 | 
 185 |     # Delete existing advanced demo DB file for a clean run
 186 |     if Path(_current_db_path).exists():
 187 |         try:
 188 |             Path(_current_db_path).unlink()
 189 |             logger.info(f"Removed existing advanced demo database: {_current_db_path}")
 190 |         except OSError as e:
 191 |             logger.error(f"Failed to remove existing advanced demo database: {e}")
 192 | 
 193 |     console.print(
 194 |         Panel(
 195 |             f"Using database: [cyan]{_current_db_path}[/]\n"
 196 |             f"[yellow]NOTE:[/yellow] This demo operates on a separate database file.",
 197 |             title="Advanced Demo Setup",
 198 |             border_style="yellow",
 199 |         )
 200 |     )
 201 | 
 202 |     # Initialize the memory system with the specific path
 203 |     init_result = await safe_tool_call(
 204 |         initialize_memory_system,
 205 |         {"db_path": _current_db_path},
 206 |         "Initialize Advanced Memory System",
 207 |     )
 208 |     if not init_result or not init_result.get("success"):
 209 |         console.print(
 210 |             "[bold red]CRITICAL:[/bold red] Failed to initialize advanced memory system. Aborting."
 211 |         )
 212 |         await cleanup_advanced_demo()
 213 |         sys.exit(1)
 214 | 
 215 | 
 216 | async def cleanup_advanced_demo():
 217 |     """Close DB connection and optionally delete the demo DB."""
 218 |     global _current_db_path
 219 |     try:
 220 |         await DBConnection.close_connection()
 221 |         logger.info("Closed database connection.")
 222 |     except Exception as e:
 223 |         logger.warning(f"Error closing DB connection during cleanup: {e}")
 224 | 
 225 |     if _current_db_path:
 226 |         logger.info(f"Advanced demo finished using database: {_current_db_path}")
 227 |         _current_db_path = None
 228 | 
 229 | 
 230 | # --- Extension Implementations ---
 231 | 
 232 | 
 233 | async def run_extension_1_goal_decomposition():
 234 |     """Extension 1: Goal Decomposition, Execution, and Synthesis"""
 235 |     console.print(
 236 |         Rule(
 237 |             "[bold green]Extension 1: Goal Decomposition, Execution, Synthesis[/bold green]",
 238 |             style="green",
 239 |         )
 240 |     )
 241 |     wf_id = None
 242 |     planning_action_id = None
 243 |     action1_id, action2_id, action3_id, action4_id = None, None, None, None
 244 |     artifact_search_id = None
 245 |     consolidated_memory_id = None
 246 |     final_artifact_id = None
 247 | 
 248 |     try:
 249 |         # --- Workflow Setup ---
 250 |         wf_res = await safe_tool_call(
 251 |             create_workflow,
 252 |             {
 253 |                 "title": "Research Report: Future of Renewable Energy",
 254 |                 "goal": "Research and write a short report on the future of renewable energy, covering trends, challenges, and synthesis.",
 255 |                 "tags": ["research", "report", "energy"],
 256 |             },
 257 |             "Create Report Workflow",
 258 |         )
 259 |         assert wf_res and wf_res.get("success"), "Failed to create workflow"
 260 |         wf_id = wf_res["workflow_id"]
 261 |         primary_thought_chain_id = wf_res["primary_thought_chain_id"]
 262 |         console.print(f"[cyan]  Workflow ID: {wf_id}[/cyan]")
 263 | 
 264 |         # --- Planning Phase ---
 265 |         plan_start_res = await safe_tool_call(
 266 |             record_action_start,
 267 |             {
 268 |                 "workflow_id": wf_id,
 269 |                 "action_type": ActionType.PLANNING.value,
 270 |                 "reasoning": "Define the steps needed to generate the report.",
 271 |                 "title": "Plan Report Generation",
 272 |                 "tags": ["planning"],
 273 |             },
 274 |             "Start Planning Action",
 275 |         )
 276 |         assert plan_start_res and plan_start_res.get("success"), "Failed to start planning action"
 277 |         planning_action_id = plan_start_res["action_id"]
 278 | 
 279 |         # Record plan thoughts (linked to planning action)
 280 |         plan_steps = [
 281 |             "Research current trends in renewable energy.",
 282 |             "Analyze challenges and obstacles.",
 283 |             "Synthesize findings from research and analysis.",
 284 |             "Draft the final report.",
 285 |         ]
 286 |         parent_tid = None
 287 |         for i, step_content in enumerate(plan_steps):
 288 |             thought_res = await safe_tool_call(
 289 |                 record_thought,
 290 |                 {
 291 |                     "workflow_id": wf_id,
 292 |                     "content": step_content,
 293 |                     "thought_type": ThoughtType.PLAN.value,
 294 |                     "thought_chain_id": primary_thought_chain_id,
 295 |                     "parent_thought_id": parent_tid,
 296 |                     "relevant_action_id": planning_action_id,
 297 |                 },
 298 |                 f"Record Plan Thought {i + 1}",
 299 |                 suppress_output=True,
 300 |             )
 301 |             assert thought_res and thought_res.get("success"), (
 302 |                 f"Failed to record plan thought {i + 1}"
 303 |             )
 304 |             parent_tid = thought_res["thought_id"]
 305 | 
 306 |         # Record planned actions (placeholders)
 307 |         action_plan_details = [
 308 |             {
 309 |                 "title": "Research Trends",
 310 |                 "type": ActionType.RESEARCH.value,
 311 |                 "reasoning": "Plan: Gather data on current renewable energy trends.",
 312 |             },
 313 |             {
 314 |                 "title": "Analyze Challenges",
 315 |                 "type": ActionType.ANALYSIS.value,
 316 |                 "reasoning": "Plan: Identify obstacles based on gathered data.",
 317 |             },
 318 |             {
 319 |                 "title": "Synthesize Findings",
 320 |                 "type": ActionType.REASONING.value,
 321 |                 "reasoning": "Plan: Combine trends and challenges into a coherent summary.",
 322 |             },
 323 |             {
 324 |                 "title": "Draft Report",
 325 |                 "type": ActionType.TOOL_USE.value,
 326 |                 "tool_name": "generate_text",
 327 |                 "reasoning": "Plan: Write the final report using synthesized findings.",
 328 |             },
 329 |         ]
 330 |         action_ids = []
 331 |         for details in action_plan_details:
 332 |             action_res = await safe_tool_call(
 333 |                 record_action_start,
 334 |                 {
 335 |                     "workflow_id": wf_id,
 336 |                     "action_type": details["type"],
 337 |                     "title": details["title"],
 338 |                     "reasoning": details["reasoning"],
 339 |                     "tool_name": details.get("tool_name"),
 340 |                     "parent_action_id": planning_action_id,
 341 |                     "tags": ["planned_step"],
 342 |                     # NOTE: Status will be IN_PROGRESS here initially
 343 |                 },
 344 |                 f"Record Planned Action: {details['title']}",
 345 |                 suppress_output=True,
 346 |             )
 347 |             assert action_res and action_res.get("success"), (
 348 |                 f"Failed to record planned action {details['title']}"
 349 |             )
 350 |             action_ids.append(action_res["action_id"])
 351 | 
 352 |         action1_id, action2_id, action3_id, action4_id = action_ids
 353 | 
 354 |         # Add dependencies between planned actions
 355 |         await safe_tool_call(
 356 |             add_action_dependency,
 357 |             {
 358 |                 "source_action_id": action2_id,
 359 |                 "target_action_id": action1_id,
 360 |                 "dependency_type": "requires",
 361 |             },
 362 |             "Link Action 2->1",
 363 |             suppress_output=True,
 364 |         )
 365 |         await safe_tool_call(
 366 |             add_action_dependency,
 367 |             {
 368 |                 "source_action_id": action3_id,
 369 |                 "target_action_id": action2_id,
 370 |                 "dependency_type": "requires",
 371 |             },
 372 |             "Link Action 3->2",
 373 |             suppress_output=True,
 374 |         )
 375 |         await safe_tool_call(
 376 |             add_action_dependency,
 377 |             {
 378 |                 "source_action_id": action4_id,
 379 |                 "target_action_id": action3_id,
 380 |                 "dependency_type": "requires",
 381 |             },
 382 |             "Link Action 4->3",
 383 |             suppress_output=True,
 384 |         )
 385 | 
 386 |         # Complete the main planning action
 387 |         await safe_tool_call(
 388 |             record_action_completion,
 389 |             {
 390 |                 "action_id": planning_action_id,
 391 |                 "status": ActionStatus.COMPLETED.value,
 392 |                 "summary": "Planning steps recorded and linked.",
 393 |             },
 394 |             "Complete Planning Action",
 395 |         )
 396 | 
 397 |         # --- Execution Phase ---
 398 |         console.print(Rule("Execution Phase", style="cyan"))
 399 | 
 400 |         # Step 1: Execute Research Trends (Simulated Tool Use)
 401 |         # Create a new action representing the execution of the planned step
 402 |         action1_exec_res = await safe_tool_call(
 403 |             record_action_start,
 404 |             {
 405 |                 "workflow_id": wf_id,
 406 |                 "action_type": ActionType.TOOL_USE.value,
 407 |                 "title": "Execute Research Trends",
 408 |                 "reasoning": "Performing web search for trends based on plan.",
 409 |                 "tool_name": "simulated_web_search",
 410 |                 "tags": ["execution"],
 411 |                 "parent_action_id": action1_id,
 412 |             },  # Link execution to the planned action
 413 |             "Start Research Action Execution",
 414 |         )
 415 |         action1_exec_id = action1_exec_res["action_id"]
 416 |         simulated_search_results = "Solar efficiency is increasing rapidly due to perovskite technology. Wind power costs continue to decrease, especially offshore. Battery storage remains a key challenge for grid stability but costs are falling. Geothermal energy is gaining traction for baseload power."
 417 |         art1_res = await safe_tool_call(
 418 |             record_artifact,
 419 |             {
 420 |                 "workflow_id": wf_id,
 421 |                 "action_id": action1_exec_id,
 422 |                 "name": "renewable_trends_search.txt",
 423 |                 "artifact_type": ArtifactType.TEXT.value,
 424 |                 "content": simulated_search_results,
 425 |                 "tags": ["research_data"],
 426 |             },
 427 |             "Record Search Results Artifact",
 428 |         )
 429 |         artifact_search_id = art1_res["artifact_id"]  # noqa: F841
 430 |         mem1_res = await safe_tool_call(  # noqa: F841
 431 |             store_memory,
 432 |             {
 433 |                 "workflow_id": wf_id,
 434 |                 "action_id": action1_exec_id,
 435 |                 "memory_type": MemoryType.OBSERVATION.value,
 436 |                 "content": f"Key findings from trends research: {simulated_search_results}",
 437 |                 "description": "Summary of renewable trends",
 438 |                 "tags": ["trends", "research"],
 439 |                 "importance": 7.0,
 440 |             },
 441 |             "Store Research Findings Memory",
 442 |         )
 443 |         await safe_tool_call(
 444 |             record_action_completion,
 445 |             {
 446 |                 "action_id": action1_exec_id,
 447 |                 "status": ActionStatus.COMPLETED.value,
 448 |                 "summary": "Web search completed.",
 449 |             },
 450 |             "Complete Research Action Execution",
 451 |         )
 452 |         # Mark the original planned action as completed now that execution is done
 453 |         await safe_tool_call(
 454 |             record_action_completion,
 455 |             {
 456 |                 "action_id": action1_id,
 457 |                 "status": ActionStatus.COMPLETED.value,
 458 |                 "summary": f"Executed as action {action1_exec_id}",
 459 |             },
 460 |             "Mark Planned Research Action as Completed",
 461 |             suppress_output=True,
 462 |         )
 463 | 
 464 |         # Step 2: Execute Analyze Challenges
 465 |         action2_exec_res = await safe_tool_call(
 466 |             record_action_start,
 467 |             {
 468 |                 "workflow_id": wf_id,
 469 |                 "action_type": ActionType.ANALYSIS.value,
 470 |                 "title": "Execute Analyze Challenges",
 471 |                 "reasoning": "Analyzing search results for challenges based on plan.",
 472 |                 "tags": ["execution"],
 473 |                 "parent_action_id": action2_id,
 474 |             },
 475 |             "Start Analysis Action Execution",
 476 |         )
 477 |         action2_exec_id = action2_exec_res["action_id"]
 478 |         thought_challenge_res = await safe_tool_call(  # noqa: F841
 479 |             record_thought,
 480 |             {
 481 |                 "workflow_id": wf_id,
 482 |                 "thought_chain_id": primary_thought_chain_id,
 483 |                 "content": "Based on trends, major challenge seems to be grid integration for intermittent sources and cost-effective, large-scale energy storage.",
 484 |                 "thought_type": ThoughtType.HYPOTHESIS.value,
 485 |                 "relevant_action_id": action2_exec_id,
 486 |             },
 487 |             "Record Challenge Hypothesis Thought",
 488 |         )
 489 |         mem2_res = await safe_tool_call(  # noqa: F841
 490 |             store_memory,
 491 |             {
 492 |                 "workflow_id": wf_id,
 493 |                 "action_id": action2_exec_id,
 494 |                 "memory_type": MemoryType.INSIGHT.value,
 495 |                 "content": "Grid integration and energy storage are primary hurdles for widespread renewable adoption, despite falling generation costs.",
 496 |                 "description": "Key challenges identified",
 497 |                 "tags": ["challenges", "insight"],
 498 |                 "importance": 8.0,
 499 |             },
 500 |             "Store Challenge Insight Memory",
 501 |         )
 502 |         await safe_tool_call(
 503 |             record_action_completion,
 504 |             {
 505 |                 "action_id": action2_exec_id,
 506 |                 "status": ActionStatus.COMPLETED.value,
 507 |                 "summary": "Analysis of challenges complete.",
 508 |             },
 509 |             "Complete Analysis Action Execution",
 510 |         )
 511 |         await safe_tool_call(
 512 |             record_action_completion,
 513 |             {
 514 |                 "action_id": action2_id,
 515 |                 "status": ActionStatus.COMPLETED.value,
 516 |                 "summary": f"Executed as action {action2_exec_id}",
 517 |             },
 518 |             "Mark Planned Analysis Action as Completed",
 519 |             suppress_output=True,
 520 |         )
 521 | 
 522 |         # Step 3: Execute Synthesize Findings
 523 |         action3_exec_res = await safe_tool_call(
 524 |             record_action_start,
 525 |             {
 526 |                 "workflow_id": wf_id,
 527 |                 "action_type": ActionType.REASONING.value,
 528 |                 "title": "Execute Synthesize Findings",
 529 |                 "reasoning": "Combining research and analysis memories.",
 530 |                 "tags": ["execution"],
 531 |                 "parent_action_id": action3_id,
 532 |             },
 533 |             "Start Synthesis Action Execution",
 534 |         )
 535 |         action3_exec_id = action3_exec_res["action_id"]
 536 |         # <<< FIX: Remove action_id from query_memories calls >>>
 537 |         query_res_obs = await safe_tool_call(
 538 |             query_memories,
 539 |             {
 540 |                 "workflow_id": wf_id,
 541 |                 "memory_type": MemoryType.OBSERVATION.value,
 542 |                 "sort_by": "created_at",
 543 |                 "limit": 5,
 544 |             },
 545 |             "Query Observation Memories for Synthesis",
 546 |         )
 547 |         query_res_insight = await safe_tool_call(
 548 |             query_memories,
 549 |             {
 550 |                 "workflow_id": wf_id,
 551 |                 "memory_type": MemoryType.INSIGHT.value,
 552 |                 "sort_by": "created_at",
 553 |                 "limit": 5,
 554 |             },
 555 |             "Query Insight Memories for Synthesis",
 556 |         )
 557 |         assert query_res_obs and query_res_obs.get("success"), "Observation query failed"
 558 |         assert query_res_insight and query_res_insight.get("success"), "Insight query failed"
 559 | 
 560 |         mem_ids_to_consolidate = [m["memory_id"] for m in query_res_obs.get("memories", [])] + [
 561 |             m["memory_id"] for m in query_res_insight.get("memories", [])
 562 |         ]
 563 |         assert len(mem_ids_to_consolidate) >= 2, (
 564 |             f"Expected at least 2 memories to consolidate, found {len(mem_ids_to_consolidate)}"
 565 |         )
 566 | 
 567 |         consolidation_res = await safe_tool_call(
 568 |             consolidate_memories,
 569 |             {
 570 |                 "workflow_id": wf_id,
 571 |                 "target_memories": mem_ids_to_consolidate,
 572 |                 "consolidation_type": "summary",
 573 |                 "store_result": True,
 574 |             },
 575 |             "Consolidate Findings",
 576 |         )
 577 |         assert consolidation_res and consolidation_res.get("success"), "Consolidation failed"
 578 |         consolidated_memory_id = consolidation_res["stored_memory_id"]
 579 |         assert consolidated_memory_id, "Consolidation did not return a stored memory ID"
 580 |         await safe_tool_call(
 581 |             record_action_completion,
 582 |             {
 583 |                 "action_id": action3_exec_id,
 584 |                 "status": ActionStatus.COMPLETED.value,
 585 |                 "summary": f"Consolidated research and analysis into memory {consolidated_memory_id[:8]}.",
 586 |             },
 587 |             "Complete Synthesis Action Execution",
 588 |         )
 589 |         await safe_tool_call(
 590 |             record_action_completion,
 591 |             {
 592 |                 "action_id": action3_id,
 593 |                 "status": ActionStatus.COMPLETED.value,
 594 |                 "summary": f"Executed as action {action3_exec_id}",
 595 |             },
 596 |             "Mark Planned Synthesis Action as Completed",
 597 |             suppress_output=True,
 598 |         )
 599 | 
 600 |         # Step 4: Execute Draft Report
 601 |         action4_exec_res = await safe_tool_call(
 602 |             record_action_start,
 603 |             {
 604 |                 "workflow_id": wf_id,
 605 |                 "action_type": ActionType.TOOL_USE.value,
 606 |                 "title": "Execute Draft Report",
 607 |                 "reasoning": "Generating report draft using consolidated summary.",
 608 |                 "tool_name": "simulated_generate_text",
 609 |                 "tags": ["execution", "reporting"],
 610 |                 "parent_action_id": action4_id,
 611 |             },
 612 |             "Start Drafting Action Execution",
 613 |         )
 614 |         action4_exec_id = action4_exec_res["action_id"]
 615 |         consolidated_mem_details = await safe_tool_call(
 616 |             get_memory_by_id,
 617 |             {"memory_id": consolidated_memory_id},
 618 |             "Fetch Consolidated Memory",
 619 |             suppress_output=True,
 620 |         )
 621 |         assert consolidated_mem_details and consolidated_mem_details.get("success"), (
 622 |             "Failed to fetch consolidated memory"
 623 |         )
 624 |         consolidated_content = consolidated_mem_details.get(
 625 |             "content", "Error fetching consolidated content."
 626 |         )
 627 | 
 628 |         simulated_draft = f"""# The Future of Renewable Energy: A Brief Report
 629 | 
 630 | ## Consolidated Findings
 631 | {consolidated_content}
 632 | 
 633 | ## Conclusion
 634 | The trajectory for renewable energy shows promise with falling costs and improving tech (solar, wind). However, significant investment in grid modernization and energy storage solutions is paramount to overcome intermittency challenges and enable widespread adoption. Geothermal offers potential for stable baseload power.
 635 | """
 636 |         art2_res = await safe_tool_call(
 637 |             record_artifact,
 638 |             {
 639 |                 "workflow_id": wf_id,
 640 |                 "action_id": action4_exec_id,
 641 |                 "name": "renewable_report_draft.md",
 642 |                 "artifact_type": ArtifactType.TEXT.value,
 643 |                 "content": simulated_draft,
 644 |                 "is_output": True,
 645 |                 "tags": ["report", "draft", "output"],
 646 |             },
 647 |             "Record Final Report Artifact",
 648 |         )
 649 |         final_artifact_id = art2_res["artifact_id"]  # noqa F841
 650 |         await safe_tool_call(
 651 |             record_action_completion,
 652 |             {
 653 |                 "action_id": action4_exec_id,
 654 |                 "status": ActionStatus.COMPLETED.value,
 655 |                 "summary": f"Draft report artifact created: {art2_res['artifact_id'][:8]}.",
 656 |             },
 657 |             "Complete Drafting Action Execution",
 658 |         )
 659 |         await safe_tool_call(
 660 |             record_action_completion,
 661 |             {
 662 |                 "action_id": action4_id,
 663 |                 "status": ActionStatus.COMPLETED.value,
 664 |                 "summary": f"Executed as action {action4_exec_id}",
 665 |             },
 666 |             "Mark Planned Drafting Action as Completed",
 667 |             suppress_output=True,
 668 |         )
 669 | 
 670 |         # --- Completion & Reporting ---
 671 |         console.print(Rule("Workflow Completion & Reporting", style="cyan"))
 672 |         await safe_tool_call(
 673 |             update_workflow_status,
 674 |             {
 675 |                 "workflow_id": wf_id,
 676 |                 "status": WorkflowStatus.COMPLETED.value,
 677 |                 "completion_message": "Report generated successfully.",
 678 |             },
 679 |             "Mark Workflow Completed",
 680 |         )
 681 |         await safe_tool_call(
 682 |             generate_workflow_report,
 683 |             {
 684 |                 "workflow_id": wf_id,
 685 |                 "report_format": "markdown",
 686 |                 "style": "professional",
 687 |                 "include_thoughts": True,
 688 |                 "include_artifacts": True,
 689 |             },
 690 |             "Generate Final Workflow Report",
 691 |         )
 692 | 
 693 |     except AssertionError as e:
 694 |         logger.error(f"Assertion failed during Extension 1: {e}", exc_info=True)
 695 |         console.print(f"[bold red]Assertion Failed:[/bold red] {e}")
 696 |     except Exception as e:
 697 |         logger.error(f"Error in Extension 1: {e}", exc_info=True)
 698 |         console.print(f"[bold red]Error in Extension 1:[/bold red] {e}")
 699 |     finally:
 700 |         console.print(Rule("Extension 1 Finished", style="green"))
 701 | 
 702 | 
 703 | async def run_extension_2_dynamic_adaptation():
 704 |     """Extension 2: Dynamic Adaptation Based on Reflection"""
 705 |     console.print(
 706 |         Rule(
 707 |             "[bold green]Extension 2: Dynamic Adaptation Based on Reflection[/bold green]",
 708 |             style="green",
 709 |         )
 710 |     )
 711 |     wf_id = None
 712 |     action1_id, action2_id, action3_id, action4_id, action5_id = None, None, None, None, None  # noqa F841
 713 |     error_memory_id = None
 714 | 
 715 |     try:
 716 |         # --- Setup ---
 717 |         wf_res = await safe_tool_call(
 718 |             create_workflow,
 719 |             {
 720 |                 "title": "Optimize Python Function",
 721 |                 "goal": "Improve performance of a sample Python function.",
 722 |             },
 723 |             "Create Optimization Workflow",
 724 |         )
 725 |         assert wf_res and wf_res.get("success"), "Failed to create workflow"
 726 |         wf_id = wf_res["workflow_id"]
 727 |         primary_thought_chain_id = wf_res["primary_thought_chain_id"]
 728 | 
 729 |         # --- Initial Actions ---
 730 |         act1_res = await safe_tool_call(
 731 |             record_action_start,
 732 |             {
 733 |                 "workflow_id": wf_id,
 734 |                 "action_type": ActionType.ANALYSIS.value,
 735 |                 "title": "Analyze function performance",
 736 |                 "reasoning": "Establish baseline performance metrics.",
 737 |             },
 738 |             "Start Analysis Action",
 739 |         )
 740 |         action1_id = act1_res["action_id"]
 741 |         await safe_tool_call(
 742 |             record_artifact,
 743 |             {
 744 |                 "workflow_id": wf_id,
 745 |                 "action_id": action1_id,
 746 |                 "name": "profile.data",
 747 |                 "artifact_type": ArtifactType.DATA.value,
 748 |             },
 749 |             "Record Profiling Artifact",
 750 |             suppress_output=True,
 751 |         )
 752 |         await safe_tool_call(
 753 |             record_action_completion,
 754 |             {"action_id": action1_id, "status": ActionStatus.COMPLETED.value},
 755 |             "Complete Analysis Action",
 756 |         )
 757 | 
 758 |         act2_res = await safe_tool_call(
 759 |             record_action_start,
 760 |             {
 761 |                 "workflow_id": wf_id,
 762 |                 "action_type": ActionType.TOOL_USE.value,
 763 |                 "title": "Attempt optimization 1 (Vectorization)",
 764 |                 "tool_name": "modify_code",
 765 |                 "reasoning": "Try vectorization approach for potential speedup.",
 766 |             },
 767 |             "Start Optimization 1 Action",
 768 |         )
 769 |         action2_id = act2_res["action_id"]
 770 |         await safe_tool_call(
 771 |             record_artifact,
 772 |             {
 773 |                 "workflow_id": wf_id,
 774 |                 "action_id": action2_id,
 775 |                 "name": "optimized_v1.py",
 776 |                 "artifact_type": ArtifactType.CODE.value,
 777 |             },
 778 |             "Record Opt 1 Artifact",
 779 |             suppress_output=True,
 780 |         )
 781 |         await safe_tool_call(
 782 |             record_action_completion,
 783 |             {"action_id": action2_id, "status": ActionStatus.COMPLETED.value},
 784 |             "Complete Optimization 1 Action",
 785 |         )
 786 | 
 787 |         act3_res = await safe_tool_call(
 788 |             record_action_start,
 789 |             {
 790 |                 "workflow_id": wf_id,
 791 |                 "action_type": ActionType.TOOL_USE.value,
 792 |                 "title": "Test optimization 1",
 793 |                 "tool_name": "run_tests",
 794 |                 "reasoning": "Verify vectorization attempt correctness and performance.",
 795 |             },
 796 |             "Start Test 1 Action",
 797 |         )
 798 |         action3_id = act3_res["action_id"]
 799 |         error_result = {
 800 |             "error": "ValueError: Array dimensions mismatch",
 801 |             "traceback": "Traceback details...",
 802 |         }
 803 |         mem_res = await safe_tool_call(
 804 |             store_memory,
 805 |             {
 806 |                 "workflow_id": wf_id,
 807 |                 "action_id": action3_id,
 808 |                 "memory_type": MemoryType.OBSERVATION.value,
 809 |                 "content": f"Test failed for optimization 1 (Vectorization): {error_result['error']}",
 810 |                 "description": "Vectorization test failure",
 811 |                 "tags": ["error", "test", "vectorization"],
 812 |                 "importance": 8.0,
 813 |             },
 814 |             "Store Failure Observation Memory",
 815 |         )
 816 |         error_memory_id = mem_res.get("memory_id")
 817 |         await safe_tool_call(
 818 |             record_action_completion,
 819 |             {
 820 |                 "action_id": action3_id,
 821 |                 "status": ActionStatus.FAILED.value,
 822 |                 "tool_result": error_result,
 823 |                 "summary": "Vectorization failed tests due to dimension mismatch.",
 824 |             },
 825 |             "Complete Test 1 Action (Failed)",
 826 |         )
 827 | 
 828 |         # --- Reflection & Adaptation ---
 829 |         console.print(Rule("Reflection and Adaptation Phase", style="cyan"))
 830 |         reflection_res = await safe_tool_call(
 831 |             generate_reflection,
 832 |             {"workflow_id": wf_id, "reflection_type": "gaps"},
 833 |             "Generate Gaps Reflection",
 834 |         )
 835 |         assert reflection_res and reflection_res.get("success"), "Reflection generation failed"
 836 |         reflection_content = reflection_res.get("content", "").lower()
 837 | 
 838 |         # Programmatic check of reflection output
 839 |         if (
 840 |             "dimension mismatch" in reflection_content
 841 |             or "valueerror" in reflection_content
 842 |             or "vectorization" in reflection_content
 843 |             or action3_id[:6] in reflection_content
 844 |         ):
 845 |             console.print(
 846 |                 "[green]  Reflection mentioned the likely error source or related action.[/green]"
 847 |             )
 848 | 
 849 |             thought1_res = await safe_tool_call(
 850 |                 record_thought,
 851 |                 {
 852 |                     "workflow_id": wf_id,
 853 |                     "thought_chain_id": primary_thought_chain_id,
 854 |                     "content": "Reflection and test failure (ValueError: Array dimensions mismatch) suggest the vectorization approach was fundamentally flawed or misapplied.",
 855 |                     "thought_type": ThoughtType.INFERENCE.value,
 856 |                     "relevant_action_id": action3_id,
 857 |                 },
 858 |                 "Record Inference Thought",
 859 |             )
 860 |             thought2_res = await safe_tool_call(  # noqa: F841
 861 |                 record_thought,
 862 |                 {
 863 |                     "workflow_id": wf_id,
 864 |                     "thought_chain_id": primary_thought_chain_id,
 865 |                     "content": "Plan B: Abandon vectorization. Try loop unrolling as an alternative optimization strategy.",
 866 |                     "thought_type": ThoughtType.PLAN.value,
 867 |                     "parent_thought_id": thought1_res.get("thought_id"),
 868 |                 },
 869 |                 "Record Plan B Thought",
 870 |             )
 871 | 
 872 |             # Action 4: Attempt Optimization 2 (Loop Unrolling)
 873 |             act4_res = await safe_tool_call(
 874 |                 record_action_start,
 875 |                 {
 876 |                     "workflow_id": wf_id,
 877 |                     "action_type": ActionType.TOOL_USE.value,
 878 |                     "title": "Attempt optimization 2 (Loop Unrolling)",
 879 |                     "tool_name": "modify_code",
 880 |                     "reasoning": "Implement loop unrolling based on failure of vectorization (Plan B).",
 881 |                 },
 882 |                 "Start Optimization 2 Action",
 883 |             )
 884 |             action4_id = act4_res["action_id"]
 885 |             await safe_tool_call(
 886 |                 record_artifact,
 887 |                 {
 888 |                     "workflow_id": wf_id,
 889 |                     "action_id": action4_id,
 890 |                     "name": "optimized_v2.py",
 891 |                     "artifact_type": ArtifactType.CODE.value,
 892 |                 },
 893 |                 "Record Opt 2 Artifact",
 894 |                 suppress_output=True,
 895 |             )
 896 |             await safe_tool_call(
 897 |                 record_action_completion,
 898 |                 {"action_id": action4_id, "status": ActionStatus.COMPLETED.value},
 899 |                 "Complete Optimization 2 Action",
 900 |             )
 901 | 
 902 |             # Action 5: Test Optimization 2 (Success)
 903 |             act5_res = await safe_tool_call(
 904 |                 record_action_start,
 905 |                 {
 906 |                     "workflow_id": wf_id,
 907 |                     "action_type": ActionType.TOOL_USE.value,
 908 |                     "title": "Test optimization 2",
 909 |                     "tool_name": "run_tests",
 910 |                     "reasoning": "Verify loop unrolling attempt.",
 911 |                 },
 912 |                 "Start Test 2 Action",
 913 |             )
 914 |             action5_id = act5_res["action_id"]
 915 |             mem_success_res = await safe_tool_call(
 916 |                 store_memory,
 917 |                 {
 918 |                     "workflow_id": wf_id,
 919 |                     "action_id": action5_id,
 920 |                     "memory_type": MemoryType.OBSERVATION.value,
 921 |                     "content": "Test passed for optimization 2 (loop unrolling). Performance improved by 15%.",
 922 |                     "description": "Loop unrolling test success",
 923 |                     "tags": ["success", "test", "unrolling"],
 924 |                     "importance": 7.0,
 925 |                 },
 926 |                 "Store Success Observation Memory",
 927 |             )
 928 |             success_memory_id = mem_success_res.get("memory_id")
 929 |             await safe_tool_call(
 930 |                 record_action_completion,
 931 |                 {
 932 |                     "action_id": action5_id,
 933 |                     "status": ActionStatus.COMPLETED.value,
 934 |                     "tool_result": {"status": "passed", "performance_gain": "15%"},
 935 |                     "summary": "Loop unrolling successful and provided performance gain.",
 936 |                 },
 937 |                 "Complete Test 2 Action (Success)",
 938 |             )
 939 | 
 940 |             # Consolidate insights from failure and success
 941 |             if error_memory_id and success_memory_id:
 942 |                 consolidation_res = await safe_tool_call(
 943 |                     consolidate_memories,
 944 |                     {
 945 |                         "workflow_id": wf_id,
 946 |                         "target_memories": [error_memory_id, success_memory_id],
 947 |                         "consolidation_type": "insight",
 948 |                     },
 949 |                     "Consolidate Failure/Success Insight",
 950 |                 )
 951 |                 assert consolidation_res and consolidation_res.get("success"), (
 952 |                     "Consolidation tool call failed"
 953 |                 )
 954 |                 consolidated_insight = consolidation_res.get("consolidated_content", "").lower()
 955 |                 # <<< FIX: Loosened Assertion >>>
 956 |                 contains_vectorization = "vectorization" in consolidated_insight
 957 |                 contains_unrolling = (
 958 |                     "loop unrolling" in consolidated_insight or "unrolling" in consolidated_insight
 959 |                 )
 960 |                 contains_fail = "fail" in consolidated_insight or "error" in consolidated_insight
 961 |                 contains_success = (
 962 |                     "success" in consolidated_insight
 963 |                     or "passed" in consolidated_insight
 964 |                     or "improved" in consolidated_insight
 965 |                 )
 966 |                 assert (
 967 |                     contains_vectorization
 968 |                     and contains_unrolling
 969 |                     and contains_fail
 970 |                     and contains_success
 971 |                 ), (
 972 |                     "Consolidated insight didn't capture key concepts (vectorization fail, unrolling success)."
 973 |                 )
 974 |                 console.print(
 975 |                     "[green]  Consolidated insight correctly reflects outcome (loosened check).[/green]"
 976 |                 )
 977 |             else:
 978 |                 console.print(
 979 |                     "[yellow]  Skipping consolidation check as required memory IDs weren't captured.[/yellow]"
 980 |                 )
 981 | 
 982 |         else:
 983 |             console.print(
 984 |                 "[yellow]  Reflection did not explicitly mention the error source. Skipping adaptation steps.[/yellow]"
 985 |             )
 986 | 
 987 |     except AssertionError as e:
 988 |         logger.error(f"Assertion failed during Extension 2: {e}", exc_info=True)
 989 |         console.print(f"[bold red]Assertion Failed:[/bold red] {e}")
 990 |     except Exception as e:
 991 |         logger.error(f"Error in Extension 2: {e}", exc_info=True)
 992 |         console.print(f"[bold red]Error in Extension 2:[/bold red] {e}")
 993 |     finally:
 994 |         console.print(Rule("Extension 2 Finished", style="green"))
 995 | 
 996 | 
 997 | async def run_extension_3_knowledge_building():
 998 |     """Extension 3: Multi-Level Memory Interaction & Knowledge Building"""
 999 |     console.print(
1000 |         Rule(
1001 |             "[bold green]Extension 3: Knowledge Building & Memory Levels[/bold green]",
1002 |             style="green",
1003 |         )
1004 |     )
1005 |     wf_id = None
1006 |     episodic_mem_ids = []
1007 |     insight_mem_id = None
1008 |     insight_mem_content = ""  # Store content for later search
1009 |     procedural_mem_id = None
1010 | 
1011 |     try:
1012 |         # --- Setup ---
1013 |         wf_res = await safe_tool_call(
1014 |             create_workflow,
1015 |             {
1016 |                 "title": "API Interaction Monitoring",
1017 |                 "goal": "Observe and learn from API call patterns.",
1018 |             },
1019 |             "Create API Monitoring Workflow",
1020 |         )
1021 |         assert wf_res and wf_res.get("success"), "Failed to create workflow"
1022 |         wf_id = wf_res["workflow_id"]
1023 | 
1024 |         # --- Record Episodic Failures ---
1025 |         console.print(Rule("Simulating API Failures (Episodic)", style="cyan"))
1026 |         for i in range(4):
1027 |             act_res = await safe_tool_call(
1028 |                 record_action_start,
1029 |                 {
1030 |                     "workflow_id": wf_id,
1031 |                     "action_type": ActionType.TOOL_USE.value,
1032 |                     "title": f"Call API Endpoint X (Attempt {i + 1})",
1033 |                     "tool_name": "call_api",
1034 |                     "reasoning": f"Attempting API call to endpoint X, attempt number {i + 1}.",  # Fixed: Added reasoning
1035 |                 },
1036 |                 f"Start API Call Action {i + 1}",
1037 |                 suppress_output=True,
1038 |             )
1039 |             assert act_res and act_res.get("success"), f"Failed to start API Call Action {i + 1}"
1040 |             action_id = act_res["action_id"]
1041 | 
1042 |             fail_result = {"error_code": 429, "message": "Too Many Requests"}
1043 |             mem_res = await safe_tool_call(
1044 |                 store_memory,
1045 |                 {
1046 |                     "workflow_id": wf_id,
1047 |                     "action_id": action_id,
1048 |                     "memory_level": MemoryLevel.EPISODIC.value,
1049 |                     "memory_type": MemoryType.OBSERVATION.value,
1050 |                     "content": "API call to endpoint X failed with 429 Too Many Requests.",
1051 |                     "description": f"API Failure {i + 1}",
1052 |                     "tags": ["api_call", "failure", "429"],
1053 |                     "importance": 6.0 - i * 0.2,
1054 |                 },
1055 |                 f"Store Episodic Failure Memory {i + 1}",
1056 |             )
1057 |             assert mem_res and mem_res.get("success"), (
1058 |                 f"Failed to store memory for action {action_id}"
1059 |             )
1060 |             episodic_mem_ids.append(mem_res["memory_id"])
1061 |             await safe_tool_call(
1062 |                 record_action_completion,
1063 |                 {
1064 |                     "action_id": action_id,
1065 |                     "status": ActionStatus.FAILED.value,
1066 |                     "tool_result": fail_result,
1067 |                 },
1068 |                 f"Complete API Call Action {i + 1} (Failed)",
1069 |                 suppress_output=True,
1070 |             )
1071 |             await asyncio.sleep(0.1)
1072 | 
1073 |         assert len(episodic_mem_ids) == 4, "Did not store all expected episodic memories"
1074 | 
1075 |         # --- Trigger Promotion ---
1076 |         console.print(Rule("Triggering Memory Promotion", style="cyan"))
1077 |         for mem_id in episodic_mem_ids:
1078 |             for _ in range(6):
1079 |                 await safe_tool_call(
1080 |                     get_memory_by_id,
1081 |                     {"memory_id": mem_id},
1082 |                     f"Access Memory {mem_id[:8]}",
1083 |                     suppress_output=True,
1084 |                 )
1085 |             promo_res = await safe_tool_call(
1086 |                 promote_memory_level, {"memory_id": mem_id}, f"Attempt Promotion for {mem_id[:8]}"
1087 |             )
1088 |             assert (
1089 |                 promo_res
1090 |                 and promo_res.get("promoted")
1091 |                 and promo_res.get("new_level") == MemoryLevel.SEMANTIC.value
1092 |             ), f"Memory {mem_id} failed promotion check"
1093 |         console.print(
1094 |             "[green]  All episodic memories successfully accessed and promoted to Semantic.[/green]"
1095 |         )
1096 | 
1097 |         # --- Consolidation ---
1098 |         console.print(Rule("Consolidating Semantic Insights", style="cyan"))
1099 |         consolidation_res = await safe_tool_call(
1100 |             consolidate_memories,
1101 |             {
1102 |                 "workflow_id": wf_id,
1103 |                 "target_memories": episodic_mem_ids,
1104 |                 "consolidation_type": "insight",
1105 |                 "store_result": True,
1106 |                 "store_as_level": MemoryLevel.SEMANTIC.value,
1107 |                 "store_as_type": MemoryType.INSIGHT.value,
1108 |             },
1109 |             "Consolidate Failures into Insight",
1110 |         )
1111 |         assert consolidation_res and consolidation_res.get("success"), "Consolidation failed"
1112 |         insight_content = consolidation_res.get("consolidated_content", "").lower()
1113 |         insight_mem_id = consolidation_res.get("stored_memory_id")
1114 |         assert insight_mem_id, "Consolidated insight memory was not stored"
1115 |         assert (
1116 |             "rate limit" in insight_content
1117 |             or "429" in insight_content
1118 |             or "too many requests" in insight_content
1119 |         ), "Consolidated insight content missing expected keywords."
1120 |         console.print(
1121 |             f"[green]  Consolidated insight created (ID: {insight_mem_id[:8]}) and content seems correct.[/green]"
1122 |         )
1123 | 
1124 |         # <<< FIX: Verify embedding was stored for the insight >>>
1125 |         insight_details = await safe_tool_call(
1126 |             get_memory_by_id,
1127 |             {"memory_id": insight_mem_id},
1128 |             "Get Insight Details",
1129 |             suppress_output=True,
1130 |         )
1131 |         assert (
1132 |             insight_details
1133 |             and insight_details.get("success")
1134 |             and insight_details.get("embedding_id")
1135 |         ), "Consolidated insight seems to lack an embedding ID."
1136 |         insight_mem_content = insight_details.get("content", "")  # Store actual content for search
1137 |         console.print(
1138 |             f"[green]  Verified embedding exists for insight memory {insight_mem_id[:8]}.[/green]"
1139 |         )
1140 |         # <<< End FIX >>>
1141 | 
1142 |         # --- Proceduralization ---
1143 |         console.print(Rule("Creating Procedural Knowledge", style="cyan"))
1144 |         proc_res = await safe_tool_call(
1145 |             store_memory,
1146 |             {
1147 |                 "workflow_id": wf_id,
1148 |                 "memory_level": MemoryLevel.PROCEDURAL.value,
1149 |                 "memory_type": MemoryType.PROCEDURE.value,
1150 |                 "content": "If API returns 429 error, wait using exponential backoff (e.g., 1s, 2s, 4s...) before retrying.",
1151 |                 "description": "API Rate Limit Retry Strategy",
1152 |                 "tags": ["api", "retry", "backoff", "rate_limit"],
1153 |                 "importance": 8.0,
1154 |                 "confidence": 0.95,
1155 |             },
1156 |             "Store Procedural Memory (Retry Strategy)",
1157 |         )
1158 |         assert proc_res and proc_res.get("success"), "Failed to store procedural memory"
1159 |         procedural_mem_id = proc_res["memory_id"]
1160 |         console.print(f"[green]  Procedural memory created (ID: {procedural_mem_id[:8]})[/green]")
1161 | 
1162 |         # --- Querying Verification ---
1163 |         console.print(Rule("Verifying Knowledge Retrieval", style="cyan"))
1164 |         # <<< FIX: Use actual insight content for query >>>
1165 |         semantic_query = (
1166 |             f"How should the system handle {insight_mem_content[:100]}..."
1167 |             if insight_mem_content
1168 |             else "problem with API rate limits"
1169 |         )
1170 |         semantic_search_res = await safe_tool_call(
1171 |             search_semantic_memories,
1172 |             {"query": semantic_query, "workflow_id": wf_id, "limit": 3},
1173 |             "Semantic Search for Insight",
1174 |         )
1175 |         assert semantic_search_res and semantic_search_res.get("success"), "Semantic search failed"
1176 |         found_insight = any(
1177 |             m["memory_id"] == insight_mem_id for m in semantic_search_res.get("memories", [])
1178 |         )
1179 |         if not found_insight:
1180 |             console.print(
1181 |                 f"[yellow]Warning: Semantic search query '{semantic_query[:60]}...' did not retrieve expected insight {insight_mem_id[:8]}. Results: {[m['memory_id'][:8] for m in semantic_search_res.get('memories', [])]}[/yellow]"
1182 |             )
1183 |         # Don't assert strictly, as semantic match can be fuzzy
1184 |         # assert found_insight, "Consolidated insight memory not found via semantic search using its own content"
1185 |         console.print(
1186 |             f"[green]  Semantic search using insight content executed ({'Found expected' if found_insight else 'Did not find expected'} insight).[/green]"
1187 |         )
1188 |         # <<< End FIX >>>
1189 | 
1190 |         # Query for procedure
1191 |         procedural_query_res = await safe_tool_call(
1192 |             query_memories,
1193 |             {
1194 |                 "memory_level": MemoryLevel.PROCEDURAL.value,
1195 |                 "search_text": "API retry strategy",
1196 |                 "workflow_id": wf_id,
1197 |             },
1198 |             "Query for Procedural Memory",
1199 |         )
1200 |         assert procedural_query_res and procedural_query_res.get("success"), (
1201 |             "Procedural query failed"
1202 |         )
1203 |         found_procedure = any(
1204 |             m["memory_id"] == procedural_mem_id for m in procedural_query_res.get("memories", [])
1205 |         )
1206 |         assert found_procedure, "Procedural memory not found via query"
1207 |         console.print(
1208 |             "[green]  Filtered query successfully retrieved the procedural memory.[/green]"
1209 |         )
1210 | 
1211 |     except AssertionError as e:
1212 |         logger.error(f"Assertion failed during Extension 3: {e}", exc_info=True)
1213 |         console.print(f"[bold red]Assertion Failed:[/bold red] {e}")
1214 |     except Exception as e:
1215 |         logger.error(f"Error in Extension 3: {e}", exc_info=True)
1216 |         console.print(f"[bold red]Error in Extension 3:[/bold red] {e}")
1217 |     finally:
1218 |         console.print(Rule("Extension 3 Finished", style="green"))
1219 | 
1220 | 
1221 | async def run_extension_4_context_persistence():
1222 |     """Extension 4: Context Persistence and Working Memory Management"""
1223 |     console.print(
1224 |         Rule(
1225 |             "[bold green]Extension 4: Context Persistence & Working Memory[/bold green]",
1226 |             style="green",
1227 |         )
1228 |     )
1229 |     wf_id = None
1230 |     m_ids = {}
1231 |     state1_id = None
1232 |     original_state1_working_set = []
1233 |     retained_ids_from_optimize = []  # Store the *result* of optimization
1234 | 
1235 |     try:
1236 |         # --- Setup ---
1237 |         wf_res = await safe_tool_call(
1238 |             create_workflow,
1239 |             {"title": "Analyze Document X", "goal": "Extract key info from Doc X."},
1240 |             "Create Doc Analysis Workflow",
1241 |         )
1242 |         assert wf_res and wf_res.get("success"), "Failed to create workflow"
1243 |         wf_id = wf_res["workflow_id"]
1244 | 
1245 |         # --- Initial Analysis & Memory Storage ---
1246 |         console.print(Rule("Initial Analysis Phase", style="cyan"))
1247 |         mem_contents = {
1248 |             "M1": "Document Section 1: Introduction and background.",
1249 |             "M2": "Document Section 2: Core methodology described.",
1250 |             "M3": "Document Section 3: Results for Experiment A.",
1251 |             "M4": "Document Section 4: Results for Experiment B.",
1252 |             "M5": "Document Section 5: Discussion and initial conclusions.",
1253 |         }
1254 |         for i, (m_key, content) in enumerate(mem_contents.items()):
1255 |             mem_res = await safe_tool_call(
1256 |                 store_memory,
1257 |                 {
1258 |                     "workflow_id": wf_id,
1259 |                     "content": content,
1260 |                     "memory_type": MemoryType.OBSERVATION.value,
1261 |                     "description": f"Notes on {m_key}",
1262 |                     "importance": 5.0 + i * 0.2,
1263 |                 },
1264 |                 f"Store Memory {m_key}",
1265 |                 suppress_output=True,
1266 |             )
1267 |             assert mem_res and mem_res.get("success"), f"Failed to store memory {m_key}"
1268 |             m_ids[m_key] = mem_res["memory_id"]
1269 | 
1270 |         # --- Save State 1 ---
1271 |         console.print(Rule("Saving Initial State", style="cyan"))
1272 |         initial_working_set_to_save = [m_ids["M1"], m_ids["M2"], m_ids["M3"]]
1273 |         initial_focus = [m_ids["M2"]]
1274 |         state1_res = await safe_tool_call(
1275 |             save_cognitive_state,
1276 |             {
1277 |                 "workflow_id": wf_id,
1278 |                 "title": "Initial Section Analysis",
1279 |                 "working_memory_ids": initial_working_set_to_save,
1280 |                 "focus_area_ids": initial_focus,
1281 |             },
1282 |             "Save Cognitive State 1",
1283 |         )
1284 |         assert state1_res and state1_res.get("success"), "Failed to save state 1"
1285 |         state1_id = state1_res["state_id"]
1286 |         console.print(f"[cyan]  State 1 ID: {state1_id}[/cyan]")
1287 | 
1288 |         # Capture original working set immediately after saving
1289 |         load_for_original_res = await safe_tool_call(
1290 |             load_cognitive_state,
1291 |             {"workflow_id": wf_id, "state_id": state1_id},
1292 |             "Load State 1 Immediately to Capture Original WM",
1293 |             suppress_output=True,
1294 |         )
1295 |         assert load_for_original_res and load_for_original_res.get("success"), (
1296 |             "Failed to load state 1 immediately after save"
1297 |         )
1298 |         original_state1_working_set = load_for_original_res.get("working_memory_ids", [])
1299 |         assert set(original_state1_working_set) == set(initial_working_set_to_save), (
1300 |             "Immediate load WM doesn't match saved WM"
1301 |         )
1302 |         console.print(
1303 |             f"[dim]  Captured original State 1 working set: {original_state1_working_set}[/dim]"
1304 |         )
1305 | 
1306 |         # --- Simulate Interruption & Calculate Optimization ---
1307 |         console.print(
1308 |             Rule("Simulate Interruption & Calculate Optimization for State 1", style="cyan")
1309 |         )
1310 |         # Store unrelated memories (doesn't affect the saved state)
1311 |         mem6_res = await safe_tool_call(
1312 |             store_memory,
1313 |             {
1314 |                 "workflow_id": wf_id,
1315 |                 "content": "Unrelated thought about lunch.",
1316 |                 "memory_type": MemoryType.OBSERVATION.value,
1317 |             },
1318 |             "Store Unrelated Memory M6",
1319 |             suppress_output=True,
1320 |         )
1321 |         mem7_res = await safe_tool_call(
1322 |             store_memory,
1323 |             {
1324 |                 "workflow_id": wf_id,
1325 |                 "content": "Another unrelated idea.",
1326 |                 "memory_type": MemoryType.OBSERVATION.value,
1327 |             },
1328 |             "Store Unrelated Memory M7",
1329 |             suppress_output=True,
1330 |         )
1331 |         m_ids["M6"] = mem6_res["memory_id"]
1332 |         m_ids["M7"] = mem7_res["memory_id"]
1333 | 
1334 |         # Calculate optimization based on State 1's snapshot
1335 |         optimize_res = await safe_tool_call(
1336 |             optimize_working_memory,
1337 |             {"context_id": state1_id, "target_size": 1, "strategy": "balanced"},
1338 |             "Calculate Optimization for State 1 (Target 1)",
1339 |         )
1340 |         assert optimize_res and optimize_res.get("success"), "Optimization calculation failed"
1341 |         assert optimize_res["after_count"] == 1, (
1342 |             f"Optimization calculation did not yield target size 1, got {optimize_res['after_count']}"
1343 |         )
1344 |         retained_ids_from_optimize = optimize_res[
1345 |             "retained_memories"
1346 |         ]  # Store the calculated result
1347 |         console.print(
1348 |             f"[cyan]  Optimization calculation recommends retaining: {retained_ids_from_optimize}[/cyan]"
1349 |         )
1350 |         assert len(retained_ids_from_optimize) == 1, (
1351 |             "Optimization calculation should retain exactly 1 ID"
1352 |         )
1353 |         assert retained_ids_from_optimize[0] in original_state1_working_set, (
1354 |             "Optimization calculation retained an unexpected memory ID"
1355 |         )
1356 | 
1357 |         # --- Load State 1 & Verify (Should be Unchanged) ---
1358 |         console.print(Rule("Load State 1 Again and Verify Context Unchanged", style="cyan"))
1359 |         loaded_state_res = await safe_tool_call(
1360 |             load_cognitive_state,
1361 |             {"workflow_id": wf_id, "state_id": state1_id},
1362 |             "Load Cognitive State 1 (After Optimization Calculation)",
1363 |         )
1364 |         assert loaded_state_res and loaded_state_res.get("success"), "Failed to load state 1"
1365 |         loaded_working_ids = loaded_state_res.get("working_memory_ids", [])
1366 |         # <<< ASSERTION SHOULD NOW PASS with refactored optimize_working_memory >>>
1367 |         assert set(loaded_working_ids) == set(original_state1_working_set), (
1368 |             f"Loaded working memory {loaded_working_ids} does not match original saved state {original_state1_working_set}"
1369 |         )
1370 |         console.print(
1371 |             "[green]  Loaded state working memory matches original saved state (as expected). Test Passed.[/green]"
1372 |         )
1373 | 
1374 |         # --- Test Focus on Loaded State ---
1375 |         # This now operates based on the original working memory loaded from the state
1376 |         focus_res = await safe_tool_call(
1377 |             auto_update_focus,
1378 |             {"context_id": state1_id},
1379 |             "Auto Update Focus on Loaded (Original) State",
1380 |         )
1381 |         assert focus_res and focus_res.get("success"), "Auto update focus failed"
1382 |         new_focus_id = focus_res.get("new_focal_memory_id")
1383 |         # The focus should be one of the *original* working set members based on relevance
1384 |         assert new_focus_id in original_state1_working_set, (
1385 |             f"New focus ID {new_focus_id} is not in the original working set {original_state1_working_set}"
1386 |         )
1387 |         console.print(
1388 |             f"[green]  Auto-focus selected a reasonable memory ID from original set: {new_focus_id[:8]}...[/green]"
1389 |         )
1390 | 
1391 |         # --- Continue Task & Test Adding to Working Memory ---
1392 |         console.print(Rule("Continue Task & Add to Working Memory of State 1", style="cyan"))
1393 |         mem8_res = await safe_tool_call(
1394 |             store_memory,
1395 |             {
1396 |                 "workflow_id": wf_id,
1397 |                 "content": "Section 6: Key Conclusion",
1398 |                 "memory_type": MemoryType.OBSERVATION.value,
1399 |                 "description": "Notes on M8",
1400 |                 "importance": 8.0,
1401 |             },
1402 |             "Store New Relevant Memory M8",
1403 |             suppress_output=True,
1404 |         )
1405 |         assert mem8_res and mem8_res.get("success"), "Failed to store M8"
1406 |         m_ids["M8"] = mem8_res["memory_id"]
1407 | 
1408 |         # Call focus_memory with add_to_working=True. This uses _add_to_active_memories
1409 |         # which *will* modify the state record referenced by state1_id.
1410 |         focus_m8_res = await safe_tool_call(
1411 |             focus_memory,
1412 |             {"memory_id": m_ids["M8"], "context_id": state1_id, "add_to_working": True},
1413 |             f"Focus on M8 ({m_ids['M8'][:8]}) and Add to Working Memory (Context {state1_id[:8]})",
1414 |         )
1415 |         assert focus_m8_res and focus_m8_res.get("success"), "Focusing on M8 failed"
1416 |         assert focus_m8_res.get("added_to_working"), (
1417 |             "M8 was not reported as added to working memory"
1418 |         )
1419 | 
1420 |         # Verify working memory contents *after* adding M8
1421 |         # This should reflect the original working set PLUS M8 (assuming limit allows)
1422 |         wm_after_add_res = await safe_tool_call(
1423 |             get_working_memory, {"context_id": state1_id}, "Get Working Memory After Adding M8"
1424 |         )
1425 |         assert wm_after_add_res and wm_after_add_res.get("success"), (
1426 |             "Failed to get working memory after adding M8"
1427 |         )
1428 |         wm_after_add_ids = [m["memory_id"] for m in wm_after_add_res.get("working_memories", [])]
1429 | 
1430 |         assert m_ids["M8"] in wm_after_add_ids, (
1431 |             "M8 is not present in working memory after add attempt"
1432 |         )
1433 |         # The expected set now contains the original IDs plus M8
1434 |         expected_final_wm = set(original_state1_working_set + [m_ids["M8"]])
1435 |         # Check if eviction occurred based on the default limit (likely 20, so no eviction)
1436 |         limit = config.agent_memory.max_working_memory_size
1437 |         if len(expected_final_wm) > limit:
1438 |             # If eviction *was* expected, the assertion needs refinement based on relevance
1439 |             console.print(
1440 |                 f"[yellow]Warning: Expected working memory size ({len(expected_final_wm)}) exceeds limit ({limit}). Eviction logic not fully tested here.[/yellow]"
1441 |             )
1442 |             # For now, just check M8 is present and size is <= limit
1443 |             assert len(wm_after_add_ids) <= limit, (
1444 |                 f"Working memory size {len(wm_after_add_ids)} exceeds limit {limit}"
1445 |             )
1446 |         else:
1447 |             # No eviction expected
1448 |             assert set(wm_after_add_ids) == expected_final_wm, (
1449 |                 f"Final working memory {set(wm_after_add_ids)} doesn't match expected {expected_final_wm} after adding M8 to original state"
1450 |             )
1451 |         console.print(
1452 |             f"[green]  Memory M8 successfully added to working memory for state {state1_id[:8]}. Final WM check passed.[/green]"
1453 |         )
1454 | 
1455 |     except AssertionError as e:
1456 |         logger.error(f"Assertion failed during Extension 4: {e}", exc_info=True)
1457 |         console.print(f"[bold red]Assertion Failed:[/bold red] {e}")
1458 |     except Exception as e:
1459 |         logger.error(f"Error in Extension 4: {e}", exc_info=True)
1460 |         console.print(f"[bold red]Error in Extension 4:[/bold red] {e}")
1461 |     finally:
1462 |         console.print(Rule("Extension 4 Finished", style="green"))
1463 | 
1464 | 
1465 | # --- Main Execution Logic ---
1466 | async def main():
1467 |     """Run the advanced Unified Memory System demonstration suite."""
1468 |     console.print(
1469 |         Rule(
1470 |             "[bold magenta]Advanced Unified Memory System Tools Demo[/bold magenta]", style="white"
1471 |         )
1472 |     )
1473 |     exit_code = 0
1474 | 
1475 |     try:
1476 |         await setup_advanced_demo()
1477 | 
1478 |         # --- Run Demo Extensions ---
1479 |         await run_extension_1_goal_decomposition()
1480 |         await run_extension_2_dynamic_adaptation()
1481 |         await run_extension_3_knowledge_building()
1482 |         await run_extension_4_context_persistence()
1483 | 
1484 |         logger.success(
1485 |             "Advanced Unified Memory System Demo completed successfully!", emoji_key="complete"
1486 |         )
1487 |         console.print(Rule("[bold green]Advanced Demo Finished[/bold green]", style="green"))
1488 | 
1489 |     except Exception as e:
1490 |         logger.critical(
1491 |             f"Advanced demo crashed unexpectedly: {str(e)}", emoji_key="critical", exc_info=True
1492 |         )
1493 |         console.print(f"\n[bold red]CRITICAL ERROR:[/bold red] {escape(str(e))}")
1494 |         console.print_exception(show_locals=False)
1495 |         exit_code = 1
1496 | 
1497 |     finally:
1498 |         console.print(Rule("Cleanup Advanced Demo", style="dim"))
1499 |         await cleanup_advanced_demo()
1500 | 
1501 |     return exit_code
1502 | 
1503 | 
1504 | if __name__ == "__main__":
1505 |     # Ensure the event loop policy is set for Windows if necessary
1506 |     # (Though typically needed for ProactorEventLoop, might help avoid some uvloop issues sometimes)
1507 |     # if sys.platform == "win32":
1508 |     #     asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
1509 |     final_exit_code = asyncio.run(main())
1510 |     sys.exit(final_exit_code)
1511 | 
```

--------------------------------------------------------------------------------
/ultimate_mcp_server/working_memory_api.py:
--------------------------------------------------------------------------------

```python
   1 | """
   2 | Working Memory Dashboard API
   3 | Provides real-time working memory management and optimization endpoints for the UMS Explorer.
   4 | """
   5 | 
   6 | import asyncio
   7 | import difflib
   8 | import hashlib
   9 | import json
  10 | import sqlite3
  11 | import time
  12 | from dataclasses import asdict, dataclass
  13 | from typing import Dict, List, Optional
  14 | 
  15 | from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
  16 | from pydantic import BaseModel
  17 | 
  18 | 
  19 | @dataclass
  20 | class WorkingMemoryItem:
  21 |     """Enhanced memory item with working memory specific metadata."""
  22 |     memory_id: str
  23 |     content: str
  24 |     memory_type: str
  25 |     memory_level: str
  26 |     importance: int
  27 |     confidence: float
  28 |     created_at: float
  29 |     last_accessed_at: Optional[float]
  30 |     access_count: int
  31 |     workflow_id: Optional[str]
  32 |     
  33 |     # Working memory specific fields
  34 |     temperature: float = 0.0  # Activity level (0-100)
  35 |     priority: str = "medium"  # critical, high, medium, low
  36 |     access_frequency: float = 0.0  # Normalized access frequency
  37 |     retention_score: float = 0.0  # How likely to remain in working memory
  38 |     added_at: float = 0.0  # When added to working memory
  39 | 
  40 | 
  41 | @dataclass
  42 | class QualityIssue:
  43 |     """Represents a memory quality issue."""
  44 |     issue_id: str
  45 |     issue_type: str  # duplicate, orphaned, low_quality, stale, corrupted
  46 |     severity: str  # critical, high, medium, low
  47 |     memory_ids: List[str]
  48 |     title: str
  49 |     description: str
  50 |     recommendation: str
  51 |     impact_score: float
  52 |     auto_fixable: bool
  53 |     estimated_savings: Dict[str, float]  # storage, performance, clarity
  54 |     metadata: Dict
  55 | 
  56 | 
  57 | @dataclass
  58 | class QualityAnalysisResult:
  59 |     """Result of memory quality analysis."""
  60 |     total_memories: int
  61 |     issues_found: int
  62 |     duplicates: int
  63 |     orphaned: int
  64 |     low_quality: int
  65 |     stale_memories: int
  66 |     corrupted: int
  67 |     overall_score: float  # 0-100
  68 |     issues: List[QualityIssue]
  69 |     recommendations: List[str]
  70 |     analysis_time: float
  71 | 
  72 | 
  73 | @dataclass
  74 | class DuplicateCluster:
  75 |     """Group of duplicate or similar memories."""
  76 |     cluster_id: str
  77 |     memory_ids: List[str]
  78 |     similarity_score: float
  79 |     primary_memory_id: str  # Best quality memory in cluster
  80 |     duplicate_count: int
  81 |     content_preview: str
  82 |     metadata: Dict
  83 | 
  84 | 
  85 | @dataclass
  86 | class BulkOperation:
  87 |     """Represents a bulk operation on memories."""
  88 |     operation_id: str
  89 |     operation_type: str  # delete, merge, update, archive
  90 |     memory_ids: List[str]
  91 |     preview_changes: List[Dict]
  92 |     estimated_impact: Dict[str, float]
  93 |     reversible: bool
  94 |     confirmation_required: bool
  95 | 
  96 | 
  97 | @dataclass
  98 | class WorkingMemoryStats:
  99 |     """Working memory statistics and metrics."""
 100 |     active_count: int
 101 |     capacity: int
 102 |     pressure: float  # 0-100%
 103 |     temperature: float  # Average activity level
 104 |     focus_score: float  # 0-100%
 105 |     efficiency: float  # 0-100%
 106 |     avg_retention_time: float
 107 |     total_accesses: int
 108 |     last_updated: float
 109 | 
 110 | 
 111 | @dataclass
 112 | class OptimizationSuggestion:
 113 |     """Memory optimization suggestion."""
 114 |     id: str
 115 |     title: str
 116 |     description: str
 117 |     priority: str  # high, medium, low
 118 |     impact: str  # High, Medium, Low
 119 |     icon: str
 120 |     action: str
 121 |     confidence: float = 0.0
 122 |     estimated_improvement: Dict[str, float] = None
 123 | 
 124 | 
 125 | class WorkingMemoryRequest(BaseModel):
 126 |     memory_id: str
 127 | 
 128 | 
 129 | class OptimizationRequest(BaseModel):
 130 |     suggestion_id: str
 131 | 
 132 | 
 133 | class FocusModeRequest(BaseModel):
 134 |     mode: str  # normal, deep, creative, analytical, maintenance
 135 |     retention_time: Optional[int] = None
 136 |     max_working_memory: Optional[int] = None
 137 | 
 138 | 
 139 | class QualityAnalysisRequest(BaseModel):
 140 |     analysis_type: str = "comprehensive"  # comprehensive, duplicates, orphaned, low_quality
 141 |     include_stale: bool = True
 142 |     include_low_importance: bool = True
 143 |     similarity_threshold: float = 0.85
 144 |     stale_threshold_days: int = 30
 145 | 
 146 | 
 147 | class BulkOperationRequest(BaseModel):
 148 |     operation_type: str  # delete, merge, archive, update
 149 |     memory_ids: List[str]
 150 |     merge_strategy: Optional[str] = "preserve_highest_importance"  # For merge operations
 151 |     target_memory_id: Optional[str] = None  # For merge operations
 152 |     update_data: Optional[Dict] = None  # For update operations
 153 | 
 154 | 
 155 | class MemoryQualityInspector:
 156 |     """Core memory quality analysis and management logic."""
 157 |     
 158 |     def __init__(self, db_path: str = "storage/unified_agent_memory.db"):
 159 |         self.db_path = db_path
 160 |         
 161 |     def get_db_connection(self):
 162 |         """Get database connection."""
 163 |         conn = sqlite3.connect(self.db_path)
 164 |         conn.row_factory = sqlite3.Row
 165 |         return conn
 166 |     
 167 |     def calculate_content_hash(self, content: str) -> str:
 168 |         """Calculate content hash for duplicate detection."""
 169 |         normalized = content.strip().lower()
 170 |         return hashlib.md5(normalized.encode()).hexdigest()
 171 |     
 172 |     def calculate_similarity(self, content1: str, content2: str) -> float:
 173 |         """Calculate content similarity using difflib."""
 174 |         normalized1 = content1.strip().lower()
 175 |         normalized2 = content2.strip().lower()
 176 |         
 177 |         # Use sequence matcher for similarity
 178 |         similarity = difflib.SequenceMatcher(None, normalized1, normalized2).ratio()
 179 |         return similarity
 180 |     
 181 |     def detect_duplicates(self, memories: List[Dict], threshold: float = 0.85) -> List[DuplicateCluster]:
 182 |         """Detect duplicate memories using content similarity."""
 183 |         clusters = []
 184 |         processed_ids = set()
 185 |         
 186 |         for i, memory1 in enumerate(memories):
 187 |             if memory1['memory_id'] in processed_ids:
 188 |                 continue
 189 |                 
 190 |             cluster_memories = [memory1]
 191 |             cluster_ids = {memory1['memory_id']}
 192 |             
 193 |             for _j, memory2 in enumerate(memories[i+1:], i+1):
 194 |                 if memory2['memory_id'] in processed_ids:
 195 |                     continue
 196 |                     
 197 |                 similarity = self.calculate_similarity(memory1['content'], memory2['content'])
 198 |                 
 199 |                 if similarity >= threshold:
 200 |                     cluster_memories.append(memory2)
 201 |                     cluster_ids.add(memory2['memory_id'])
 202 |             
 203 |             if len(cluster_memories) > 1:
 204 |                 # Find the best quality memory (highest importance * confidence)
 205 |                 primary = max(cluster_memories, 
 206 |                             key=lambda m: (m.get('importance', 1) * m.get('confidence', 0.5)))
 207 |                 
 208 |                 cluster = DuplicateCluster(
 209 |                     cluster_id=f"dup_{memory1['memory_id'][:8]}",
 210 |                     memory_ids=list(cluster_ids),
 211 |                     similarity_score=max(self.calculate_similarity(memory1['content'], m['content']) 
 212 |                                        for m in cluster_memories[1:]),
 213 |                     primary_memory_id=primary['memory_id'],
 214 |                     duplicate_count=len(cluster_memories) - 1,
 215 |                     content_preview=memory1['content'][:100] + "..." if len(memory1['content']) > 100 else memory1['content'],
 216 |                     metadata={
 217 |                         'avg_importance': sum(m.get('importance', 1) for m in cluster_memories) / len(cluster_memories),
 218 |                         'avg_confidence': sum(m.get('confidence', 0.5) for m in cluster_memories) / len(cluster_memories),
 219 |                         'total_size': sum(len(m['content']) for m in cluster_memories)
 220 |                     }
 221 |                 )
 222 |                 clusters.append(cluster)
 223 |                 processed_ids.update(cluster_ids)
 224 |         
 225 |         return clusters
 226 |     
 227 |     def detect_orphaned_memories(self, memories: List[Dict]) -> List[Dict]:
 228 |         """Detect orphaned memories not connected to any workflow or relationship."""
 229 |         conn = self.get_db_connection()
 230 |         try:
 231 |             cursor = conn.cursor()
 232 |             
 233 |             orphaned = []
 234 |             for memory in memories:
 235 |                 memory_id = memory['memory_id']
 236 |                 
 237 |                 # Check if memory has workflow association
 238 |                 has_workflow = memory.get('workflow_id') is not None
 239 |                 
 240 |                 # Check if memory is linked to other memories
 241 |                 cursor.execute("""
 242 |                     SELECT COUNT(*) as count FROM memory_links 
 243 |                     WHERE source_memory_id = ? OR target_memory_id = ?
 244 |                 """, (memory_id, memory_id))
 245 |                 
 246 |                 link_count = cursor.fetchone()['count']
 247 |                 
 248 |                 # Check if memory is referenced in goals or actions
 249 |                 cursor.execute("""
 250 |                     SELECT COUNT(*) as action_count FROM actions 
 251 |                     WHERE memory_id = ? OR input_data LIKE ? OR output_data LIKE ?
 252 |                 """, (memory_id, f'%{memory_id}%', f'%{memory_id}%'))
 253 |                 
 254 |                 action_refs = cursor.fetchone()['action_count']
 255 |                 
 256 |                 cursor.execute("""
 257 |                     SELECT COUNT(*) as goal_count FROM goals 
 258 |                     WHERE memory_id = ? OR description LIKE ?
 259 |                 """, (memory_id, f'%{memory_id}%'))
 260 |                 
 261 |                 goal_refs = cursor.fetchone()['goal_count']
 262 |                 
 263 |                 # Memory is orphaned if it has no workflow, no links, and no references
 264 |                 if not has_workflow and link_count == 0 and action_refs == 0 and goal_refs == 0:
 265 |                     orphaned.append({
 266 |                         **memory,
 267 |                         'orphan_score': self.calculate_orphan_score(memory),
 268 |                         'isolation_level': 'complete'
 269 |                     })
 270 |                 elif link_count == 0 and (action_refs == 0 or goal_refs == 0):
 271 |                     orphaned.append({
 272 |                         **memory,
 273 |                         'orphan_score': self.calculate_orphan_score(memory),
 274 |                         'isolation_level': 'partial'
 275 |                     })
 276 |             
 277 |             return orphaned
 278 |             
 279 |         finally:
 280 |             conn.close()
 281 |     
 282 |     def calculate_orphan_score(self, memory: Dict) -> float:
 283 |         """Calculate how orphaned a memory is (0-100, higher = more orphaned)."""
 284 |         score = 50  # Base score
 285 |         
 286 |         # Adjust based on importance (lower importance = more likely orphan)
 287 |         importance = memory.get('importance', 1)
 288 |         score += (5 - importance) * 10
 289 |         
 290 |         # Adjust based on confidence (lower confidence = more likely orphan)
 291 |         confidence = memory.get('confidence', 0.5)
 292 |         score += (0.5 - confidence) * 50
 293 |         
 294 |         # Adjust based on age (older = more likely to be orphaned)
 295 |         created_at = memory.get('created_at', time.time())
 296 |         age_days = (time.time() - created_at) / 86400
 297 |         if age_days > 30:
 298 |             score += min(20, age_days / 10)
 299 |         
 300 |         # Adjust based on access patterns
 301 |         access_count = memory.get('access_count', 0)
 302 |         if access_count == 0:
 303 |             score += 15
 304 |         elif access_count < 3:
 305 |             score += 10
 306 |         
 307 |         return min(100, max(0, score))
 308 |     
 309 |     def analyze_memory_quality(self, memory: Dict) -> Dict:
 310 |         """Analyze individual memory quality."""
 311 |         quality_score = 50  # Base score
 312 |         issues = []
 313 |         
 314 |         content = memory.get('content', '')
 315 |         importance = memory.get('importance', 1)
 316 |         confidence = memory.get('confidence', 0.5)
 317 |         
 318 |         # Content quality checks
 319 |         if len(content) < 10:
 320 |             issues.append("Content too short")
 321 |             quality_score -= 20
 322 |         elif len(content) > 10000:
 323 |             issues.append("Content extremely long")
 324 |             quality_score -= 10
 325 |         
 326 |         # Check for common quality issues
 327 |         if content.count('\n') / max(1, len(content)) > 0.1:  # Too many line breaks
 328 |             issues.append("Excessive line breaks")
 329 |             quality_score -= 5
 330 |         
 331 |         if len(set(content.split())) / max(1, len(content.split())) < 0.3:  # Low vocabulary diversity
 332 |             issues.append("Low vocabulary diversity")
 333 |             quality_score -= 10
 334 |         
 335 |         # Importance and confidence checks
 336 |         if importance < 3:
 337 |             issues.append("Low importance rating")
 338 |             quality_score -= 10
 339 |         
 340 |         if confidence < 0.3:
 341 |             issues.append("Low confidence rating")
 342 |             quality_score -= 15
 343 |         
 344 |         # Memory type consistency
 345 |         memory_type = memory.get('memory_type', '')
 346 |         memory_level = memory.get('memory_level', '')
 347 |         
 348 |         if not memory_type:
 349 |             issues.append("Missing memory type")
 350 |             quality_score -= 15
 351 |         
 352 |         if not memory_level:
 353 |             issues.append("Missing memory level")
 354 |             quality_score -= 15
 355 |         
 356 |         # Check for encoding issues or corruption
 357 |         try:
 358 |             content.encode('utf-8').decode('utf-8')
 359 |         except UnicodeError:
 360 |             issues.append("Encoding corruption detected")
 361 |             quality_score -= 25
 362 |         
 363 |         # Age and staleness
 364 |         created_at = memory.get('created_at', time.time())
 365 |         age_days = (time.time() - created_at) / 86400
 366 |         
 367 |         if age_days > 90 and memory.get('access_count', 0) == 0:
 368 |             issues.append("Stale memory (old and unaccessed)")
 369 |             quality_score -= 20
 370 |         
 371 |         return {
 372 |             'quality_score': max(0, min(100, quality_score)),
 373 |             'issues': issues,
 374 |             'recommendations': self.generate_quality_recommendations(memory, issues)
 375 |         }
 376 |     
 377 |     def generate_quality_recommendations(self, memory: Dict, issues: List[str]) -> List[str]:
 378 |         """Generate recommendations for improving memory quality."""
 379 |         recommendations = []
 380 |         
 381 |         if "Content too short" in issues:
 382 |             recommendations.append("Consider expanding content with more context or details")
 383 |         
 384 |         if "Content extremely long" in issues:
 385 |             recommendations.append("Consider breaking into smaller, focused memories")
 386 |         
 387 |         if "Low importance rating" in issues:
 388 |             recommendations.append("Review and adjust importance rating if memory is valuable")
 389 |         
 390 |         if "Low confidence rating" in issues:
 391 |             recommendations.append("Verify information accuracy and update confidence")
 392 |         
 393 |         if "Missing memory type" in issues:
 394 |             recommendations.append("Assign appropriate memory type classification")
 395 |         
 396 |         if "Stale memory (old and unaccessed)" in issues:
 397 |             recommendations.append("Archive or delete if no longer relevant")
 398 |         
 399 |         if "Encoding corruption detected" in issues:
 400 |             recommendations.append("Critical: Clean up encoding issues immediately")
 401 |         
 402 |         return recommendations
 403 |     
 404 |     async def perform_quality_analysis(self, request: QualityAnalysisRequest) -> QualityAnalysisResult:
 405 |         """Perform comprehensive memory quality analysis."""
 406 |         start_time = time.time()
 407 |         
 408 |         conn = self.get_db_connection()
 409 |         try:
 410 |             cursor = conn.cursor()
 411 |             
 412 |             # Get all memories
 413 |             cursor.execute("SELECT * FROM memories ORDER BY created_at DESC")
 414 |             memories = [dict(row) for row in cursor.fetchall()]
 415 |             
 416 |             total_memories = len(memories)
 417 |             issues = []
 418 |             
 419 |             # Detect duplicates
 420 |             duplicates = []
 421 |             if request.analysis_type in ['comprehensive', 'duplicates']:
 422 |                 duplicate_clusters = self.detect_duplicates(memories, request.similarity_threshold)
 423 |                 for cluster in duplicate_clusters:
 424 |                     issue = QualityIssue(
 425 |                         issue_id=f"dup_{cluster.cluster_id}",
 426 |                         issue_type="duplicate",
 427 |                         severity="medium" if cluster.duplicate_count <= 2 else "high",
 428 |                         memory_ids=cluster.memory_ids,
 429 |                         title=f"Duplicate memories ({cluster.duplicate_count} duplicates)",
 430 |                         description=f"Found {cluster.duplicate_count} duplicate memories with {cluster.similarity_score:.2%} similarity",
 431 |                         recommendation=f"Merge duplicates into primary memory {cluster.primary_memory_id}",
 432 |                         impact_score=cluster.duplicate_count * 10,
 433 |                         auto_fixable=True,
 434 |                         estimated_savings={
 435 |                             'storage': len(cluster.content_preview) * cluster.duplicate_count * 0.8,
 436 |                             'performance': cluster.duplicate_count * 5,
 437 |                             'clarity': cluster.duplicate_count * 15
 438 |                         },
 439 |                         metadata=cluster.metadata
 440 |                     )
 441 |                     issues.append(issue)
 442 |                     duplicates.extend(cluster.memory_ids[1:])  # Exclude primary
 443 |             
 444 |             # Detect orphaned memories
 445 |             orphaned = []
 446 |             if request.analysis_type in ['comprehensive', 'orphaned']:
 447 |                 orphaned_memories = self.detect_orphaned_memories(memories)
 448 |                 for orphan in orphaned_memories:
 449 |                     issue = QualityIssue(
 450 |                         issue_id=f"orphan_{orphan['memory_id'][:8]}",
 451 |                         issue_type="orphaned",
 452 |                         severity="low" if orphan['orphan_score'] < 70 else "medium",
 453 |                         memory_ids=[orphan['memory_id']],
 454 |                         title=f"Orphaned memory (isolation: {orphan['isolation_level']})",
 455 |                         description="Memory has no connections to workflows, goals, or other memories",
 456 |                         recommendation="Connect to relevant workflow or consider archiving",
 457 |                         impact_score=orphan['orphan_score'],
 458 |                         auto_fixable=orphan['isolation_level'] == 'complete' and orphan['orphan_score'] > 80,
 459 |                         estimated_savings={
 460 |                             'clarity': orphan['orphan_score'] * 0.5,
 461 |                             'organization': 20
 462 |                         },
 463 |                         metadata={'orphan_score': orphan['orphan_score'], 'isolation_level': orphan['isolation_level']}
 464 |                     )
 465 |                     issues.append(issue)
 466 |                     orphaned.append(orphan['memory_id'])
 467 |             
 468 |             # Analyze individual memory quality
 469 |             low_quality = []
 470 |             corrupted = []
 471 |             if request.analysis_type in ['comprehensive', 'low_quality']:
 472 |                 for memory in memories:
 473 |                     quality_analysis = self.analyze_memory_quality(memory)
 474 |                     
 475 |                     if quality_analysis['quality_score'] < 30:
 476 |                         issue = QualityIssue(
 477 |                             issue_id=f"quality_{memory['memory_id'][:8]}",
 478 |                             issue_type="low_quality",
 479 |                             severity="high" if quality_analysis['quality_score'] < 20 else "medium",
 480 |                             memory_ids=[memory['memory_id']],
 481 |                             title=f"Low quality memory (score: {quality_analysis['quality_score']})",
 482 |                             description=f"Quality issues: {', '.join(quality_analysis['issues'])}",
 483 |                             recommendation='; '.join(quality_analysis['recommendations']),
 484 |                             impact_score=50 - quality_analysis['quality_score'],
 485 |                             auto_fixable=False,
 486 |                             estimated_savings={'quality': 50 - quality_analysis['quality_score']},
 487 |                             metadata={'quality_analysis': quality_analysis}
 488 |                         )
 489 |                         issues.append(issue)
 490 |                         low_quality.append(memory['memory_id'])
 491 |                     
 492 |                     # Check for corruption
 493 |                     if "Encoding corruption detected" in quality_analysis['issues']:
 494 |                         corrupted.append(memory['memory_id'])
 495 |             
 496 |             # Detect stale memories
 497 |             stale_memories = []
 498 |             if request.include_stale:
 499 |                 stale_cutoff = time.time() - (request.stale_threshold_days * 86400)
 500 |                 for memory in memories:
 501 |                     if (memory.get('created_at', time.time()) < stale_cutoff and 
 502 |                         memory.get('access_count', 0) == 0 and
 503 |                         memory.get('importance', 1) < 5):
 504 |                         
 505 |                         issue = QualityIssue(
 506 |                             issue_id=f"stale_{memory['memory_id'][:8]}",
 507 |                             issue_type="stale",
 508 |                             severity="low",
 509 |                             memory_ids=[memory['memory_id']],
 510 |                             title=f"Stale memory ({(time.time() - memory.get('created_at', time.time())) / 86400:.0f} days old)",
 511 |                             description="Old memory with no recent access and low importance",
 512 |                             recommendation="Archive or delete if no longer relevant",
 513 |                             impact_score=min(30, (time.time() - memory.get('created_at', time.time())) / 86400 * 0.5),
 514 |                             auto_fixable=True,
 515 |                             estimated_savings={'storage': len(memory.get('content', ''))},
 516 |                             metadata={'age_days': (time.time() - memory.get('created_at', time.time())) / 86400}
 517 |                         )
 518 |                         issues.append(issue)
 519 |                         stale_memories.append(memory['memory_id'])
 520 |             
 521 |             # Calculate overall quality score
 522 |             issues_count = len(issues)
 523 |             overall_score = max(0, 100 - (issues_count * 5) - (len(duplicates) * 2) - (len(orphaned) * 1))
 524 |             
 525 |             # Generate high-level recommendations
 526 |             recommendations = []
 527 |             if len(duplicates) > 10:
 528 |                 recommendations.append("High number of duplicates detected. Run bulk duplicate cleanup.")
 529 |             if len(orphaned) > total_memories * 0.2:
 530 |                 recommendations.append("Many orphaned memories. Review workflow organization.")
 531 |             if len(low_quality) > total_memories * 0.1:
 532 |                 recommendations.append("Quality issues detected. Review content standards.")
 533 |             if len(stale_memories) > 50:
 534 |                 recommendations.append("Archive old, unused memories to improve performance.")
 535 |             
 536 |             analysis_time = time.time() - start_time
 537 |             
 538 |             return QualityAnalysisResult(
 539 |                 total_memories=total_memories,
 540 |                 issues_found=issues_count,
 541 |                 duplicates=len(duplicates),
 542 |                 orphaned=len(orphaned),
 543 |                 low_quality=len(low_quality),
 544 |                 stale_memories=len(stale_memories),
 545 |                 corrupted=len(corrupted),
 546 |                 overall_score=overall_score,
 547 |                 issues=issues,
 548 |                 recommendations=recommendations,
 549 |                 analysis_time=analysis_time
 550 |             )
 551 |             
 552 |         finally:
 553 |             conn.close()
 554 |     
 555 |     async def preview_bulk_operation(self, request: BulkOperationRequest) -> BulkOperation:
 556 |         """Preview bulk operation changes before execution."""
 557 |         operation_id = f"bulk_{int(time.time())}"
 558 |         
 559 |         conn = self.get_db_connection()
 560 |         try:
 561 |             cursor = conn.cursor()
 562 |             
 563 |             # Get affected memories
 564 |             placeholders = ','.join('?' * len(request.memory_ids))
 565 |             cursor.execute(f"SELECT * FROM memories WHERE memory_id IN ({placeholders})", 
 566 |                          request.memory_ids)
 567 |             memories = [dict(row) for row in cursor.fetchall()]
 568 |             
 569 |             preview_changes = []
 570 |             estimated_impact = {'memories_affected': len(memories)}
 571 |             
 572 |             if request.operation_type == "delete":
 573 |                 for memory in memories:
 574 |                     preview_changes.append({
 575 |                         'action': 'delete',
 576 |                         'memory_id': memory['memory_id'],
 577 |                         'content_preview': memory['content'][:100] + "..." if len(memory['content']) > 100 else memory['content'],
 578 |                         'impact': 'Memory will be permanently deleted'
 579 |                     })
 580 |                 estimated_impact['storage_freed'] = sum(len(m['content']) for m in memories)
 581 |                 
 582 |             elif request.operation_type == "merge":
 583 |                 if request.target_memory_id:
 584 |                     target = next((m for m in memories if m['memory_id'] == request.target_memory_id), None)
 585 |                     if target:
 586 |                         others = [m for m in memories if m['memory_id'] != request.target_memory_id]
 587 |                         preview_changes.append({
 588 |                             'action': 'merge_target',
 589 |                             'memory_id': target['memory_id'],
 590 |                             'impact': f'Will be kept as primary memory, enhanced with content from {len(others)} others'
 591 |                         })
 592 |                         for other in others:
 593 |                             preview_changes.append({
 594 |                                 'action': 'merge_source',
 595 |                                 'memory_id': other['memory_id'],
 596 |                                 'impact': 'Content will be merged into target, then deleted'
 597 |                             })
 598 |                 
 599 |             elif request.operation_type == "archive":
 600 |                 for memory in memories:
 601 |                     preview_changes.append({
 602 |                         'action': 'archive',
 603 |                         'memory_id': memory['memory_id'],
 604 |                         'impact': 'Memory will be marked as archived (soft delete)'
 605 |                     })
 606 |             
 607 |             return BulkOperation(
 608 |                 operation_id=operation_id,
 609 |                 operation_type=request.operation_type,
 610 |                 memory_ids=request.memory_ids,
 611 |                 preview_changes=preview_changes,
 612 |                 estimated_impact=estimated_impact,
 613 |                 reversible=request.operation_type in ['archive'],
 614 |                 confirmation_required=request.operation_type in ['delete', 'merge']
 615 |             )
 616 |             
 617 |         finally:
 618 |             conn.close()
 619 |     
 620 |     async def execute_bulk_operation(self, operation: BulkOperation) -> Dict:
 621 |         """Execute bulk operation with safety checks."""
 622 |         conn = self.get_db_connection()
 623 |         try:
 624 |             cursor = conn.cursor()
 625 |             results = {'success': 0, 'failed': 0, 'errors': []}
 626 |             
 627 |             if operation.operation_type == "delete":
 628 |                 for memory_id in operation.memory_ids:
 629 |                     try:
 630 |                         # Delete related links first
 631 |                         cursor.execute("DELETE FROM memory_links WHERE source_memory_id = ? OR target_memory_id = ?", 
 632 |                                      (memory_id, memory_id))
 633 |                         # Delete memory
 634 |                         cursor.execute("DELETE FROM memories WHERE memory_id = ?", (memory_id,))
 635 |                         results['success'] += 1
 636 |                     except Exception as e:
 637 |                         results['failed'] += 1
 638 |                         results['errors'].append(f"Failed to delete {memory_id}: {str(e)}")
 639 |             
 640 |             elif operation.operation_type == "archive":
 641 |                 for memory_id in operation.memory_ids:
 642 |                     try:
 643 |                         cursor.execute("UPDATE memories SET archived = 1 WHERE memory_id = ?", (memory_id,))
 644 |                         results['success'] += 1
 645 |                     except Exception as e:
 646 |                         results['failed'] += 1
 647 |                         results['errors'].append(f"Failed to archive {memory_id}: {str(e)}")
 648 |             
 649 |             conn.commit()
 650 |             return results
 651 |             
 652 |         except Exception as e:
 653 |             conn.rollback()
 654 |             raise HTTPException(status_code=500, detail=f"Bulk operation failed: {str(e)}") from e
 655 |         finally:
 656 |             conn.close()
 657 | 
 658 | 
 659 | class WorkingMemoryManager:
 660 |     """Core working memory management and optimization logic."""
 661 |     
 662 |     def __init__(self, db_path: str = "storage/unified_agent_memory.db"):
 663 |         self.db_path = db_path
 664 |         self.active_memories: Dict[str, WorkingMemoryItem] = {}
 665 |         self.capacity = 7  # Miller's rule: 7±2
 666 |         self.focus_mode = "normal"
 667 |         self.retention_time = 30  # minutes
 668 |         self.connected_clients: List[WebSocket] = []
 669 |         
 670 |     def get_db_connection(self):
 671 |         """Get database connection."""
 672 |         conn = sqlite3.connect(self.db_path)
 673 |         conn.row_factory = sqlite3.Row
 674 |         return conn
 675 |     
 676 |     def calculate_memory_temperature(self, memory: Dict) -> float:
 677 |         """Calculate memory temperature based on access patterns."""
 678 |         now = time.time()
 679 |         last_access = memory.get('last_accessed_at', memory.get('created_at', now))
 680 |         access_count = memory.get('access_count', 0)
 681 |         
 682 |         # Recency component (decreases over time)
 683 |         time_since_access = now - last_access
 684 |         recency_score = max(0, 100 - (time_since_access / 3600) * 10)  # Decreases over hours
 685 |         
 686 |         # Frequency component
 687 |         frequency_score = min(100, access_count * 10)
 688 |         
 689 |         # Weighted combination
 690 |         temperature = recency_score * 0.7 + frequency_score * 0.3
 691 |         return round(temperature)
 692 |     
 693 |     def calculate_memory_priority(self, memory: Dict) -> str:
 694 |         """Calculate memory priority level."""
 695 |         importance = memory.get('importance', 1)
 696 |         if importance >= 9:
 697 |             return 'critical'
 698 |         elif importance >= 7:
 699 |             return 'high'
 700 |         elif importance >= 5:
 701 |             return 'medium'
 702 |         else:
 703 |             return 'low'
 704 |     
 705 |     def calculate_access_frequency(self, memory: Dict) -> float:
 706 |         """Calculate normalized access frequency."""
 707 |         access_count = memory.get('access_count', 0)
 708 |         return min(10, access_count / 5)  # Normalized to 0-10 scale
 709 |     
 710 |     def calculate_retention_score(self, memory: Dict) -> float:
 711 |         """Calculate how likely memory should remain in working memory."""
 712 |         importance = memory.get('importance', 1)
 713 |         confidence = memory.get('confidence', 0.5)
 714 |         access_count = memory.get('access_count', 0)
 715 |         
 716 |         score = (importance * 0.4 + confidence * 100 * 0.3 + min(access_count * 10, 100) * 0.3) / 10
 717 |         return round(score, 2)
 718 |     
 719 |     def enhance_memory_for_working_memory(self, memory: Dict) -> WorkingMemoryItem:
 720 |         """Convert database memory to enhanced working memory item."""
 721 |         return WorkingMemoryItem(
 722 |             memory_id=memory['memory_id'],
 723 |             content=memory['content'],
 724 |             memory_type=memory['memory_type'],
 725 |             memory_level=memory['memory_level'],
 726 |             importance=memory['importance'],
 727 |             confidence=memory.get('confidence', 0.5),
 728 |             created_at=memory['created_at'],
 729 |             last_accessed_at=memory.get('last_accessed_at'),
 730 |             access_count=memory.get('access_count', 0),
 731 |             workflow_id=memory.get('workflow_id'),
 732 |             temperature=self.calculate_memory_temperature(memory),
 733 |             priority=self.calculate_memory_priority(memory),
 734 |             access_frequency=self.calculate_access_frequency(memory),
 735 |             retention_score=self.calculate_retention_score(memory),
 736 |             added_at=time.time()
 737 |         )
 738 |     
 739 |     def calculate_focus_score(self) -> float:
 740 |         """Calculate current focus score based on working memory coherence."""
 741 |         if not self.active_memories:
 742 |             return 100.0
 743 |         
 744 |         memories = list(self.active_memories.values())
 745 |         
 746 |         # Calculate average importance
 747 |         avg_importance = sum(m.importance for m in memories) / len(memories)
 748 |         
 749 |         # Calculate diversity penalty
 750 |         type_variety = len(set(m.memory_type for m in memories))
 751 |         level_variety = len(set(m.memory_level for m in memories))
 752 |         
 753 |         # Lower variety = higher focus
 754 |         variety_penalty = (type_variety + level_variety) * 5
 755 |         importance_bonus = avg_importance * 10
 756 |         
 757 |         focus_score = max(0, min(100, importance_bonus - variety_penalty + 20))
 758 |         return round(focus_score, 1)
 759 |     
 760 |     def calculate_efficiency(self) -> float:
 761 |         """Calculate working memory efficiency."""
 762 |         if not self.active_memories:
 763 |             return 100.0
 764 |         
 765 |         memories = list(self.active_memories.values())
 766 |         
 767 |         # Average temperature (activity level)
 768 |         avg_temperature = sum(m.temperature for m in memories) / len(memories)
 769 |         
 770 |         # Utilization rate
 771 |         utilization = (len(memories) / self.capacity) * 100
 772 |         
 773 |         # Optimal utilization is around 70%
 774 |         optimal_utilization = 100 - abs(utilization - 70) if abs(utilization - 70) < 30 else 70
 775 |         
 776 |         efficiency = (avg_temperature * 0.6 + optimal_utilization * 0.4)
 777 |         return round(efficiency)
 778 |     
 779 |     def get_working_memory_stats(self) -> WorkingMemoryStats:
 780 |         """Get current working memory statistics."""
 781 |         memories = list(self.active_memories.values())
 782 |         
 783 |         return WorkingMemoryStats(
 784 |             active_count=len(memories),
 785 |             capacity=self.capacity,
 786 |             pressure=round((len(memories) / self.capacity) * 100),
 787 |             temperature=round(sum(m.temperature for m in memories) / len(memories)) if memories else 0,
 788 |             focus_score=self.calculate_focus_score(),
 789 |             efficiency=self.calculate_efficiency(),
 790 |             avg_retention_time=round(sum(m.retention_score for m in memories) / len(memories)) if memories else 0,
 791 |             total_accesses=sum(m.access_count for m in memories),
 792 |             last_updated=time.time()
 793 |         )
 794 |     
 795 |     def generate_optimization_suggestions(self) -> List[OptimizationSuggestion]:
 796 |         """Generate optimization suggestions based on current state."""
 797 |         suggestions = []
 798 |         stats = self.get_working_memory_stats()
 799 |         memories = list(self.active_memories.values())
 800 |         
 801 |         # High pressure suggestion
 802 |         if stats.pressure > 80:
 803 |             suggestions.append(OptimizationSuggestion(
 804 |                 id="reduce-pressure",
 805 |                 title="Reduce Memory Pressure",
 806 |                 description="Working memory is near capacity. Consider removing lower priority items.",
 807 |                 priority="high",
 808 |                 impact="High",
 809 |                 icon="alert-triangle",
 810 |                 action="Auto-Remove",
 811 |                 confidence=0.9,
 812 |                 estimated_improvement={"pressure": -20, "efficiency": 15}
 813 |             ))
 814 |         
 815 |         # Cold memories suggestion
 816 |         cold_memories = [m for m in memories if m.temperature < 30]
 817 |         if cold_memories:
 818 |             suggestions.append(OptimizationSuggestion(
 819 |                 id="remove-cold",
 820 |                 title="Remove Stale Memories",
 821 |                 description=f"{len(cold_memories)} memories haven't been accessed recently.",
 822 |                 priority="medium",
 823 |                 impact="Medium",
 824 |                 icon="snowflake",
 825 |                 action="Clear Stale",
 826 |                 confidence=0.8,
 827 |                 estimated_improvement={"temperature": 15, "efficiency": 10}
 828 |             ))
 829 |         
 830 |         # Low focus suggestion
 831 |         if stats.focus_score < 50:
 832 |             suggestions.append(OptimizationSuggestion(
 833 |                 id="improve-focus",
 834 |                 title="Improve Focus",
 835 |                 description="Working memory contains diverse, unrelated items. Consider focusing on a single task.",
 836 |                 priority="medium",
 837 |                 impact="High",
 838 |                 icon="target",
 839 |                 action="Focus Mode",
 840 |                 confidence=0.7,
 841 |                 estimated_improvement={"focus_score": 30, "efficiency": 20}
 842 |             ))
 843 |         
 844 |         # Underutilization suggestion
 845 |         if stats.active_count < self.capacity / 2:
 846 |             suggestions.append(OptimizationSuggestion(
 847 |                 id="add-related",
 848 |                 title="Add Related Memories",
 849 |                 description="Working memory has capacity for more relevant items.",
 850 |                 priority="low",
 851 |                 impact="Medium",
 852 |                 icon="plus-circle",
 853 |                 action="Add Related",
 854 |                 confidence=0.6,
 855 |                 estimated_improvement={"efficiency": 10, "focus_score": 5}
 856 |             ))
 857 |         
 858 |         return suggestions
 859 |     
 860 |     async def load_initial_working_memory(self) -> List[WorkingMemoryItem]:
 861 |         """Load initial working memory with high-importance memories."""
 862 |         conn = self.get_db_connection()
 863 |         try:
 864 |             cursor = conn.cursor()
 865 |             
 866 |             # Get high-importance or working-level memories
 867 |             cursor.execute("""
 868 |                 SELECT * FROM memories 
 869 |                 WHERE memory_level = 'working' OR importance >= 8
 870 |                 ORDER BY created_at DESC, importance DESC
 871 |                 LIMIT ?
 872 |             """, (self.capacity,))
 873 |             
 874 |             memories = []
 875 |             for row in cursor.fetchall():
 876 |                 memory_dict = dict(row)
 877 |                 enhanced_memory = self.enhance_memory_for_working_memory(memory_dict)
 878 |                 memories.append(enhanced_memory)
 879 |                 self.active_memories[enhanced_memory.memory_id] = enhanced_memory
 880 |             
 881 |             return memories
 882 |             
 883 |         finally:
 884 |             conn.close()
 885 |     
 886 |     async def add_to_working_memory(self, memory_id: str) -> bool:
 887 |         """Add a memory to working memory."""
 888 |         if len(self.active_memories) >= self.capacity:
 889 |             return False
 890 |         
 891 |         if memory_id in self.active_memories:
 892 |             return False
 893 |         
 894 |         conn = self.get_db_connection()
 895 |         try:
 896 |             cursor = conn.cursor()
 897 |             cursor.execute("SELECT * FROM memories WHERE memory_id = ?", (memory_id,))
 898 |             row = cursor.fetchone()
 899 |             
 900 |             if not row:
 901 |                 return False
 902 |             
 903 |             memory_dict = dict(row)
 904 |             enhanced_memory = self.enhance_memory_for_working_memory(memory_dict)
 905 |             self.active_memories[memory_id] = enhanced_memory
 906 |             
 907 |             # Broadcast update to connected clients
 908 |             await self.broadcast_update()
 909 |             
 910 |             return True
 911 |             
 912 |         finally:
 913 |             conn.close()
 914 |     
 915 |     async def remove_from_working_memory(self, memory_id: str) -> bool:
 916 |         """Remove a memory from working memory."""
 917 |         if memory_id not in self.active_memories:
 918 |             return False
 919 |         
 920 |         del self.active_memories[memory_id]
 921 |         
 922 |         # Broadcast update to connected clients
 923 |         await self.broadcast_update()
 924 |         
 925 |         return True
 926 |     
 927 |     async def clear_working_memory(self):
 928 |         """Clear all working memory."""
 929 |         self.active_memories.clear()
 930 |         await self.broadcast_update()
 931 |     
 932 |     async def apply_focus_mode(self, mode: str, retention_time: Optional[int] = None, max_memory: Optional[int] = None):
 933 |         """Apply focus mode settings."""
 934 |         mode_settings = {
 935 |             'deep': {'capacity': 5, 'retention': 60},
 936 |             'creative': {'capacity': 9, 'retention': 45},
 937 |             'analytical': {'capacity': 6, 'retention': 90},
 938 |             'maintenance': {'capacity': 3, 'retention': 20},
 939 |             'normal': {'capacity': 7, 'retention': 30}
 940 |         }
 941 |         
 942 |         settings = mode_settings.get(mode, mode_settings['normal'])
 943 |         
 944 |         self.focus_mode = mode
 945 |         self.capacity = max_memory or settings['capacity']
 946 |         self.retention_time = retention_time or settings['retention']
 947 |         
 948 |         # If we're over capacity, remove lowest priority memories
 949 |         if len(self.active_memories) > self.capacity:
 950 |             memories_by_priority = sorted(
 951 |                 self.active_memories.values(),
 952 |                 key=lambda m: (m.importance, m.retention_score),
 953 |                 reverse=True
 954 |             )
 955 |             
 956 |             # Keep only the top memories
 957 |             to_keep = memories_by_priority[:self.capacity]
 958 |             self.active_memories = {m.memory_id: m for m in to_keep}
 959 |         
 960 |         await self.broadcast_update()
 961 |     
 962 |     async def auto_optimize(self) -> List[str]:
 963 |         """Apply automatic optimizations."""
 964 |         applied_optimizations = []
 965 |         suggestions = self.generate_optimization_suggestions()
 966 |         
 967 |         for suggestion in suggestions:
 968 |             if suggestion.priority in ['medium', 'low'] and suggestion.confidence > 0.7:
 969 |                 success = await self.apply_optimization(suggestion.id)
 970 |                 if success:
 971 |                     applied_optimizations.append(suggestion.title)
 972 |         
 973 |         return applied_optimizations
 974 |     
 975 |     async def apply_optimization(self, suggestion_id: str) -> bool:
 976 |         """Apply a specific optimization."""
 977 |         memories = list(self.active_memories.values())
 978 |         
 979 |         if suggestion_id == "reduce-pressure":
 980 |             # Remove lowest priority memories
 981 |             low_priority = [m for m in memories if m.priority == 'low']
 982 |             for memory in low_priority[:2]:
 983 |                 await self.remove_from_working_memory(memory.memory_id)
 984 |             return True
 985 |             
 986 |         elif suggestion_id == "remove-cold":
 987 |             # Remove cold memories
 988 |             cold_memories = [m for m in memories if m.temperature < 30]
 989 |             for memory in cold_memories[:3]:
 990 |                 await self.remove_from_working_memory(memory.memory_id)
 991 |             return True
 992 |             
 993 |         elif suggestion_id == "improve-focus":
 994 |             # Switch to deep focus mode
 995 |             await self.apply_focus_mode('deep')
 996 |             return True
 997 |             
 998 |         elif suggestion_id == "add-related":
 999 |             # Add related memories
1000 |             await self.add_related_memories()
1001 |             return True
1002 |         
1003 |         return False
1004 |     
1005 |     async def add_related_memories(self):
1006 |         """Add memories related to current working memory."""
1007 |         if not self.active_memories or len(self.active_memories) >= self.capacity:
1008 |             return
1009 |         
1010 |         current_types = set(m.memory_type for m in self.active_memories.values())
1011 |         current_workflows = set(m.workflow_id for m in self.active_memories.values() if m.workflow_id)
1012 |         
1013 |         conn = self.get_db_connection()
1014 |         try:
1015 |             cursor = conn.cursor()
1016 |             
1017 |             # Find related memories
1018 |             placeholders = ','.join('?' * len(current_types)) if current_types else "''"
1019 |             workflow_placeholders = ','.join('?' * len(current_workflows)) if current_workflows else "''"
1020 |             
1021 |             query = f"""
1022 |                 SELECT * FROM memories 
1023 |                 WHERE memory_id NOT IN ({','.join('?' * len(self.active_memories))})
1024 |                 AND (memory_type IN ({placeholders}) OR workflow_id IN ({workflow_placeholders}))
1025 |                 AND importance >= 6
1026 |                 ORDER BY importance DESC
1027 |                 LIMIT ?
1028 |             """
1029 |             
1030 |             params = (
1031 |                 list(self.active_memories.keys()) + 
1032 |                 list(current_types) + 
1033 |                 list(current_workflows) + 
1034 |                 [self.capacity - len(self.active_memories)]
1035 |             )
1036 |             
1037 |             cursor.execute(query, params)
1038 |             
1039 |             for row in cursor.fetchall():
1040 |                 memory_dict = dict(row)
1041 |                 enhanced_memory = self.enhance_memory_for_working_memory(memory_dict)
1042 |                 self.active_memories[enhanced_memory.memory_id] = enhanced_memory
1043 |                 
1044 |                 if len(self.active_memories) >= self.capacity:
1045 |                     break
1046 |             
1047 |         finally:
1048 |             conn.close()
1049 |         
1050 |         await self.broadcast_update()
1051 |     
1052 |     def get_memory_pool(self, search: str = "", filter_type: str = "", limit: int = 50) -> List[Dict]:
1053 |         """Get available memory pool for working memory."""
1054 |         conn = self.get_db_connection()
1055 |         try:
1056 |             cursor = conn.cursor()
1057 |             
1058 |             # Build query
1059 |             where_conditions = ["memory_id NOT IN ({})".format(','.join('?' * len(self.active_memories)))]
1060 |             params = list(self.active_memories.keys())
1061 |             
1062 |             if search:
1063 |                 where_conditions.append("(content LIKE ? OR memory_type LIKE ?)")
1064 |                 params.extend([f"%{search}%", f"%{search}%"])
1065 |             
1066 |             if filter_type == "high":
1067 |                 where_conditions.append("importance >= 8")
1068 |             elif filter_type == "recent":
1069 |                 day_ago = time.time() - 86400
1070 |                 where_conditions.append("created_at > ?")
1071 |                 params.append(day_ago)
1072 |             elif filter_type == "related" and self.active_memories:
1073 |                 current_types = set(m.memory_type for m in self.active_memories.values())
1074 |                 current_workflows = set(m.workflow_id for m in self.active_memories.values() if m.workflow_id)
1075 |                 
1076 |                 if current_types or current_workflows:
1077 |                     type_placeholders = ','.join('?' * len(current_types)) if current_types else "''"
1078 |                     workflow_placeholders = ','.join('?' * len(current_workflows)) if current_workflows else "''"
1079 |                     where_conditions.append(f"(memory_type IN ({type_placeholders}) OR workflow_id IN ({workflow_placeholders}))")
1080 |                     params.extend(list(current_types) + list(current_workflows))
1081 |             
1082 |             query = f"""
1083 |                 SELECT * FROM memories 
1084 |                 WHERE {' AND '.join(where_conditions)}
1085 |                 ORDER BY importance DESC
1086 |                 LIMIT ?
1087 |             """
1088 |             params.append(limit)
1089 |             
1090 |             cursor.execute(query, params)
1091 |             
1092 |             memories = []
1093 |             for row in cursor.fetchall():
1094 |                 memory_dict = dict(row)
1095 |                 memory_dict['access_frequency'] = self.calculate_access_frequency(memory_dict)
1096 |                 memories.append(memory_dict)
1097 |             
1098 |             return memories
1099 |             
1100 |         finally:
1101 |             conn.close()
1102 |     
1103 |     def generate_heatmap_data(self, timeframe: str = "24h") -> List[Dict]:
1104 |         """Generate memory activity heatmap data."""
1105 |         now = time.time()
1106 |         intervals = []
1107 |         
1108 |         # Configure timeframe
1109 |         timeframe_config = {
1110 |             '1h': {'seconds': 300, 'count': 12},      # 5 minute intervals
1111 |             '6h': {'seconds': 1800, 'count': 12},     # 30 minute intervals
1112 |             '24h': {'seconds': 3600, 'count': 24},    # 1 hour intervals
1113 |             '7d': {'seconds': 86400, 'count': 7}      # 1 day intervals
1114 |         }
1115 |         
1116 |         config = timeframe_config.get(timeframe, timeframe_config['24h'])
1117 |         interval_seconds = config['seconds']
1118 |         interval_count = config['count']
1119 |         
1120 |         conn = self.get_db_connection()
1121 |         try:
1122 |             cursor = conn.cursor()
1123 |             
1124 |             for i in range(interval_count):
1125 |                 interval_start = now - (interval_count - i) * interval_seconds
1126 |                 interval_end = interval_start + interval_seconds
1127 |                 
1128 |                 # Count activities in this interval
1129 |                 cursor.execute("""
1130 |                     SELECT COUNT(*) as activity_count 
1131 |                     FROM memories 
1132 |                     WHERE created_at >= ? AND created_at <= ?
1133 |                 """, (interval_start, interval_end))
1134 |                 
1135 |                 activity_count = cursor.fetchone()[0]
1136 |                 
1137 |                 intervals.append({
1138 |                     'time': interval_start,
1139 |                     'activity': activity_count,
1140 |                     'intensity': min(1.0, activity_count / 10)  # Normalize to 0-1
1141 |                 })
1142 |             
1143 |             return intervals
1144 |             
1145 |         finally:
1146 |             conn.close()
1147 |     
1148 |     async def register_client(self, websocket: WebSocket):
1149 |         """Register a WebSocket client for real-time updates."""
1150 |         self.connected_clients.append(websocket)
1151 |     
1152 |     async def unregister_client(self, websocket: WebSocket):
1153 |         """Unregister a WebSocket client."""
1154 |         if websocket in self.connected_clients:
1155 |             self.connected_clients.remove(websocket)
1156 |     
1157 |     async def broadcast_update(self):
1158 |         """Broadcast working memory update to all connected clients."""
1159 |         if not self.connected_clients:
1160 |             return
1161 |         
1162 |         update_data = {
1163 |             'type': 'working_memory_update',
1164 |             'stats': asdict(self.get_working_memory_stats()),
1165 |             'active_memories': [asdict(m) for m in self.active_memories.values()],
1166 |             'suggestions': [asdict(s) for s in self.generate_optimization_suggestions()],
1167 |             'timestamp': time.time()
1168 |         }
1169 |         
1170 |         # Send to all connected clients
1171 |         disconnected_clients = []
1172 |         for client in self.connected_clients:
1173 |             try:
1174 |                 await client.send_text(json.dumps(update_data))
1175 |             except Exception:
1176 |                 disconnected_clients.append(client)
1177 |         
1178 |         # Remove disconnected clients
1179 |         for client in disconnected_clients:
1180 |             await self.unregister_client(client)
1181 | 
1182 | 
1183 | # Global working memory manager instance
1184 | working_memory_manager = WorkingMemoryManager()
1185 | 
1186 | # Global memory quality inspector instance
1187 | memory_quality_inspector = MemoryQualityInspector()
1188 | 
1189 | 
1190 | def setup_working_memory_routes(app: FastAPI):
1191 |     """Setup working memory API routes."""
1192 |     
1193 |     @app.get("/api/working-memory/status")
1194 |     async def get_working_memory_status():
1195 |         """Get current working memory status and statistics."""
1196 |         try:
1197 |             stats = working_memory_manager.get_working_memory_stats()
1198 |             active_memories = [asdict(m) for m in working_memory_manager.active_memories.values()]
1199 |             suggestions = [asdict(s) for s in working_memory_manager.generate_optimization_suggestions()]
1200 |             
1201 |             return {
1202 |                 'status': 'connected',
1203 |                 'stats': asdict(stats),
1204 |                 'active_memories': active_memories,
1205 |                 'suggestions': suggestions,
1206 |                 'focus_mode': working_memory_manager.focus_mode,
1207 |                 'capacity': working_memory_manager.capacity,
1208 |                 'retention_time': working_memory_manager.retention_time
1209 |             }
1210 |         except Exception as e:
1211 |             raise HTTPException(status_code=500, detail=str(e)) from e
1212 |     
1213 |     @app.post("/api/working-memory/initialize")
1214 |     async def initialize_working_memory():
1215 |         """Initialize working memory with default high-importance memories."""
1216 |         try:
1217 |             memories = await working_memory_manager.load_initial_working_memory()
1218 |             stats = working_memory_manager.get_working_memory_stats()
1219 |             
1220 |             return {
1221 |                 'success': True,
1222 |                 'message': f'Initialized with {len(memories)} memories',
1223 |                 'stats': asdict(stats),
1224 |                 'active_memories': [asdict(m) for m in memories]
1225 |             }
1226 |         except Exception as e:
1227 |             raise HTTPException(status_code=500, detail=str(e)) from e
1228 |     
1229 |     @app.post("/api/working-memory/add")
1230 |     async def add_memory_to_working_memory(request: WorkingMemoryRequest):
1231 |         """Add a memory to working memory."""
1232 |         try:
1233 |             success = await working_memory_manager.add_to_working_memory(request.memory_id)
1234 |             
1235 |             if success:
1236 |                 stats = working_memory_manager.get_working_memory_stats()
1237 |                 return {
1238 |                     'success': True,
1239 |                     'message': 'Memory added to working memory',
1240 |                     'stats': asdict(stats)
1241 |                 }
1242 |             else:
1243 |                 return {
1244 |                     'success': False,
1245 |                     'message': 'Could not add memory (capacity reached or already exists)'
1246 |                 }
1247 |         except Exception as e:
1248 |             raise HTTPException(status_code=500, detail=str(e)) from e
1249 |     
1250 |     @app.post("/api/working-memory/remove")
1251 |     async def remove_memory_from_working_memory(request: WorkingMemoryRequest):
1252 |         """Remove a memory from working memory."""
1253 |         try:
1254 |             success = await working_memory_manager.remove_from_working_memory(request.memory_id)
1255 |             
1256 |             if success:
1257 |                 stats = working_memory_manager.get_working_memory_stats()
1258 |                 return {
1259 |                     'success': True,
1260 |                     'message': 'Memory removed from working memory',
1261 |                     'stats': asdict(stats)
1262 |                 }
1263 |             else:
1264 |                 return {
1265 |                     'success': False,
1266 |                     'message': 'Memory not found in working memory'
1267 |                 }
1268 |         except Exception as e:
1269 |             raise HTTPException(status_code=500, detail=str(e)) from e
1270 |     
1271 |     @app.post("/api/working-memory/clear")
1272 |     async def clear_working_memory():
1273 |         """Clear all working memory."""
1274 |         try:
1275 |             await working_memory_manager.clear_working_memory()
1276 |             stats = working_memory_manager.get_working_memory_stats()
1277 |             
1278 |             return {
1279 |                 'success': True,
1280 |                 'message': 'Working memory cleared',
1281 |                 'stats': asdict(stats)
1282 |             }
1283 |         except Exception as e:
1284 |             raise HTTPException(status_code=500, detail=str(e)) from e
1285 |     
1286 |     @app.post("/api/working-memory/focus-mode")
1287 |     async def set_focus_mode(request: FocusModeRequest):
1288 |         """Set focus mode and apply related optimizations."""
1289 |         try:
1290 |             await working_memory_manager.apply_focus_mode(
1291 |                 request.mode,
1292 |                 request.retention_time,
1293 |                 request.max_working_memory
1294 |             )
1295 |             
1296 |             stats = working_memory_manager.get_working_memory_stats()
1297 |             
1298 |             return {
1299 |                 'success': True,
1300 |                 'message': f'Applied {request.mode} focus mode',
1301 |                 'focus_mode': working_memory_manager.focus_mode,
1302 |                 'capacity': working_memory_manager.capacity,
1303 |                 'retention_time': working_memory_manager.retention_time,
1304 |                 'stats': asdict(stats)
1305 |             }
1306 |         except Exception as e:
1307 |             raise HTTPException(status_code=500, detail=str(e)) from e
1308 |     
1309 |     @app.post("/api/working-memory/optimize")
1310 |     async def optimize_working_memory():
1311 |         """Apply automatic working memory optimizations."""
1312 |         try:
1313 |             applied = await working_memory_manager.auto_optimize()
1314 |             stats = working_memory_manager.get_working_memory_stats()
1315 |             
1316 |             return {
1317 |                 'success': True,
1318 |                 'message': f'Applied {len(applied)} optimizations',
1319 |                 'optimizations_applied': applied,
1320 |                 'stats': asdict(stats)
1321 |             }
1322 |         except Exception as e:
1323 |             raise HTTPException(status_code=500, detail=str(e)) from e
1324 |     
1325 |     @app.post("/api/working-memory/apply-suggestion")
1326 |     async def apply_optimization_suggestion(request: OptimizationRequest):
1327 |         """Apply a specific optimization suggestion."""
1328 |         try:
1329 |             success = await working_memory_manager.apply_optimization(request.suggestion_id)
1330 |             
1331 |             if success:
1332 |                 stats = working_memory_manager.get_working_memory_stats()
1333 |                 suggestions = [asdict(s) for s in working_memory_manager.generate_optimization_suggestions()]
1334 |                 
1335 |                 return {
1336 |                     'success': True,
1337 |                     'message': 'Optimization applied successfully',
1338 |                     'stats': asdict(stats),
1339 |                     'suggestions': suggestions
1340 |                 }
1341 |             else:
1342 |                 return {
1343 |                     'success': False,
1344 |                     'message': 'Could not apply optimization'
1345 |                 }
1346 |         except Exception as e:
1347 |             raise HTTPException(status_code=500, detail=str(e)) from e
1348 |     
1349 |     @app.get("/api/working-memory/pool")
1350 |     async def get_memory_pool(
1351 |         search: str = "",
1352 |         filter_type: str = "",  # "", "high", "recent", "related"
1353 |         limit: int = 50
1354 |     ):
1355 |         """Get available memory pool for working memory."""
1356 |         try:
1357 |             memories = working_memory_manager.get_memory_pool(search, filter_type, limit)
1358 |             
1359 |             return {
1360 |                 'success': True,
1361 |                 'memories': memories,
1362 |                 'count': len(memories)
1363 |             }
1364 |         except Exception as e:
1365 |             raise HTTPException(status_code=500, detail=str(e)) from e
1366 |     
1367 |     @app.get("/api/working-memory/heatmap")
1368 |     async def get_memory_heatmap(timeframe: str = "24h"):
1369 |         """Get memory activity heatmap data."""
1370 |         try:
1371 |             heatmap_data = working_memory_manager.generate_heatmap_data(timeframe)
1372 |             
1373 |             return {
1374 |                 'success': True,
1375 |                 'timeframe': timeframe,
1376 |                 'data': heatmap_data
1377 |             }
1378 |         except Exception as e:
1379 |             raise HTTPException(status_code=500, detail=str(e)) from e
1380 |     
1381 |     @app.websocket("/ws/working-memory")
1382 |     async def working_memory_websocket(websocket: WebSocket):
1383 |         """WebSocket endpoint for real-time working memory updates."""
1384 |         await websocket.accept()
1385 |         await working_memory_manager.register_client(websocket)
1386 |         
1387 |         try:
1388 |             # Send initial data
1389 |             initial_data = {
1390 |                 'type': 'initial_data',
1391 |                 'stats': asdict(working_memory_manager.get_working_memory_stats()),
1392 |                 'active_memories': [asdict(m) for m in working_memory_manager.active_memories.values()],
1393 |                 'suggestions': [asdict(s) for s in working_memory_manager.generate_optimization_suggestions()],
1394 |                 'focus_mode': working_memory_manager.focus_mode,
1395 |                 'capacity': working_memory_manager.capacity
1396 |             }
1397 |             await websocket.send_text(json.dumps(initial_data))
1398 |             
1399 |             # Keep connection alive and handle messages
1400 |             while True:
1401 |                 try:
1402 |                     # Wait for messages from client
1403 |                     data = await websocket.receive_text()
1404 |                     message = json.loads(data)
1405 |                     
1406 |                     # Handle different message types
1407 |                     if message.get('type') == 'ping':
1408 |                         await websocket.send_text(json.dumps({'type': 'pong'}))
1409 |                     
1410 |                 except WebSocketDisconnect:
1411 |                     break
1412 |                 except Exception as e:
1413 |                     print(f"WebSocket error: {e}")
1414 |                     break
1415 |                     
1416 |         finally:
1417 |             await working_memory_manager.unregister_client(websocket)
1418 | 
1419 |     # Memory Quality Inspector API Endpoints
1420 |     @app.post("/api/memory-quality/analyze")
1421 |     async def analyze_memory_quality(request: QualityAnalysisRequest):
1422 |         """Perform comprehensive memory quality analysis."""
1423 |         try:
1424 |             result = await memory_quality_inspector.perform_quality_analysis(request)
1425 |             
1426 |             return {
1427 |                 'success': True,
1428 |                 'analysis': asdict(result)
1429 |             }
1430 |         except Exception as e:
1431 |             raise HTTPException(status_code=500, detail=f"Quality analysis failed: {str(e)}") from e
1432 |     
1433 |     @app.get("/api/memory-quality/quick-scan")
1434 |     async def quick_quality_scan():
1435 |         """Perform quick quality scan with basic metrics."""
1436 |         try:
1437 |             request = QualityAnalysisRequest(
1438 |                 analysis_type="comprehensive",
1439 |                 include_stale=False,
1440 |                 include_low_importance=False,
1441 |                 similarity_threshold=0.90,
1442 |                 stale_threshold_days=7
1443 |             )
1444 |             result = await memory_quality_inspector.perform_quality_analysis(request)
1445 |             
1446 |             # Return simplified metrics for quick overview
1447 |             return {
1448 |                 'success': True,
1449 |                 'quick_metrics': {
1450 |                     'total_memories': result.total_memories,
1451 |                     'overall_score': result.overall_score,
1452 |                     'critical_issues': len([i for i in result.issues if i.severity == 'critical']),
1453 |                     'duplicates': result.duplicates,
1454 |                     'orphaned': result.orphaned,
1455 |                     'low_quality': result.low_quality,
1456 |                     'top_recommendations': result.recommendations[:3]
1457 |                 }
1458 |             }
1459 |         except Exception as e:
1460 |             raise HTTPException(status_code=500, detail=f"Quick scan failed: {str(e)}") from e
1461 |     
1462 |     @app.post("/api/memory-quality/bulk-preview")
1463 |     async def preview_bulk_operation(request: BulkOperationRequest):
1464 |         """Preview bulk operation changes before execution."""
1465 |         try:
1466 |             operation = await memory_quality_inspector.preview_bulk_operation(request)
1467 |             
1468 |             return {
1469 |                 'success': True,
1470 |                 'operation': asdict(operation)
1471 |             }
1472 |         except Exception as e:
1473 |             raise HTTPException(status_code=500, detail=f"Bulk preview failed: {str(e)}") from e
1474 |     
1475 |     @app.post("/api/memory-quality/bulk-execute")
1476 |     async def execute_bulk_operation(operation_request: BulkOperationRequest):
1477 |         """Execute bulk operation with safety checks."""
1478 |         try:
1479 |             # First preview the operation
1480 |             operation = await memory_quality_inspector.preview_bulk_operation(operation_request)
1481 |             
1482 |             # Execute the operation
1483 |             results = await memory_quality_inspector.execute_bulk_operation(operation)
1484 |             
1485 |             return {
1486 |                 'success': True,
1487 |                 'operation_id': operation.operation_id,
1488 |                 'results': results,
1489 |                 'message': f"Bulk operation completed: {results['success']} successful, {results['failed']} failed"
1490 |             }
1491 |         except Exception as e:
1492 |             raise HTTPException(status_code=500, detail=f"Bulk operation failed: {str(e)}") from e
1493 |     
1494 |     @app.get("/api/memory-quality/duplicates")
1495 |     async def get_duplicates():
1496 |         """Get all duplicate memory clusters."""
1497 |         try:
1498 |             conn = memory_quality_inspector.get_db_connection()
1499 |             try:
1500 |                 cursor = conn.cursor()
1501 |                 cursor.execute("SELECT * FROM memories ORDER BY created_at DESC")
1502 |                 memories = [dict(row) for row in cursor.fetchall()]
1503 |             finally:
1504 |                 conn.close()
1505 |             
1506 |             clusters = memory_quality_inspector.detect_duplicates(memories, threshold=0.85)
1507 |             
1508 |             return {
1509 |                 'success': True,
1510 |                 'clusters': [asdict(cluster) for cluster in clusters],
1511 |                 'total_clusters': len(clusters),
1512 |                 'total_duplicates': sum(cluster.duplicate_count for cluster in clusters)
1513 |             }
1514 |         except Exception as e:
1515 |             raise HTTPException(status_code=500, detail=f"Duplicate detection failed: {str(e)}") from e
1516 |     
1517 |     @app.get("/api/memory-quality/orphaned")
1518 |     async def get_orphaned_memories():
1519 |         """Get all orphaned memories."""
1520 |         try:
1521 |             conn = memory_quality_inspector.get_db_connection()
1522 |             try:
1523 |                 cursor = conn.cursor()
1524 |                 cursor.execute("SELECT * FROM memories ORDER BY created_at DESC")
1525 |                 memories = [dict(row) for row in cursor.fetchall()]
1526 |             finally:
1527 |                 conn.close()
1528 |             
1529 |             orphaned = memory_quality_inspector.detect_orphaned_memories(memories)
1530 |             
1531 |             return {
1532 |                 'success': True,
1533 |                 'orphaned_memories': orphaned,
1534 |                 'total_orphaned': len(orphaned),
1535 |                 'completely_isolated': len([m for m in orphaned if m['isolation_level'] == 'complete']),
1536 |                 'partially_isolated': len([m for m in orphaned if m['isolation_level'] == 'partial'])
1537 |             }
1538 |         except Exception as e:
1539 |             raise HTTPException(status_code=500, detail=f"Orphaned memory detection failed: {str(e)}") from e
1540 |     
1541 |     @app.get("/api/memory-quality/stats")
1542 |     async def get_quality_stats():
1543 |         """Get overall memory quality statistics."""
1544 |         try:
1545 |             conn = memory_quality_inspector.get_db_connection()
1546 |             try:
1547 |                 cursor = conn.cursor()
1548 |                 
1549 |                 # Basic stats
1550 |                 cursor.execute("SELECT COUNT(*) as total FROM memories")
1551 |                 total_memories = cursor.fetchone()['total']
1552 |                 
1553 |                 cursor.execute("SELECT AVG(importance) as avg_importance, AVG(confidence) as avg_confidence FROM memories")
1554 |                 quality_metrics = cursor.fetchone()
1555 |                 
1556 |                 cursor.execute("SELECT COUNT(*) as with_workflow FROM memories WHERE workflow_id IS NOT NULL")
1557 |                 with_workflow = cursor.fetchone()['with_workflow']
1558 |                 
1559 |                 cursor.execute("SELECT COUNT(*) as recent FROM memories WHERE created_at > ?", (time.time() - 86400 * 7,))
1560 |                 recent_memories = cursor.fetchone()['recent']
1561 |                 
1562 |                 # Quality distribution
1563 |                 cursor.execute("""
1564 |                     SELECT 
1565 |                         SUM(CASE WHEN importance >= 8 THEN 1 ELSE 0 END) as high_importance,
1566 |                         SUM(CASE WHEN importance >= 5 THEN 1 ELSE 0 END) as medium_importance,
1567 |                         SUM(CASE WHEN confidence >= 0.8 THEN 1 ELSE 0 END) as high_confidence,
1568 |                         SUM(CASE WHEN confidence >= 0.5 THEN 1 ELSE 0 END) as medium_confidence
1569 |                     FROM memories
1570 |                 """)
1571 |                 quality_dist = cursor.fetchone()
1572 |                 
1573 |             finally:
1574 |                 conn.close()
1575 |             
1576 |             return {
1577 |                 'success': True,
1578 |                 'stats': {
1579 |                     'total_memories': total_memories,
1580 |                     'avg_importance': round(quality_metrics['avg_importance'], 2),
1581 |                     'avg_confidence': round(quality_metrics['avg_confidence'], 2),
1582 |                     'workflow_coverage': round(with_workflow / max(1, total_memories) * 100, 1),
1583 |                     'recent_activity': recent_memories,
1584 |                     'quality_distribution': {
1585 |                         'high_importance': quality_dist['high_importance'],
1586 |                         'medium_importance': quality_dist['medium_importance'],
1587 |                         'high_confidence': quality_dist['high_confidence'],
1588 |                         'medium_confidence': quality_dist['medium_confidence']
1589 |                     }
1590 |                 }
1591 |             }
1592 |         except Exception as e:
1593 |             raise HTTPException(status_code=500, detail=f"Stats collection failed: {str(e)}") from e
1594 | 
1595 | 
1596 | # Background task to periodically update working memory
1597 | async def working_memory_background_task():
1598 |     """Background task for periodic working memory updates."""
1599 |     while True:
1600 |         try:
1601 |             # Update temperatures and stats periodically
1602 |             for memory in working_memory_manager.active_memories.values():
1603 |                 # Recalculate temperature based on current time
1604 |                 memory.temperature = working_memory_manager.calculate_memory_temperature(asdict(memory))
1605 |             
1606 |             # Broadcast updates if there are connected clients
1607 |             if working_memory_manager.connected_clients:
1608 |                 await working_memory_manager.broadcast_update()
1609 |             
1610 |             # Wait 30 seconds before next update
1611 |             await asyncio.sleep(30)
1612 |             
1613 |         except Exception as e:
1614 |             print(f"Background task error: {e}")
1615 |             await asyncio.sleep(60)  # Wait longer if there's an error
1616 | 
1617 | 
1618 | def start_background_tasks(app: FastAPI):
1619 |     """Start background tasks for working memory management."""
1620 |     
1621 |     @app.on_event("startup")
1622 |     async def startup_event():
1623 |         # Start background task
1624 |         asyncio.create_task(working_memory_background_task())
1625 |         
1626 |         # Initialize working memory with default data
1627 |         try:
1628 |             await working_memory_manager.load_initial_working_memory()
1629 |             print("✅ Working memory initialized successfully")
1630 |         except Exception as e:
1631 |             print(f"⚠️ Could not initialize working memory: {e}") 
```
Page 22/45FirstPrevNextLast