From fd886bf23655a1d6b16de63a0d17b715ec086173 Mon Sep 17 00:00:00 2001 From: Sopaco Date: Tue, 30 Dec 2025 20:43:06 +0800 Subject: [PATCH 1/3] docs update --- litho.docs/1.Overview.md | 308 +- litho.docs/2.Architecture.md | 907 +- litho.docs/3.Workflow.md | 763 +- .../Access Interface Domain.md | 422 + .../Configuration Management Domain.md | 444 + .../LLM Integration Domain.md | 335 + .../Memory Management Domain.md | 1004 +- .../Memory Optimization Domain.md | 280 + .../Storage Integration Domain.md | 504 + litho.docs/5.Boundary-Interfaces.md | 411 +- litho.docs/__Litho_Summary_Brief__.md | 45 +- litho.docs/__Litho_Summary_Detail__.md | 18168 +++++++--------- 12 files changed, 11421 insertions(+), 12170 deletions(-) create mode 100644 litho.docs/4.Deep-Exploration/Access Interface Domain.md create mode 100644 litho.docs/4.Deep-Exploration/Configuration Management Domain.md create mode 100644 litho.docs/4.Deep-Exploration/LLM Integration Domain.md create mode 100644 litho.docs/4.Deep-Exploration/Memory Optimization Domain.md create mode 100644 litho.docs/4.Deep-Exploration/Storage Integration Domain.md diff --git a/litho.docs/1.Overview.md b/litho.docs/1.Overview.md index dd84451..f5f5bf7 100644 --- a/litho.docs/1.Overview.md +++ b/litho.docs/1.Overview.md @@ -1,9 +1,7 @@ # System Context Overview -**Document Generated on:** 2025-12-18 03:27:26 (UTC) (UTC) -**Timestamp:** 1766028446 -**System:** `cortex-mem` – AI Agent Memory Management System -**Architecture Level:** C4 Model – System Context (Level 1) +**Generated on:** 2025-12-30 11:17:28 (UTC) +**Timestamp:** 1767093448 --- @@ -13,181 +11,166 @@ **cortex-mem** ### Project Description -`cortex-mem` is a full-stack memory management system designed specifically for AI agents. It enables persistent, semantically searchable, and optimizable storage of experiential knowledge (referred to as "memories") that AI agents accumulate during interactions. The system supports long-term context retention, enhancing agent consistency, reasoning, and intelligence across sessions. +*cortex-mem* is a full-stack memory management system designed to provide AI agents with persistent, searchable, and optimizable memory storage. It enables intelligent agents to retain context across interactions by storing conversation history, procedural knowledge, and factual information in a structured and semantically accessible format. -At its core, `cortex-mem` leverages vector embeddings and large language models (LLMs) to store, retrieve, and refine memories based on semantic meaning rather than exact keyword matches. It provides advanced capabilities such as deduplication, relevance scoring, memory optimization, and analytics through a modular architecture. +The system supports multiple access interfaces—including CLI, HTTP API, MCP (Memory Control Protocol), and a web-based dashboard—allowing seamless integration into diverse AI agent architectures and operational workflows. By leveraging vector embeddings and Large Language Models (LLMs), *cortex-mem* delivers advanced semantic search, intelligent memory classification, and automated optimization capabilities. ### Core Functionality -- **Memory CRUD Operations**: Create, read, update, and delete memories with rich metadata. -- **Semantic Search**: Retrieve relevant memories using natural language queries via vector similarity search. -- **Memory Optimization**: Automatically detect and resolve issues like redundancy, low quality, or irrelevance using LLM-driven analysis. -- **Analytics & Insights**: Visualize memory usage patterns, performance metrics, and optimization outcomes via a dashboard. -- **Multi-Interface Access**: Support for REST API, CLI, and MCP protocol for integration flexibility. -- **Evaluation Framework**: Built-in tools for benchmarking recall accuracy, search effectiveness, and system performance. +- **Persistent Memory Storage**: Long-term retention of agent memories with metadata and semantic context. +- **Semantic Search**: Retrieve relevant memories using natural language queries via vector similarity. +- **Memory Optimization**: Automatically detect and resolve issues such as duplication, irrelevance, or redundancy. +- **Multi-Interface Access**: Support for developers, agents, and operators through CLI, REST API, MCP, and web UI. +- **Configurable & Extensible**: Centralized configuration enables consistent behavior across deployment environments. ### Business Value -`cortex-mem` significantly enhances the cognitive capabilities of AI agents by providing reliable, scalable, and intelligent memory infrastructure. This enables: -- Improved continuity and personalization in agent-user interactions. -- Higher fidelity in decision-making through access to historical context. -- Operational visibility into agent memory health and evolution. -- Accelerated development and evaluation of memory-augmented AI systems. +*cortex-mem* enhances the intelligence and continuity of AI agents by enabling them to: +- Maintain contextual awareness across extended conversations and tasks. +- Recall relevant past experiences efficiently. +- Operate more autonomously through self-maintained memory quality. + +This results in improved user experience, higher task success rates, and reduced cognitive load on both agents and their human operators. ### Technical Characteristics -- **Modular & Layered Architecture**: Clear separation between core logic, interfaces, and external dependencies. -- **Vector-First Design**: Memories are stored and retrieved using high-dimensional embeddings in a dedicated vector database. -- **LLM-Integrated Processing**: External LLM services are used for embedding generation and semantic understanding. -- **Configurable & Extensible**: Supports TOML-based configuration and pluggable components for storage and LLM providers. -- **Evaluation-Driven Development**: Includes tools for testing, benchmarking, and validating memory system behavior. +- **Architecture Style**: Modular microservices with domain-driven design principles. +- **Primary Languages**: Rust (backend/core logic), TypeScript (frontend/dashboard). +- **Integration Model**: API-first, service-oriented with external dependencies on LLMs and vector databases. +- **Deployment Target**: Cloud-native, containerizable services with minimal infrastructure coupling. --- ## 2. Target Users -| User Role | Description | Key Needs | Usage Scenarios | -|---------|-------------|-----------|----------------| -| **AI Agent Developers** | Software engineers building AI-powered applications requiring persistent memory. | | | -| **System Administrators** | Operations personnel managing AI infrastructure and monitoring system health. | | | -| **Research Scientists** | Academics and researchers studying AI memory systems and agent cognition. | | | +The *cortex-mem* system serves three primary user roles, each with distinct needs and interaction patterns: + +| User Role | Description | Key Needs | +|---------|-------------|-----------| +| **AI Agents** | Intelligent software agents requiring persistent memory to maintain context across interactions. | | +| **Developers** | Software engineers integrating *cortex-mem* into AI applications or agent frameworks. | | +| **System Administrators** | Operators responsible for monitoring, maintaining, and tuning the memory infrastructure. | | -These user roles interact with the system through different entry points: -- **Developers** use the REST API and CLI. -- **Administrators** use the CLI and Insights Dashboard. -- **Researchers** use the evaluation framework and API for data collection. +### Usage Scenarios +- An AI customer support agent recalls previous interactions with a user to provide personalized responses. +- A developer integrates *cortex-mem* into an autonomous agent framework using the HTTP API for memory persistence. +- An operator runs a nightly optimization job via CLI to deduplicate and clean memory entries. +- A research team evaluates *cortex-mem* against alternative memory systems like *LangMem* for benchmarking purposes. --- ## 3. System Boundaries ### System Scope -The `cortex-mem` system provides a complete, end-to-end solution for managing AI agent memories. It encompasses all aspects of memory lifecycle management, from ingestion and storage to search, optimization, and analysis. +*cortex-mem* provides a dedicated memory management layer for AI agents. It handles the full lifecycle of memory data—including creation, retrieval, search, update, deletion, and optimization—through multiple standardized interfaces. -> **Scope Statement**: -> *The cortex-mem system provides a complete memory management solution for AI agents, including storage, retrieval, optimization, and analysis capabilities.* +The system acts as a **middleware service**, decoupling memory logic from core agent intelligence, allowing it to be reused across different agent implementations. ### Included Components -The following core components are within the system boundary: +The following capabilities are within the scope of *cortex-mem*: | Component | Description | |--------|-------------| -| **Memory CRUD Operations** | Full support for creating, reading, updating, and deleting memories with metadata tagging. | -| **Semantic Search Engine** | Enables natural language search over stored memories using vector embeddings. | -| **Memory Optimization Engine** | Detects duplicates, improves memory quality, and removes irrelevant entries using LLMs. | -| **Insights & Analytics Dashboard** | Web-based UI for visualizing memory usage, trends, and optimization reports. | -| **REST API Service** (`cortex-mem-service`) | HTTP interface for programmatic access to memory operations and system controls. | -| **Command-Line Interface** (`cortex-mem-cli`) | Tool for local interaction, scripting, and administrative tasks. | -| **MCP Protocol Adapter** (`cortex-mem-mcp`) | Enables integration with agent frameworks using the Memory Consistency Protocol. | -| **Evaluation Framework** | Tools for testing recall, precision, latency, and optimization effectiveness. | -| **Core Memory Engine** (`cortex-mem-core`) | Central module handling memory logic, vector integration, and LLM coordination. | -| **Configuration Management** | Centralized configuration via TOML files supporting environment-specific settings. | +| **Memory Storage & Retrieval** | CRUD operations for memory entries with rich metadata. | +| **Semantic Search** | Vector-based similarity search using embeddings generated by LLMs. | +| **Memory Optimization Engine** | Automated analysis and improvement of memory quality (e.g., deduplication, relevance filtering). | +| **Multiple Access Interfaces** | | +| **Web-Based Monitoring Dashboard** | Real-time insights into memory usage, performance metrics, and optimization results. | +| **Configuration Management** | Centralized configuration system for all components (e.g., LLM endpoints, Qdrant settings). | ### Excluded Components -The following are explicitly outside the scope of `cortex-mem`: +The following are explicitly **outside** the scope of *cortex-mem*: | Component | Reason for Exclusion | |--------|------------------------| -| **Core LLM Model Training** | The system consumes LLM APIs but does not train or fine-tune foundation models. | -| **Vector Embedding Model Development** | Relies on external LLM services for embeddings; does not develop embedding models. | -| **Operating System Resource Management** | Does not manage CPU, memory, or disk at the OS level. | -| **Network Infrastructure Provisioning** | Assumes network connectivity is provided; does not handle networking setup or scaling. | +| **Core AI Agent Logic** | *cortex-mem* does not implement agent reasoning, planning, or decision-making. It only provides memory services. | +| **Application-Specific Business Rules** | Domain-specific logic (e.g., "remember user preferences only after consent") is handled by the agent, not *cortex-mem*. | +| **User Interface Design for End Applications** | While a monitoring dashboard is provided, end-user UIs (e.g., chat interfaces) are outside scope. | +| **Network Infrastructure Management** | Deployment, scaling, networking, and security of underlying infrastructure are managed externally. | -This boundary ensures focus on memory-specific functionality while leveraging best-of-breed external services for AI processing and infrastructure. +> ✅ **Boundary Principle**: *cortex-mem* focuses on **what** is remembered and **how** it is accessed, not **why** or **when** it should be used. --- ## 4. External System Interactions -The `cortex-mem` system interacts with several external systems to deliver its functionality. These interactions are essential for storage, AI processing, and user access. +*cortex-mem* integrates with several external systems to deliver its functionality. These interactions are essential for core operations such as embedding generation, vector storage, and benchmarking. + +### External Systems -### External Systems List +| System Name | Interaction Type | Description | Direction | +|------------|------------------|-------------|----------| +| **Qdrant** | Database Storage | Vector database used to store and retrieve memory embeddings. Enables high-performance semantic search through approximate nearest neighbor (ANN) indexing. | *cortex-mem* → Qdrant | +| **OpenAI** | API Integration | LLM service used for: | *cortex-mem* → OpenAI | +| **LangMem** | Benchmarking | Alternative memory system used for comparative evaluation and performance testing. Not integrated at runtime. | One-way analysis (no runtime dependency) | -| External System | Interaction Type | Description | Direction | -|----------------|------------------|-------------|----------| -| **Qdrant** | Database Storage | Vector database used to store memory embeddings and enable semantic search. Memories are indexed by their vector representations for fast similarity lookup. | System → Qdrant (Write/Read) | -| **LLM Services** (e.g., OpenAI, Anthropic, etc.) | API Integration | External language model APIs used to generate embeddings for memory content and queries. Also used during optimization to assess and rewrite memories. | System → LLM (Request/Response) | -| **HTTP Clients** | User Interface | Web browsers and HTTP clients that access the Insights Dashboard and REST API. Includes developer tools, monitoring systems, and agent integrations. | Client → System | -| **Command Line Interface (CLI)** | User Interface | Terminal applications used by developers and administrators to interact with the system via shell commands. | User → System | +### Interaction Details -### Dependency Analysis +#### Qdrant Integration +- **Purpose**: Persistent storage of vector embeddings and metadata. +- **Protocol**: gRPC/HTTP (via Qdrant client SDK). +- **Data Flow**: + - On memory creation: embedding vector + metadata → stored in Qdrant collection. + - On search: query embedding → similarity search → top-k results returned. +- **Failure Impact**: Loss of semantic search capability; memory metadata may still be accessible via fallback mechanisms. -| From | To | Type | Strength | Description | -|------|----|------|----------|-------------| -| `cortex-mem` → Qdrant | Service Dependency | 9.5 | Core persistence layer; all memory data is stored and retrieved via Qdrant’s vector search capabilities. | -| `cortex-mem` → LLM Services | Service Call | 9.0 | Critical for embedding generation and memory optimization. System cannot function without access to LLM APIs. | -| HTTP Clients → `cortex-mem` | User Interaction | 8.5 | Primary means for users to view analytics and trigger operations via dashboard or API. | -| CLI Users → `cortex-mem` | User Interaction | 8.0 | Used for scripting, debugging, and administrative control. | +#### OpenAI Integration +- **Purpose**: Enable intelligent memory processing via LLM capabilities. +- **Usage Patterns**: + - Embedding generation (`text-embedding-ada-002` or equivalent). + - Structured extraction (e.g., "extract entities from this conversation"). + - Memory analysis (e.g., "are these two memories duplicates?"). +- **Protocol**: HTTPS REST API. +- **Failure Impact**: Degraded functionality in memory creation, search, and optimization; system may fall back to cached embeddings or skip enrichment. -> **Note**: All external dependencies are abstracted behind interfaces in `cortex-mem-core`, allowing for future pluggability (e.g., switching from Qdrant to Pinecone or Weaviate). +#### LangMem (Benchmarking) +- **Purpose**: Used in evaluation workflows to compare *cortex-mem*’s performance (latency, recall, precision) against alternative memory systems. +- **Interaction**: Offline comparison scripts; no runtime coupling. +- **Impact**: No operational dependency; used solely for R&D and validation. --- ## 5. System Context Diagram -### C4 Model – System Context (Level 1) +Below is a **C4 Model - Level 1: System Context** diagram representing *cortex-mem* and its relationships with users and external systems. -Below is a Mermaid-formatted diagram representing the **System Context** view of `cortex-mem`. This diagram illustrates the system as a single container and its relationships with external entities. +### Mermaid Diagram (System Context) ```mermaid C4Context title System Context Diagram for cortex-mem - Person(developer, "AI Agent Developer", "Builds AI agents that require persistent memory. Uses API and CLI.") - Person(admin, "System Administrator", "Manages system health, runs optimizations, monitors usage.") - Person(researcher, "Research Scientist", "Evaluates memory performance, runs benchmarks, analyzes data.") + Person(ai_agent, "AI Agent", "Intelligent software agent requiring persistent memory") + Person(developer, "Developer", "Engineer integrating memory into AI applications") + Person(admin, "System Administrator", "Operator managing memory infrastructure") - System(cortex_mem, "cortex-mem", "Memory management system for AI agents with semantic search, optimization, and analytics.") + System(cortex_mem, "cortex-mem", "Memory management system for AI agents\nProvides persistent, searchable, and optimizable memory storage") - System_Ext(qdrant, "Qdrant", "Vector database for storing and retrieving memory embeddings.") - System_Ext(llm_services, "LLM Services", "External APIs for generating embeddings and improving memory quality (e.g., OpenAI, Anthropic).") - System_Boundary(http_clients, "HTTP Clients") { - System_Ext(browser, "Web Browser", "Accesses insights dashboard and REST API.") - } - System_Boundary(cli, "CLI Environment") { - System_Ext(terminal, "Terminal", "Runs cortex-mem-cli commands.") - } + System_Ext(qdrant, "Qdrant", "Vector database for embedding storage and semantic search") + System_Ext(openai, "OpenAI", "LLM service for embeddings, text generation, and analysis") + System_Ext(langmem, "LangMem", "Alternative memory system used for benchmarking") - Rel(developer, cortex_mem, "Uses API and CLI to manage agent memories") - Rel(admin, cortex_mem, "Monitors via dashboard, runs optimization via CLI") - Rel(researcher, cortex_mem, "Runs evaluations, extracts data via API") + Rel(ai_agent, cortex_mem, "Uses memory via MCP or API", "JSON/HTTP, MCP Protocol") + Rel(developer, cortex_mem, "Integrates via API/CLI", "REST API, CLI Commands") + Rel(admin, cortex_mem, "Monitors & manages via dashboard", "Web UI, CLI") - Rel(cortex_mem, qdrant, "Stores and retrieves vector embeddings via gRPC/HTTP") - Rel(cortex_mem, llm_services, "Sends text to generate embeddings and optimize memories") - Rel(cortex_mem, browser, "Serves web UI and accepts API requests over HTTPS") - Rel(cortex_mem, terminal, "Accepts command-line input and returns output") - - UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="2") + Rel(cortex_mem, qdrant, "Stores/retrieves vectors", "gRPC/HTTP") + Rel(cortex_mem, openai, "Requests embeddings & analysis", "HTTPS API") + Rel_R(cortex_mem, langmem, "Benchmarked against", "Offline evaluation") ``` ### Key Interaction Flows 1. **Memory Creation Flow** - - Developer sends `POST /memories` via HTTP client. - - `cortex-mem` uses LLM service to generate embedding. - - Stores memory + embedding in Qdrant. - - Returns memory ID. - -2. **Semantic Search Flow** - - User submits natural language query via API or CLI. - - Query is embedded using LLM service. - - Vector search performed in Qdrant. - - Results filtered and ranked by `cortex-mem-core`, returned to user. - -3. **Optimization Flow** - - Admin triggers `/optimization/start`. - - System analyzes memories for duplicates and quality issues. - - LLM rewrites or merges low-quality entries. - - Updated memories stored back in Qdrant. - - Report generated and accessible via dashboard. - -4. **Insights Access Flow** - - Browser requests dashboard page. - - `cortex-mem-insights` fetches memory statistics and optimization history. - - Renders charts and tables showing memory growth, search success rate, etc. - -### Architecture Decisions Reflected -- **Single System Boundary**: All components (`core`, `service`, `cli`, `insights`) are treated as one cohesive system. -- **Externalized AI & Storage**: LLMs and vector DBs are external systems, emphasizing cloud-native integration. -- **Multi-Channel Access**: Supports both machine (API) and human (CLI, UI) interaction patterns. -- **No Direct User-to-Database Access**: All data flows through `cortex-mem`, ensuring consistency and security. + - User/AI agent → CLI/API/MCP → *cortex-mem* → OpenAI (embedding) → Qdrant (storage) + - Metadata and extracted facts are stored alongside the vector. + +2. **Memory Retrieval Flow** + - Query → *cortex-mem* → OpenAI (query embedding) → Qdrant (similarity search) → Ranked results → User/Agent + +3. **Memory Optimization Flow** + - Admin triggers optimization → *cortex-mem* analyzes memory collection → Uses OpenAI to assess similarity → Executes merge/delete → Reports via dashboard + +4. **Monitoring & Configuration** + - Admin uses web dashboard to view memory stats, run optimizations, and adjust configuration. + - All components read from centralized config (e.g., `config.toml`). --- @@ -195,60 +178,69 @@ C4Context ### Main Technology Stack -| Layer | Technology | Purpose | -|------|-----------|--------| -| **Core Engine** | Rust (`cortex-mem-core`) | High-performance, memory-safe implementation of memory logic. | -| **API Service** | Rust + Axum (`cortex-mem-service`) | RESTful HTTP server with async support. | -| **CLI Tool** | Rust + Clap (`cortex-mem-cli`) | Cross-platform command-line interface. | -| **Vector Database** | Qdrant | Persistent, scalable vector storage with semantic search. | -| **LLM Integration** | OpenAI API, Anthropic, or compatible | Embedding generation and memory refinement. | -| **Frontend (Insights)** | TypeScript + React | Interactive dashboard for analytics and reporting. | -| **Configuration** | TOML | Human-readable, version-controllable config files. | -| **Protocol Support** | MCP (Memory Consistency Protocol) | Standardized interface for agent memory synchronization. | +| Layer | Technology | Rationale | +|------|-----------|---------| +| **Backend Core** | Rust | High performance, memory safety, ideal for systems-level logic and LLM integration. | +| **Frontend Dashboard** | TypeScript (SvelteKit) | Reactive UI framework for real-time monitoring and interaction. | +| **Vector Database** | Qdrant | Efficient ANN search, strong filtering support, gRPC/HTTP API. | +| **LLM Provider** | OpenAI | Industry-standard embeddings and structured output capabilities. | +| **Configuration** | TOML + Environment Variables | Human-readable, hierarchical, and deployable across environments. | +| **Interfaces** | | Multiple entry points for different user types and integration needs. | ### Architecture Patterns -| Pattern | Application in `cortex-mem` | -|--------|----------------------------| -| **Layered Architecture** | Clear separation: Core Logic → Service Layer → Interface Layer (API/CLI/Dashboard). | -| **Plugin Abstraction** | LLM clients and vector stores implement traits/interfaces for easy swapping. | -| **Event-Driven Orchestration** | Optimization workflows are triggered and progress asynchronously. | -| **Configuration-Driven Behavior** | System behavior (e.g., embedding model, thresholds) is defined in TOML. | -| **Evaluation-First Design** | Built-in tools allow continuous validation of memory recall and quality. | - -### Key Design Decisions +#### Modular Microservices Architecture +- **Separation of Concerns**: Each domain (e.g., Memory Management, LLM Integration) is encapsulated with clear responsibilities. +- **Shared Core Library**: `cortex-mem-core` contains business logic reused across CLI, API, MCP, and dashboard services. +- **Frontend-Backend Decoupling**: Web dashboard communicates via RESTful APIs to backend services. -1. **Rust as Primary Language** - - Chosen for memory safety, performance, and concurrency—critical for systems handling large-scale data and real-time queries. +#### Domain-Driven Design (DDD) +The system is organized into well-defined domains: -2. **Vector Database Abstraction** - - The `vector_store` module abstracts Qdrant behind a trait, enabling future support for alternative backends (e.g., Chroma, FAISS). +| Domain | Type | Responsibility | +|-------|------|----------------| +| **Memory Management Domain** | Core Business | Memory lifecycle, search, classification | +| **Memory Optimization Domain** | Core Business | Quality analysis, deduplication, plan execution | +| **LLM Integration Domain** | Core Business | Embedding generation, content extraction, intelligence | +| **Storage Integration Domain** | Infrastructure | Qdrant interaction, vector persistence | +| **Access Interface Domain** | Tool Support | CLI, API, MCP, Web UI | +| **Configuration Management Domain** | Infrastructure | Centralized settings for all components | -3. **LLM as a Service** - - Avoids coupling to any single provider; supports multiple LLM APIs through a unified client interface. +#### Key Design Decisions -4. **Unified Core Module** - - `cortex-mem-core` encapsulates all business logic, ensuring consistent behavior across API, CLI, and dashboard. +| Decision | Rationale | Impact | +|--------|---------|--------| +| **Rust for Core Logic** | Ensures performance, safety, and concurrency for high-throughput memory operations. | Enables low-latency responses even under heavy load. | +| **Multiple Access Interfaces** | Supports diverse use cases: agents (MCP), developers (API), operators (CLI/Web). | Increases adoption and integration flexibility. | +| **Centralized Configuration** | Single source of truth for LLM keys, Qdrant URLs, optimization thresholds. | Simplifies deployment and reduces misconfiguration risk. | +| **Vector + Metadata Hybrid Storage** | Combines semantic search (vector) with precise filtering (metadata). | Delivers accurate and contextually relevant results. | +| **LLM-Driven Intelligence** | Leverages LLMs not just for embeddings but also for fact extraction and decision logic. | Enables smarter memory management beyond keyword matching. | -5. **Separation of Concerns** - - Each domain (Memory Management, Storage, Optimization, Insights) has well-defined responsibilities and minimal overlap. +### Cross-Domain Dependencies -6. **Extensible Evaluation Framework** - - Allows developers and researchers to define custom test suites for memory recall, search accuracy, and optimization impact. +```mermaid +graph TD + A[Access Interface Domain] -->|Calls| B(Memory Management Domain) + B -->|Uses| C[LLM Integration Domain] + B -->|Stores to| D[Storage Integration Domain] + C -->|Config from| E[Configuration Management Domain] + D -->|Config from| E + F[Memory Optimization Domain] -->|Analyzes & Updates| B + F -->|Config from| E + A -->|Config from| E +``` -7. **TOML-Based Configuration** - - Enables declarative setup of system parameters (e.g., embedding dimensions, optimization thresholds, API keys). +> 🔗 **Dependency Strength Summary**: +> - Strongest: Access Interface → Memory Management (9.5) +> - Critical: Memory Management → Storage Integration (9.0) +> - Important: All domains → Configuration Management (7.0–8.5) --- ## Conclusion -The `cortex-mem` system represents a robust, production-ready memory management platform tailored for AI agents. Its **System Context** clearly defines its role within the broader ecosystem: a centralized, intelligent memory store that integrates with vector databases and LLMs to provide persistent, searchable, and self-improving knowledge retention. - -By adhering to the C4 model’s System Context level, this document establishes a shared understanding across stakeholders—developers, operators, and researchers—of what the system does, who uses it, how it interacts with the outside world, and what lies within its architectural boundaries. - -This foundation enables informed decision-making for future development, integration, and operational planning. +The *cortex-mem* system establishes a robust, extensible foundation for AI agent memory management. By clearly defining its boundaries, user roles, and external integrations, it serves as a reliable middleware component in modern AI architectures. ---- +Its modular design, multi-interface support, and intelligent use of LLMs and vector databases make it suitable for both research and production environments. The system enables AI agents to evolve from stateless responders to context-aware, learning entities—paving the way for more autonomous and effective artificial intelligence. -**End of Document** \ No newline at end of file +This C4 System Context document provides a comprehensive view of *cortex-mem*’s role in the ecosystem, serving as a reference for architects, developers, and operators involved in its deployment and extension. \ No newline at end of file diff --git a/litho.docs/2.Architecture.md b/litho.docs/2.Architecture.md index bb089c2..f942bcd 100644 --- a/litho.docs/2.Architecture.md +++ b/litho.docs/2.Architecture.md @@ -1,7 +1,7 @@ # System Architecture Documentation **Project:** `cortex-mem` -**Generated at:** 2025-12-18 03:29:11 (UTC) (UTC) -**Timestamp:** 1766028551 +**Generated on:** 2025-12-30 11:19:34 (UTC) +**Timestamp:** 1767093574 --- @@ -9,44 +9,31 @@ ### Architecture Design Philosophy -The **cortex-mem** system is designed around the principles of **modularity**, **extensibility**, and **intelligent automation**, with a strong emphasis on enabling **AI agents to maintain persistent, searchable, and optimizable knowledge** across interactions. The architecture follows a **layered, domain-driven design (DDD)** approach, where core business logic is encapsulated in a central engine (`cortex-mem-core`), while multiple interface layers provide flexible access for developers, operators, and researchers. +The `cortex-mem` system is designed around the principle of **persistent, intelligent memory management for AI agents**, enabling them to retain context across interactions and improve decision-making over time. The architecture emphasizes **modularity, extensibility, and interoperability**, ensuring that diverse integration scenarios—from CLI tools to agent frameworks—can coexist seamlessly. -The system embraces **separation of concerns** through well-defined boundaries between: -- **Core memory processing** -- **Storage and retrieval** -- **User-facing interfaces** -- **Analytics and evaluation** +At its core, the system follows a **microservices-inspired modular monolith pattern**, where core business logic is centralized in a shared Rust-based library (`cortex-mem-core`), while multiple access interfaces (CLI, HTTP API, MCP, Web UI) operate as independent but tightly integrated components. This design balances performance and maintainability with flexibility and scalability. -This modular structure ensures that each component can evolve independently, supporting both research experimentation and production deployment. - ---- +The system leverages **external AI and vector storage services** (OpenAI and Qdrant) to offload computationally intensive tasks such as embedding generation and semantic search, allowing the core engine to focus on orchestration, optimization, and lifecycle management. ### Core Architecture Patterns -| Pattern | Application | Benefit | -|-------|------------|--------| -| **Modular Monolith / Plugin Architecture** | Core engine exposes pluggable traits (`VectorStore`, `LLMClient`, etc.) | Enables extensibility without tight coupling | -| **Strategy Pattern** | Used for detectors, evaluators, and optimizers | Allows runtime selection of algorithms based on configuration | -| **Shared State Pattern** | `Arc` shared across API handlers | Thread-safe access to core engine in async environment | -| **Command-Query Responsibility Segregation (CQRS)** | Separation of write (CRUD) and read (search/analysis) paths | Optimized data flow for different use cases | -| **Event-Driven Orchestration (Implicit)** | Optimization workflows trigger analysis → planning → execution | Supports complex, multi-stage processes | - ---- +- **Layered Architecture**: Clear separation into access, business logic, infrastructure, and support layers. +- **Modular Monolith with Shared Core**: All interfaces share a common core engine, reducing duplication and ensuring consistency. +- **Event-Driven Orchestration**: Workflows are initiated by user or agent requests and orchestrated through a centralized memory manager. +- **Configuration-Driven Behavior**: A centralized configuration system (`cortex-mem-config`) ensures uniform behavior across all components. +- **LLM-Augmented Intelligence**: LLMs are used not just for embeddings, but also for content analysis, fact extraction, and optimization decisions. ### Technology Stack Overview -| Layer | Technology | Rationale | -|------|-----------|---------| -| **Core Engine** | Rust | Memory safety, performance, async support, trait-based abstraction | -| **REST API** | Axum (Rust) | Lightweight, type-safe, integrates seamlessly with Tokio | -| **CLI Interface** | Clap (Rust) | Declarative argument parsing, rich CLI experience | -| **Web Dashboard** | SvelteKit + Elysia.js | Fast frontend, minimal boilerplate, TypeScript safety | -| **Vector Database** | Qdrant | High-performance semantic search, metadata filtering, cloud-native | -| **LLM Integration** | OpenAI-compatible APIs via RIG | Flexibility to use various LLM providers (OpenAI, Anthropic, local models) | -| **Configuration** | TOML | Human-readable, hierarchical, widely supported | -| **Runtime** | Tokio | Asynchronous execution model for high concurrency | - -> ✅ **Key Insight**: The choice of **Rust** as the primary language reflects a focus on **performance, reliability, and safety**—critical for systems handling persistent agent memory where data integrity and low-latency operations are essential. +| Layer | Technology | Purpose | +|------|-----------|--------| +| **Core Engine** | Rust | High-performance, memory-safe backend logic | +| **Interfaces** | Rust (CLI, API, MCP), TypeScript/Svelte (Web UI) | Multi-channel access | +| **Storage** | Qdrant (Vector DB) | Semantic search via embeddings | +| **AI Services** | OpenAI API | Embedding generation, content analysis, structured extraction | +| **Configuration** | TOML | Human-readable, hierarchical configuration | +| **Observability** | Tracing (OpenTelemetry), Logging | Debugging and monitoring | +| **Build & Packaging** | Cargo (Rust), npm/pnpm (Web) | Dependency and build management | --- @@ -54,65 +41,78 @@ This modular structure ensures that each component can evolve independently, sup ### System Positioning and Value -**cortex-mem** is a **full-stack memory management platform** designed specifically for **AI agents**. It enables agents to: -- Retain context across conversations and tasks -- Recall relevant past experiences via semantic search -- Optimize memory quality through deduplication and enhancement -- Visualize and analyze memory patterns +`cortex-mem` is a **full-stack memory management system** that provides AI agents with persistent, searchable, and optimizable memory. It enables agents to: -This capability significantly improves agent **consistency**, **effectiveness**, and **long-term intelligence**, making it a foundational component in advanced AI agent architectures. +- Retain conversation history and learned knowledge +- Retrieve relevant memories using semantic search +- Optimize memory usage by removing duplicates and low-quality entries +- Integrate seamlessly into agent workflows via multiple protocols ---- +This significantly enhances **agent intelligence, continuity, and user experience** by preventing context loss and enabling long-term learning. ### User Roles and Scenarios -| Role | Description | Key Use Cases | -|------|-----------|-------------| -| **AI Agent Developers** | Engineers integrating memory into AI applications | - Store agent interactions
- Retrieve context before responses
- Monitor memory usage via API | -| **System Administrators** | Ops teams managing agent infrastructure | - Run memory optimization jobs
- Monitor system health
- Analyze memory trends and costs | -| **Research Scientists** | Researchers evaluating agent memory systems | - Benchmark recall accuracy
- Test optimization strategies
- Generate synthetic datasets | - ---- +| Role | Needs | Interaction Scenarios | +|------|-------|------------------------| +| **AI Agents** | Store context, retrieve relevant memories, optimize memory usage | Use MCP or API to log interactions and query past experiences | +| **Developers** | Integrate memory into AI apps, configure behavior, debug | Use CLI, API, or SDKs to build agent systems | +| **System Administrators** | Monitor health, optimize performance, manage configurations | Use web dashboard and CLI for maintenance and tuning | ### External System Interactions ```mermaid graph TD - A[cortex-mem] -->|API Calls| B[LLM Services] - A -->|gRPC/HTTP| C[Qdrant Vector Database] - D[AI Agent / App] -->|HTTP Requests| A - E[Admin User] -->|CLI Commands| A - F[Researcher] -->|Dashboard Access| A - G[External Agent Framework] -->|MCP Tool Calls| A - - style A fill:#2196F3,stroke:#1976D2,color:white - style B fill:#FFC107,stroke:#FFA000,color:black - style C fill:#4CAF50,stroke:#388E3C,color:white - style D fill:#9E9E9E,stroke:#616161,color:white - style E fill:#9E9E9E,stroke:#616161,color:white - style F fill:#9E9E9E,stroke:#616161,color:white - style G fill:#9E9E9E,stroke:#616161,color:white - - classDef user fill:#9E9E9E,stroke:#616161,color:white; - class D,E,F,G user; -``` + subgraph ExternalSystems [External Systems] + Qdrant[(Qdrant Vector Database)] + OpenAI[(OpenAI LLM Service)] + LangMem[(LangMem - Benchmark)] + end -#### Interaction Types: -- **LLM Services**: Used for embedding generation, classification, keyword extraction, and optimization guidance. -- **Qdrant**: Primary vector database for storing memory embeddings and metadata. -- **HTTP Clients**: Access REST API and web dashboard. -- **CLI Tools**: Direct terminal interaction for scripting and debugging. -- **MCP Clients**: Integration with agent frameworks using Model Context Protocol. + subgraph cortex-mem [cortex-mem System] + CLI[cortex-mem-cli] + HTTPAPI[cortex-mem-service] + MCP[cortex-mem-mcp] + WebUI[cortex-mem-insights] + Core[cortex-mem-core] + end ---- + CLI --> Core + HTTPAPI --> Core + MCP --> Core + WebUI --> HTTPAPI + WebUI --> Core + + Core --> Qdrant + Core --> OpenAI + HTTPAPI --> OpenAI + MCP --> OpenAI + + WebUI -.-> LangMem + HTTPAPI -.-> LangMem + + style cortex-mem fill:#2196F3,stroke:#1976D2,color:white + style ExternalSystems fill:#9C27B0,stroke:#7B1FA2,color:white +``` + +- **Qdrant**: Used for storing and retrieving memory embeddings via vector similarity search. +- **OpenAI**: Provides text embeddings, content analysis, and structured extraction (facts, entities). +- **LangMem**: Used for benchmarking and comparative evaluation of memory systems. ### System Boundary Definition -| Included Components | Excluded Components | -|---------------------|---------------------| -| - Memory CRUD operations
- Semantic search with vector embeddings
- Deduplication and optimization
- Analytics dashboard
- REST/CLI/MCP interfaces
- Evaluation framework | - Training of LLMs or embedding models
- Development of vector database engine
- OS-level resource management
- Network infrastructure provisioning | +#### Included Components +- Memory storage and retrieval +- Semantic search capabilities +- Memory optimization engine +- Multiple access interfaces (CLI, API, MCP, Web Dashboard) +- Configuration management +- Integration with LLM and vector database services -> 🔒 **Boundary Clarity**: The system **integrates** with external AI and storage services but does **not own** their underlying models or infrastructure. +#### Excluded Components +- Core AI agent logic (e.g., reasoning, planning) +- Application-specific business rules +- End-user UI design for agent applications +- Network infrastructure management (e.g., load balancing, firewalls) --- @@ -120,113 +120,79 @@ graph TD ### Domain Module Division -The system is structured into **five primary domains**, each representing a logical grouping of functionality: +The system is divided into six primary domain modules, each responsible for a distinct aspect of functionality: | Domain | Type | Responsibility | |-------|------|----------------| -| **Memory Management Domain** | Core Business | Full lifecycle management of memories (CRUD, retrieval, metadata) | -| **Memory Storage Domain** | Technical | Persistent storage using vector database (Qdrant) | -| **Optimization Domain** | Core Business | Improve memory quality via deduplication, relevance tuning, enhancement | -| **Insights & Analytics Domain** | Support | Visualization, monitoring, and reporting | -| **Evaluation & Tools Domain** | Support | Benchmarking, testing, and example agents | - ---- +| **Memory Management Domain** | Core Business | CRUD, classification, search | +| **Memory Optimization Domain** | Core Business | Deduplication, quality analysis, merging | +| **LLM Integration Domain** | Core Business | Embedding, extraction, intelligence | +| **Storage Integration Domain** | Infrastructure | Qdrant interaction, vector persistence | +| **Access Interface Domain** | Tool Support | CLI, API, MCP, Web UI | +| **Configuration Management Domain** | Infrastructure | Centralized settings | ### Domain Module Architecture ```mermaid graph TD - subgraph "External Systems" - LLM[LLM Services] - QDRANT[Qdrant DB] - HTTP[HTTP Clients] - CLI[CLI Users] - MCP[Agent Frameworks] - end - - subgraph "cortex-mem System" - CORE[cortex-mem-core\n(Memory Engine)] - - subgraph Interfaces - SERVICE[cortex-mem-service\n(Rest API)] - CLI_INTERFACE[cortex-mem-cli\n(Command Line)] - MCP_ADAPTER[cortex-mem-mcp\n(MCP Adapter)] - INSIGHTS[cortex-mem-insights\n(Dashboard UI)] - end - - subgraph Support - EVAL[cortex-mem-evaluation\n(Benchmarking)] - TARS[cortex-mem-tars\n(Terminal Agent)] - end + subgraph Domains [Domain Modules] + MMD[Memory Management Domain] + MOD[Memory Optimization Domain] + LLM[LLM Integration Domain] + SID[Storage Integration Domain] + AID[Access Interface Domain] + CMD[Configuration Management Domain] end - SERVICE --> CORE - CLI_INTERFACE --> CORE - MCP_ADAPTER --> CORE - INSIGHTS --> SERVICE - EVAL --> CORE - TARS --> CORE - - CORE --> LLM - CORE --> QDRANT - - HTTP --> SERVICE - CLI --> CLI_INTERFACE - MCP --> MCP_ADAPTER - - style CORE fill:#4CAF50,stroke:#388E3C,color:white - style SERVICE fill:#2196F3,stroke:#1976D2,color:white - style CLI_INTERFACE fill:#9C27B0,stroke:#7B1FA2,color:white - style MCP_ADAPTER fill:#FF9800,stroke:#F57C00,color:white - style INSIGHTS fill:#00BCD4,stroke:#00ACC1,color:white - style EVAL fill:#795548,stroke:#5D4037,color:white - style TARS fill:#607D8B,stroke:#455A64,color:white + AID --> MMD + MMD --> LLM + MMD --> SID + MOD --> MMD + LLM --> CMD + SID --> CMD + MOD --> CMD + AID --> CMD + + style MMD fill:#4CAF50,stroke:#388E3C,color:white + style MOD fill:#4CAF50,stroke:#388E3C,color:white + style LLM fill:#4CAF50,stroke:#388E3C,color:white + style SID fill:#2196F3,stroke:#1976D2,color:white + style CMD fill:#2196F3,stroke:#1976D2,color:white + style AID fill:#607D8B,stroke:#455A64,color:white ``` ---- - ### Storage Design -#### Data Model (Qdrant Collection Schema) - -```json -{ - "id": "uuid", - "vector": "float[1536]", // Embedding dimension (auto-detected) - "payload": { +- **Primary Storage**: Qdrant vector database + - Stores memory vectors (embeddings), metadata, and structured content + - Supports similarity search via cosine distance + - Collections are auto-created based on agent or session context +- **Metadata Schema**: + ```json + { + "id": "uuid", "content": "string", - "metadata": { - "source": "string", - "timestamp": "datetime", - "type": "string", - "importance": "float", - "version": "int" - }, + "embedding": "float[]", + "type": "conversational|factual|procedural", + "timestamp": "datetime", + "source": "agent|user", + "quality_score": "float", "keywords": ["string"], - "embedding_model": "string" + "entities": ["string"] } -} -``` - -#### Key Features: -- **Semantic Search**: Enabled via vector similarity (cosine distance) -- **Metadata Filtering**: Supports filtering by `type`, `source`, `timestamp`, etc. -- **Batch Operations**: Efficient bulk insert/update/delete -- **Auto-detection**: Dynamically detects embedding dimensions from LLM - ---- + ``` +- **Indexing Strategy**: HNSW index for fast approximate nearest neighbor search +- **Persistence**: All writes are synchronous; reads are cached via in-memory LRU where applicable ### Inter-Domain Module Communication -| From → To | Communication Mechanism | Protocol/Data Format | -|---------|--------------------------|-----------------------| -| Memory Management → Storage | Function call (`upsert_point`, `search`) | Rust trait interface (`VectorStore`) | -| Memory Management → LLM | HTTP API call | JSON over HTTPS | -| Optimization → Memory Management | Direct method invocation | In-process Rust calls | -| Insights → Memory Management | REST API call | JSON over HTTP | -| CLI/API → Core | Shared state (`Arc`) | In-memory Rust objects | - -> 🔄 **Communication Pattern**: Most inter-module communication occurs **in-process** via function calls or shared state, ensuring low latency. External access (dashboard, evaluation) uses **REST API** as a secure boundary. +| From → To | Communication Type | Mechanism | +|---------|---------------------|----------| +| Access Interface → Memory Management | Synchronous Request | Function calls (Rust) / HTTP (Web) | +| Memory Management → LLM Integration | Synchronous API Call | REST to OpenAI | +| Memory Management → Storage Integration | Synchronous DB Call | gRPC to Qdrant | +| Memory Optimization → Memory Management | Synchronous Service Call | In-process method invocation | +| All Domains → Configuration Management | Configuration Pull | TOML file parsing at startup | --- @@ -234,208 +200,167 @@ graph TD ### Core Functional Components -#### `cortex-mem-core` – Memory Engine - -**Responsibilities**: -- Orchestrate memory lifecycle -- Generate embeddings and metadata -- Execute optimization plans -- Manage vector store interactions - -**Internal Structure**: -```rust -pub struct MemoryManager { - vector_store: Box, - llm_client: Box, - fact_extractor: Box, - memory_updater: Box, - importance_evaluator: Box, - duplicate_detector: Box, - memory_classifier: Box, -} -``` - -**Key Traits**: -| Trait | Purpose | Implementations | -|------|--------|----------------| -| `VectorStore` | Abstracts vector DB operations | `QdrantStore`, `ChromaStore` (planned) | -| `LLMClient` | Standardizes LLM interactions | `OpenAIClient`, `AnthropicClient`, `LocalLlama` | -| `DuplicateDetector` | Detects redundant memories | Rule-based, LLM-based, hybrid | - ---- - -#### `cortex-mem-service` – REST API - -- Built with **Axum** -- Endpoints: - - `POST /memories` – Create memory - - `GET /memories/{id}` – Retrieve by ID - - `POST /memories/search` – Semantic search - - `POST /optimization/start` – Trigger optimization - - `GET /status` – Health check -- Uses `Arc` for thread-safe access -- JSON request/response format - ---- - -#### `cortex-mem-cli` – Command Line Interface - -- Built with **Clap** -- Commands: - - `add "content" --type=event` - - `search "what did I do yesterday?"` - - `optimize --strategy=dedupe` - - `list --filter type=belief` -- Reads config from `config.toml` -- Asynchronous execution via Tokio - ---- - -#### `cortex-mem-mcp` – MCP Protocol Adapter - -- Implements **Model Context Protocol (MCP)** -- Exposes tools: - - `store_memory(content, metadata)` - - `query_memories(query, filters)` - - `list_memories(filter)` - - `get_memory(id)` -- Enables integration with agent frameworks like LangChain, AutoGPT - ---- - -#### `cortex-mem-insights` – Dashboard UI - -- **Frontend**: SvelteKit (TypeScript) -- **Backend**: Elysia.js (Bun runtime) -- Features: - - Memory distribution charts - - Optimization history timeline - - Real-time memory stream - - Search playground -- Pulls data from `cortex-mem-service` API - ---- +| Component | Responsibility | Key Functions | +|---------|----------------|-------------| +| **MemoryManager** | Central orchestrator | Create, retrieve, update, delete memories; coordinate optimization | +| **LLMClient** | LLM interaction | Generate embeddings, extract facts, analyze content | +| **VectorStore (Qdrant)** | Vector persistence | Store/retrieve embeddings, execute similarity search | +| **OptimizationEngine** | Memory quality control | Detect duplicates, suggest merges, execute cleanup | +| **MemoryClassifier** | Type inference | Classify memory as conversational, factual, procedural | +| **MemoryExtractor** | Structured data extraction | Extract keywords, entities, facts using LLM prompts | ### Technical Support Components -| Component | Purpose | Technology | -|--------|--------|-----------| -| `cortex-mem-evaluation` | Benchmark memory recall and effectiveness | Synthetic dataset generation, accuracy scoring | -| `cortex-mem-tars` | Example terminal agent demonstrating usage | Interactive CLI agent with memory loop | - ---- +| Component | Responsibility | Key Functions | +|---------|----------------|-------------| +| **ConfigManager** | Configuration loading | Parse `config.toml`, validate, provide defaults | +| **TracingSystem** | Observability | Log spans, metrics, errors via OpenTelemetry | +| **CLI Interface** | Command-line access | Parse commands, execute workflows, display results | +| **HTTP API Service** | RESTful access | Handle JSON requests, validate input, return responses | +| **MCP Server** | Agent protocol interface | Handle MCP tool calls, route to memory functions | +| **Web Dashboard** | Monitoring UI | Visualize memory stats, trigger optimization, view logs | ### Component Responsibility Division ```mermaid graph TD - CLI_INTERFACE -->|Calls| CORE - SERVICE -->|Exposes| CORE - MCP_ADAPTER -->|Translates| CORE - INSIGHTS -->|Fetches| SERVICE - EVAL -->|Tests| CORE - TARS -->|Uses| CORE - - CORE -->|Uses| LLM_CLIENT[cortex-mem-core::llm::client] - CORE -->|Uses| VEC_STORE[cortex-mem-core::vector_store::qdrant] - CORE -->|Uses| MEM_MANAGER[cortex-mem-core::memory::manager] - CORE -->|Uses| OPTIMIZER[cortex-mem-core::memory::optimizer] - - style CORE fill:#4CAF50,stroke:#388E3C,color:white - style LLM_CLIENT fill:#FFEB3B,stroke:#FDD835,color:black - style VEC_STORE fill:#FF9800,stroke:#FB8C00,color:black - style MEM_MANAGER fill:#03A9F4,stroke:#0288D1,color:white - style OPTIMIZER fill:#8BC34A,stroke:#7CB342,color:white + subgraph Components [Component Responsibilities] + MM[MemoryManager] + LC[LLMClient] + VS[VectorStore] + OE[OptimizationEngine] + MC[MemoryClassifier] + ME[MemoryExtractor] + CM[ConfigManager] + CLI[CLI Interface] + API[HTTP API Service] + MCP[MCP Server] + WD[Web Dashboard] + end + + CLI --> MM + API --> MM + MCP --> MM + WD --> API + WD --> MM + + MM --> LC + MM --> VS + MM --> OE + MM --> MC + MM --> ME + + LC --> OpenAI + VS --> Qdrant + + MM --> CM + CLI --> CM + API --> CM + MCP --> CM + WD --> CM + + style MM fill:#4CAF50,stroke:#388E3C,color:white + style LC fill:#FF9800,stroke:#F57C00,color:white + style VS fill:#9C27B0,stroke:#7B1FA2,color:white + style OE fill:#FF5722,stroke:#E64A19,color:white ``` +### Component Interaction Relationships + +- **Request Flow**: + 1. Interface receives request (e.g., `add memory`) + 2. ConfigManager provides settings + 3. MemoryManager orchestrates: + - Classify content + - Generate embedding via LLMClient + - Extract facts/entities + - Store in VectorStore + 4. Response returned via interface + +- **Optimization Flow**: + 1. User triggers optimization + 2. OptimizationEngine analyzes memory collection + 3. Uses LLMClient to assess similarity + 4. Generates plan (merge/delete) + 5. Executes via MemoryManager + 6. Reports results via Web Dashboard or CLI + --- ## 5. Key Processes ### Core Functional Processes -#### Memory Management Process +#### Memory Creation Process ```mermaid graph TD - A[User/Application] --> B{Initiate Memory Operation} - B --> C[HTTP API / CLI Command] - C --> D[Process Request] - D --> E[Generate Embeddings via LLM Client] - E --> F[Store in Qdrant Vector Database] - F --> G[Return Success Response with Memory ID] - G --> A + A[User/Agent: Add Memory] --> B[CLI/API/MCP Interface] + B --> C[MemoryManager: Classify Type] + C --> D[LLMClient: Generate Embedding] + D --> E[MemoryExtractor: Extract Facts/Entities] + E --> F[MemoryManager: Build Memory Object] + F --> G[VectorStore: Persist in Qdrant] + G --> H[Return Success] ``` -**Sequence Flow**: -1. Client sends `POST /memories` with content -2. Handler calls `MemoryManager::create_memory()` -3. LLM generates embedding and extracts keywords/classification -4. Memory stored in Qdrant with full payload -5. Response returns `201 Created` with memory ID +#### Memory Retrieval Process ---- +```mermaid +graph TD + A[User/Agent: Search Query] --> B[API/CLI Interface] + B --> C[LLMClient: Generate Query Embedding] + C --> D[VectorStore: Semantic Search] + D --> E[MemoryManager: Apply Metadata Filters] + E --> F[Rank & Return Results] +``` -#### Memory Search Process +#### Memory Optimization Process ```mermaid graph TD - A[User/Application] --> B{Initiate Search} - B --> C[Send Search Request with Query] - C --> D[Generate Query Embedding via LLM] - D --> E[Semantic Search in Qdrant DB] - E --> F[Apply Metadata Filters] - F --> G[Return Ranked Matching Memories] - G --> A + A[User: Initiate Optimization] --> B[OptimizationEngine: Analyze Collection] + B --> C[LLMClient: Assess Similarity] + C --> D[Generate Plan: Merge/Delete] + D --> E[Preview & Confirm] + E --> F{Execute?} + F -->|Yes| G[MemoryManager: Apply Changes] + F -->|No| H[Cancel] + G --> I[Log Actions] + I --> J[Report Results] ``` -**Sequence Flow**: -1. Client sends `POST /memories/search` with query -2. Query embedded using same LLM model -3. Qdrant performs approximate nearest neighbor (ANN) search -4. Results filtered by metadata (e.g., `type=belief`, `timestamp > 2024`) -5. Ranked list returned with relevance scores - ---- +### Technical Processing Workflows -#### Memory Optimization Process +#### System Initialization Workflow ```mermaid -flowchart TD - Start[Start Optimization] --> Detector[Detect Issues] - Detector -->|Duplicates| Deduplicator[DeduplicationDetector] - Detector -->|Low Quality| QualityChecker[QualityAssessment] - Detector -->|Outdated| TimeAnalyzer[TimeDecayAnalysis] - Detector -->|Classification| Classifier[ClassificationValidator] - - Detector --> Planner[Generate Optimization Plan] - Planner --> Analyzer[Analyze Impact & Risk] - Analyzer --> User[Review Recommendations?] - User -->|Approve| Executor[Execute Actions] - Executor --> Reporter[Generate Results Report] - Reporter --> End[Return Summary] +graph TD + A[Load config.toml] --> B[Initialize Tracing] + B --> C[Auto-detect LLM Client] + C --> D[Auto-detect Vector Store] + D --> E[Determine Embedding Dim] + E --> F[Create MemoryManager] + F --> G[Start CLI] + F --> H[Start HTTP API] + F --> I[Start MCP Server] + F --> J[Start Web Dashboard] ``` -**Key Stages**: -1. **Detection**: Scan memories for duplicates, low importance, outdated content -2. **Planning**: Propose merge/delete/enhance actions -3. **Approval**: Optional human-in-the-loop review -4. **Execution**: Apply changes via `MemoryUpdater` -5. **Reporting**: Return summary of actions taken +### Data Flow Paths ---- +- **Ingress**: Raw text from user/agent → Interface → Core +- **Processing**: Text → Embedding → Structured data → Metadata enrichment +- **Egress**: Search results, optimization reports, logs +- **Storage**: Vector + metadata → Qdrant → Indexed for search ### Exception Handling Mechanisms -| Failure Type | Handling Strategy | -|-------------|-------------------| -| **LLM Timeout** | Retry with exponential backoff (3 attempts) | -| **Qdrant Unavailable** | Return 503, log error, retry on next operation | -| **Invalid Request** | Return 400 with validation details | -| **Duplicate Memory** | Auto-merge or return conflict (configurable) | -| **Optimization Risk High** | Block execution unless override flag set | - -> ⚠️ **Improvement Suggestion**: Add circuit breaker pattern for external dependencies and implement distributed tracing. +- **LLM Timeout**: Retry with exponential backoff; fallback to cached embeddings if available +- **Qdrant Unavailable**: Queue operations (in-memory buffer); retry on reconnect +- **Invalid Configuration**: Fail fast at startup; provide detailed error messages +- **Memory Corruption**: Validate checksums; isolate and flag corrupted entries +- **Optimization Safety**: Preview mode required; all destructive actions require confirmation --- @@ -443,113 +368,75 @@ flowchart TD ### Core Module Implementation -#### `memory/manager.rs` – Orchestration Hub +#### MemoryManager (Rust) +- **Location**: `cortex-mem-core/src/memory/manager.rs` +- **Key Traits**: + ```rust + trait MemoryStorage { + fn add(&self, memory: Memory) -> Result; + fn search(&self, query: &str, filters: Filters) -> Result>; + fn update(&self, id: Id, patch: MemoryPatch) -> Result<()>; + } + ``` -- Single entry point for all memory operations -- Composes extractors, evaluators, and updaters -- Thread-safe via `Arc>` or async equivalents +#### LLMClient (Rust) +- Uses OpenAI embeddings API (`text-embedding-3-small`) +- Implements structured extraction via JSON-mode prompting +- Caches embeddings to reduce API cost -```rust -impl MemoryManager { - pub async fn create_memory(&self, content: String, metadata: Metadata) -> Result { - let embedding = self.llm_client.embed(&content).await?; - let keywords = self.llm_client.extract_keywords(&content).await?; - let memory_type = self.memory_classifier.classify(&content).await?; - let importance = self.importance_evaluator.score(&content).await?; - - let memory = Memory::new(content, embedding, metadata) - .with_keywords(keywords) - .with_type(memory_type) - .with_importance(importance); - - self.vector_store.upsert(&memory).await?; - Ok(memory) - } -} -``` - ---- +#### OptimizationEngine +- **Duplicate Detection**: Uses cosine similarity + LLM semantic comparison +- **Quality Scoring**: Based on length, coherence, relevance, and recency +- **Merge Strategy**: Combines overlapping memories using LLM summarization ### Key Algorithm Design -#### Hybrid Deduplication Algorithm - -```rust -fn detect_duplicate(&self, new_memory: &Memory, existing: &[Memory]) -> Option { - // Step 1: Fast rule-based filter (same source + similar timestamp) - let candidates = rule_filter(new_memory, existing); - - // Step 2: Semantic similarity (vector distance < threshold) - let similar = semantic_filter(new_memory.embedding, &candidates); - - // Step 3: LLM validation (are they truly duplicates?) - if let Some(primary) = similar.first() { - if self.llm_client.confirm_duplicate(new_memory, primary).await? { - return Some(primary.id); - } - } - None -} +#### Semantic Search Algorithm +1. Normalize query text +2. Generate embedding via LLM +3. Query Qdrant with `with_payload=true`, `with_vectors=false` +4. Apply metadata filters (date, type, source) +5. Re-rank using hybrid scoring (similarity + quality score) + +#### Optimization Plan Generation +```python +def generate_optimization_plan(memories): + clusters = cluster_by_similarity(memories, threshold=0.92) + plan = [] + for cluster in clusters: + if len(cluster) > 1: + primary = select_highest_quality(cluster) + for mem in cluster: + if mem != primary: + plan.append(MergeAction(primary.id, mem.id)) + return plan ``` -> ✅ **Accuracy**: Combines speed (rules) with precision (LLM) - ---- - ### Data Structure Design -#### Memory Object (Rust) - ```rust #[derive(Serialize, Deserialize)] pub struct Memory { pub id: Uuid, pub content: String, pub embedding: Vec, - pub metadata: HashMap, + pub memory_type: MemoryType, // Conversational, Factual, Procedural + pub timestamp: DateTime, + pub source: String, // Agent ID or User ID + pub quality_score: f32, + pub facts: Vec, + pub entities: Vec, pub keywords: Vec, - pub memory_type: MemoryType, - pub importance: f32, - pub created_at: DateTime, - pub updated_at: DateTime, - pub version: u32, } ``` -#### Configuration Model (`config.toml`) - -```toml -[server] -host = "127.0.0.1" -port = 8080 - -[qdrant] -url = "http://localhost:6334" -collection = "memories" - -[llm] -provider = "openai" -model = "text-embedding-3-small" -api_key = "sk-..." - -[optimization] -deduplication_threshold = 0.92 -importance_decay_rate = 0.01 -``` - ---- - ### Performance Optimization Strategies -| Strategy | Implementation | -|--------|----------------| -| **Embedding Caching** | Cache recent embeddings by content hash (planned) | -| **Batch Processing** | Support bulk insert/search operations | -| **Async I/O** | All LLM and DB calls are non-blocking | -| **Indexing** | Qdrant uses HNSW index for fast ANN search | -| **Connection Pooling** | Reuse HTTP connections to LLM and Qdrant | - -> 🚀 **Future Enhancement**: Add Redis cache layer for frequent queries. +- **Embedding Caching**: Cache embeddings by content hash to avoid redundant LLM calls +- **Batch Processing**: Support bulk insert/search operations +- **Index Partitioning**: Split collections by agent or time window +- **Asynchronous Logging**: Non-blocking trace and metric emission +- **Connection Pooling**: Reuse HTTP and gRPC connections to Qdrant/OpenAI --- @@ -557,101 +444,145 @@ importance_decay_rate = 0.01 ### Runtime Environment Requirements -| Component | CPU | Memory | Disk | Network | -|--------|------|--------|------|---------| -| `cortex-mem-core` | 2 vCPU | 4 GB | 1 GB (temp) | Outbound to LLM/Qdrant | -| `cortex-mem-service` | 1 vCPU | 1 GB | - | Inbound HTTP | -| `cortex-mem-insights` | 1 vCPU | 2 GB | - | Inbound HTTP | -| Qdrant | 4 vCPU | 8 GB | SSD | Internal gRPC | -| LLM (external) | N/A | N/A | N/A | Public API | - ---- +| Component | Language | Runtime | Memory | CPU | +|--------|---------|--------|-------|-----| +| Core Engine | Rust | Native binary | 512MB+ | 1 vCPU | +| HTTP API | Rust | Native binary | 256MB+ | 0.5 vCPU | +| Web Dashboard | Node.js + Svelte | Node 18+ | 256MB | 0.5 vCPU | +| Qdrant | Rust | Docker | 2GB+ | 2 vCPU | +| OpenAI | Cloud | N/A | N/A | N/A | ### Deployment Topology Structure ```mermaid -graph LR - subgraph "Cloud / On-Prem" - LB[Load Balancer] - subgraph "Application Layer" - API[cortex-mem-service\nReplica 1-3] - CLI[CLI Tools] - end - subgraph "Core Layer" - CORE[cortex-mem-core\nShared Engine] - end - subgraph "Data Layer" - QDRANT[Qdrant Cluster] - CACHE[(Redis Cache)\nOptional] - end - subgraph "External" - LLM[LLM API\n(OpenAI, etc.)] - BROWSER[Web Browser] - AGENT[AI Agent] +graph TD + subgraph Cloud [Cloud Environment] + subgraph VPC [VPC: cortex-mem-vpc] + subgraph Compute [Compute Layer] + CLI[CLI - Local or CI] + API[cortex-mem-service] + MCP[cortex-mem-mcp] + WD[cortex-mem-insights] + end + + subgraph Data [Data Layer] + Qdrant[Qdrant Cluster] + PG[PostgreSQL - Optional Metadata] + end + + subgraph External [External Services] + OpenAI[OpenAI API] + LangMem[LangMem Benchmark] + end end end - BROWSER --> LB --> API --> CORE - CLI --> CORE - AGENT --> API - CORE --> QDRANT - CORE --> CACHE - CORE --> LLM - - style CORE fill:#4CAF50,stroke:#388E3C,color:white - style API fill:#2196F3,stroke:#1976D2,color:white - style QDRANT fill:#4CAF50,stroke:#388E3C,color:white - style LLM fill:#FFC107,stroke:#FFA000,color:black - style CACHE fill:#9C27B0,stroke:#7B1FA2,color:white + CLI --> API + API --> Qdrant + API --> OpenAI + MCP --> API + WD --> API + WD --> Qdrant + + style VPC fill:#E3F2FD,stroke:#2196F3 + style Compute fill:#BBDEFB,stroke:#1976D2 + style Data fill:#E1BEE7,stroke:#9C27B0 ``` +### Scalability Design + +- **Horizontal Scaling**: HTTP API and MCP services can be deployed in multiple instances behind a load balancer +- **Vertical Scaling**: Qdrant can scale memory and CPU for larger embedding dimensions or collections +- **Caching Layer**: Optional Redis cache for frequent queries and embeddings +- **Queue-Based Processing**: Long-running optimization jobs can be offloaded to a message queue (e.g., RabbitMQ) + +### Monitoring and Operations + +- **Metrics**: Prometheus for latency, request rate, error rate +- **Tracing**: OpenTelemetry + Jaeger for request flow visibility +- **Logging**: Structured JSON logs with severity levels +- **Alerting**: Grafana alerts on high error rates or slow LLM responses +- **Health Checks**: `/health` endpoint for liveness and readiness +- **Backup**: Regular snapshots of Qdrant collections + --- +## Architecture Insights + ### Scalability Design -| Aspect | Strategy | -|------|----------| -| **Horizontal Scaling** | Multiple `cortex-mem-service` instances behind load balancer | -| **Vertical Scaling** | Scale Qdrant and core engine with memory/CPU | -| **Data Partitioning** | Future support for sharded collections by agent ID | -| **Streaming Support** | Planned for large search results and long-running optimizations | +- **Extension Points**: + - New interfaces (e.g., WebSocket, gRPC) can be added without modifying core + - Additional vector databases (Pinecone, Weaviate) can be integrated via adapter pattern + - Custom optimization strategies can be plugged in via trait implementation ---- +- **Future-Proofing**: + - Support for multiple LLM providers (Anthropic, Cohere) via abstraction layer + - Federated memory across agents using sharding -### Monitoring and Operations +### Performance Considerations + +- **Bottlenecks**: + - LLM API latency (mitigated by caching and batching) + - Vector search performance at scale (mitigated by HNSW indexing) + - Memory classification overhead (mitigated by lightweight models) -#### Current Capabilities -- Health checks (`GET /status`) -- Logging via `tracing` crate -- Configuration reload (TOML) - -#### Recommended Enhancements -| Tool | Purpose | -|------|--------| -| **Prometheus + Grafana** | Metrics: request rate, latency, memory count | -| **OpenTelemetry** | Distributed tracing across components | -| **Sentry** | Error tracking and alerting | -| **Log Aggregation** | ELK or Loki for centralized logs | - -> 🔐 **Security Recommendations**: -> - Add JWT authentication for API -> - Rate limiting on public endpoints -> - Encrypt sensitive config (e.g., API keys) -> - Role-based access control (RBAC) for dashboard +- **Optimization Roadmap**: + - On-premise embedding models (e.g., BERT, Sentence Transformers) + - Incremental optimization (vs. full scans) + - In-memory vector cache for hot queries + +### Security Design + +- **Authentication**: API keys for HTTP/MCP interfaces +- **Authorization**: Role-based access (read/write/optimization) +- **Data Encryption**: TLS for all external communications +- **Secrets Management**: API keys stored in environment variables or secret manager +- **Input Sanitization**: All user inputs validated and sanitized --- -## Conclusion +## Development & Operations Guidance + +### Development Guidance + +- **Contribution Workflow**: + 1. Modify `cortex-mem-core` for business logic + 2. Update interfaces as needed + 3. Add tests in `tests/` directory + 4. Run `cargo test --all-features` -The **cortex-mem** architecture presents a **robust, modular, and intelligent** solution for AI agent memory management. Its **Rust-based core** ensures performance and safety, while **multiple access interfaces** (REST, CLI, MCP, Dashboard) provide flexibility for diverse users. +- **Best Practices**: + - Keep core logic in `cortex-mem-core` + - Use configuration for environment-specific settings + - Log all LLM and DB interactions for debugging -The system effectively combines **vector storage (Qdrant)** with **LLM-powered intelligence** to enable semantic search, automatic metadata enrichment, and proactive optimization. The clear separation of domains and components makes it **easy to extend, maintain, and scale**. +### Operations Guidance -### Final Recommendations -1. **Add Caching Layer** (Redis) to reduce LLM/vector DB load -2. **Implement Authentication** for production use -3. **Enhance Observability** with metrics and tracing -4. **Expand Testing** with unit/integration tests -5. **Support More Vector Stores** (Chroma, Weaviate) via trait implementation +- **Deployment**: + - Use Docker Compose for local development + - Use Kubernetes for production (with HPA and monitoring) + +- **Maintenance**: + - Schedule nightly optimization jobs + - Monitor embedding API usage and costs + - Backup Qdrant collections weekly + +### Decision Support + +- **Why Rust?** Performance, safety, and concurrency for core logic +- **Why Qdrant?** Open-source, high-performance vector DB with rich filtering +- **Why Multiple Interfaces?** Supports diverse integration scenarios +- **Why Centralized Core?** Ensures consistency and reduces duplication + +### Knowledge Transfer + +New team members should: +1. Read `config.toml` to understand system settings +2. Study `cortex-mem-core/src/memory/manager.rs` for core logic +3. Run CLI commands to see end-to-end flow +4. Explore web dashboard for monitoring capabilities + +--- -This architecture is well-positioned to serve as a **foundational memory layer** in next-generation AI agent systems. \ No newline at end of file +**End of Document** \ No newline at end of file diff --git a/litho.docs/3.Workflow.md b/litho.docs/3.Workflow.md index c0337c1..5ddaa69 100644 --- a/litho.docs/3.Workflow.md +++ b/litho.docs/3.Workflow.md @@ -1,226 +1,189 @@ # Core Workflows -**Document Generated On:** 2025-12-18 11:29:14 UTC -**System Name:** `cortex-mem` – A Comprehensive Memory Management System for AI Agents - ---- - ## 1. Workflow Overview -The **cortex-mem** system is a full-stack memory management platform designed to enable AI agents with persistent, searchable, and optimizable long-term memory. It supports multiple interaction modes—via HTTP API, CLI, MCP protocol, and interactive TUI applications—and provides advanced capabilities such as semantic search, deduplication, quality optimization, and analytics. +The **Cortex-Mem** system is a comprehensive memory management platform designed to enable AI agents and human operators to store, retrieve, analyze, and optimize persistent memories across multiple interaction modalities. The system supports diverse access patterns through CLI, HTTP API, MCP (Memory Control Protocol), and a web-based dashboard, all unified under a shared core logic layer. -### Main Workflows -The system revolves around three core workflows: -1. **Memory Management Process (CRUD)** – Creation, retrieval, update, and deletion of memories. -2. **Memory Search Process** – Semantic and metadata-based querying of stored memories. -3. **Memory Optimization Process** – Quality improvement through deduplication, relevance tuning, and structural refinement. - -These workflows are orchestrated across modular domains including **Memory Management**, **Memory Storage**, **Optimization**, and **Insights & Analytics**, all coordinated via shared state, configuration, and asynchronous execution. +### System Main Workflows +- **Memory Management Workflow**: End-to-end lifecycle of creating, storing, retrieving, and optimizing memories. +- **System Initialization Workflow**: Bootstrapping process that loads configuration, initializes services, and exposes interfaces. +- **Optimization Execution Workflow**: Intelligent analysis and improvement of memory collections for quality, relevance, and efficiency. ### Core Execution Paths -| Workflow | Entry Point(s) | Key Output | -|--------|----------------|-----------| -| Memory Management | `/memories` (API), `add`, `delete` (CLI) | Memory ID or operation status | -| Memory Search | `/search`, `list`, `search` commands | Ranked list of matching memories | -| Memory Optimization | `/optimization/start`, `optimize` command | Optimization report with impact metrics | +1. **User-initiated Memory Operations** (via CLI or Web UI) +2. **Agent-initiated Memory Access** (via MCP or API) +3. **Automated Optimization & Maintenance** +4. **Monitoring & Diagnostic Queries** ### Key Process Nodes -- **Request Initiation**: User or agent triggers an action via CLI, API, or UI. -- **Embedding Generation**: LLM client generates vector embeddings from text content. -- **Vector Storage/Retrieval**: Qdrant database stores and retrieves embeddings using approximate nearest neighbor (ANN) search. -- **Filtering & Scoring**: Metadata filters applied post-semantic search; results ranked by relevance score. -- **Optimization Orchestration**: Detection of duplicates, low-quality entries, and relevance issues followed by LLM-driven cleanup. -- **Response Delivery**: Structured response returned to client with success/failure status and data. +| Node | Description | +|------|-------------| +| `Configuration Load` | Parses TOML config with fallback paths; sets up logging, LLM, vector DB, and service parameters | +| `MemoryManager Init` | Central orchestrator that binds storage, LLM, and optimization logic | +| `Content Analysis` | Uses LLM to extract facts, entities, keywords, and classify memory type | +| `Embedding Generation` | Converts text into vector representations using OpenAI or compatible models | +| `Vector Storage` | Persists embeddings in Qdrant with metadata indexing | +| `Semantic Search` | Matches queries via cosine similarity on vectors | +| `Optimization Engine` | Detects duplicates, low-quality entries, and suggests merging strategies | +| `State Reporting` | Aggregates health metrics from backend services for monitoring | ### Process Coordination Mechanisms -- **Asynchronous Communication**: All operations use async/await patterns via Tokio runtime for non-blocking I/O. -- **Shared State via Arc/Mutex**: In CLI and TUI apps, `Arc` enables thread-safe access. -- **Configuration-Driven Initialization**: TOML-based config (`config.toml`) controls subsystem behavior (LLM, Qdrant, logging). -- **Event Loop Architecture**: Interactive tools like `cortex-mem-tars` use message-passing loops for input handling and rendering. - -```mermaid -graph TD - subgraph "User Interfaces" - A[HTTP Client] --> C[cortex-mem-service] - B[Terminal User] --> D[cortex-mem-cli] - E[AI Agent] --> F[cortex-mem-mcp] - G[TUI Application] --> H[cortex-mem-tars] - end - - C --> I[cortex-mem-core] - D --> I - F --> I - H --> I - - subgraph "Core Engine" - I --> J[LLM Client] - I --> K[Qdrant Vector Store] - end - - I --> L[cortex-mem-insights] - L --> M[Dashboard UI] - - style I fill:#4a90e2,color:white - style J fill:#50c878,color:white - style K fill:#d64161,color:white -``` - -> **Figure 1: High-Level System Architecture and Workflow Coordination** +- **Shared State via `Arc`**: Ensures thread-safe access across async handlers in Rust components. +- **Event-Driven Communication**: Message passing between UI and background tasks using channels (`mpsc::UnboundedSender`) in TUI applications. +- **API Abstraction Layer**: TypeScript client encapsulates REST interactions, enabling consistent frontend-backend communication. +- **Reactive Stores (Svelte)**: Frontend state managed via writable/derived stores for real-time updates. +- **Tracing Integration**: Unified logging across modules using `tracing` crate for observability. --- ## 2. Main Workflows -### 2.1 Memory Management Process +### 2.1 Memory Management Workflow -This is the foundational CRUD workflow enabling creation, reading, updating, and deletion of memory records. +This is the primary business workflow, governing how memories are created, stored, retrieved, and optimized. -#### Execution Path ```mermaid -sequenceDiagram - participant U as User/Application - participant CLI as cortex-mem-cli - participant API as cortex-mem-service - participant MM as MemoryManager - participant LLM as LLM Client - participant VS as Qdrant Vector Store - - U->>CLI: add --content "Hello world" - CLI->>MM: create_memory(content) - MM->>LLM: generate_embedding(content) - LLM-->>MM: embedding vector - MM->>VS: upsert(memory + embedding) - VS-->>MM: success - MM-->>CLI: memory_id - CLI-->>U: Created memory ID: abc123 +graph TD + A[User/Agent Request] --> B{Operation Type} + B -->|Create| C[Content Analysis & Classification] + B -->|Search| D[Generate Query Embedding] + B -->|Optimize| E[Analyze Memory Collection] + + C --> F[Extract Facts, Entities, Keywords] + F --> G[Generate Memory Embedding] + G --> H[Store in Qdrant DB] + + D --> I[Vector Similarity Search] + I --> J[Apply Metadata Filters] + J --> K[Return Ranked Results] + + E --> L[Detect Duplicates & Issues] + L --> M[Generate Optimization Plan] + M --> N[Execute Merge/Delete Operations] + + H --> P((Persistent Storage)) + K --> Q[Display Results] + N --> R[Update Memory State] + + style A fill:#4CAF50,stroke:#388E3C + style Q fill:#2196F3,stroke:#1976D2 + style P fill:#FF9800,stroke:#F57C00 ``` -#### Detailed Steps -| Step | Component | Operation | Input | Output | -|------|---------|----------|-------|--------| -| 1 | `cortex-mem-cli/src/main.rs` | Parse CLI args, dispatch to `add` command | Command-line arguments | `AddCommand` struct | -| 2 | `cortex-mem-cli/src/commands/add.rs` | Extract content, detect conversation format | Raw string input | Parsed `MemoryContent` | -| 3 | `cortex-mem-core/src/memory/manager.rs` | Call `create_memory()` | Content, user/agent IDs | Memory object | -| 4 | `cortex-mem-core/src/llm/client.rs` | Generate embedding via LLM API | Text content | `[f32; 384]` vector | -| 5 | `cortex-mem-core/src/vector_store/qdrant.rs` | Upsert into Qdrant collection | Memory + embedding | Success/Failure | -| 6 | `cortex-mem-cli/src/commands/add.rs` | Print result to console | Memory ID | Human-readable output | - -#### Business Value -- Enables AI agents to persist context across sessions. -- Supports structured metadata tagging (topics, keywords, type). -- Provides reliable storage with unique identifiers for future reference. - -#### Technical Characteristics -- **Idempotent Operations**: Duplicate inserts return existing ID if detected. -- **Batch Support**: Bulk operations supported via `/batch` endpoints. -- **Async I/O**: All steps executed asynchronously using Tokio. +#### Execution Order and Dependencies +1. **Input Reception** → Command parsing (CLI) or route dispatch (HTTP/MCP) +2. **Preprocessing** → Content classification, role detection (e.g., "User:", "Assistant:") +3. **LLM Interaction** → Fact extraction, embedding generation +4. **Storage Operation** → Vector write to Qdrant + metadata persistence +5. **Indexing & Retrieval** → Semantic search using vector similarity +6. **Post-processing** → Filtering by user_id, agent_id, topics, etc. +7. **Output Formatting** → Structured console output or JSON response + +#### Input/Output Data Flows +| Step | Input | Output | Source Module | Target Module | +|------|-------|--------|---------------|----------------| +| Create | Raw text/conversation | Classified content | CLI/API | Memory Types System | +| Extract | Text content | Facts, entities, keywords | Information Extraction | Memory CRUD | +| Embed | Text | Vector embedding | LLM Client | Vector Store | +| Store | Memory object | Stored ID | Memory Manager | Qdrant | +| Search | Query string/filters | List of scored memories | Semantic Search | Access Interface | +| Optimize | Strategy + filters | Optimization plan | Optimization Engine | Memory Manager | + +> ✅ **Business Value**: Enables contextual continuity in AI agents by preserving conversational history and derived insights. --- -### 2.2 Memory Search Process +### 2.2 System Initialization Workflow -Enables users and agents to retrieve relevant memories based on semantic similarity or metadata filtering. +Handles bootstrapping of the entire system across all entry points. -#### Execution Path ```mermaid graph TD - A[User/App] --> B{Initiate Search} - B --> C[Send Query + Filters] - C --> D[Generate Embedding via LLM] - D --> E[Semantic Search in Qdrant] - E --> F[Apply Metadata Filters] - F --> G[Rank & Return Results] - G --> A + A[Load Configuration] --> B[Initialize Tracing & Logging] + B --> C{Auto-detect Components} + C --> D[Detect Vector Store] + C --> E[Detect LLM Client] + C --> F[Determine Embedding Dimension] + + D --> G[Create MemoryManager] + E --> G + F --> G + + G --> H[Expose Interfaces] + H --> I[CLI Interface] + H --> J[HTTP API Service] + H --> K[MCP Server] + H --> L[Web Dashboard] + + style A fill:#4CAF50,stroke:#388E3C + style I fill:#2196F3,stroke:#1976D2 + style J fill:#2196F3,stroke:#1976D2 + style K fill:#2196F3,stroke:#1976D2 + style L fill:#2196F3,stroke:#1976D2 ``` -#### Detailed Steps -| Step | Module | Function | Description | -|------|--------|--------|-------------| -| 1 | `handlers.rs::search_memories` | Receive `/search` request | Accepts query string and optional filters (user_id, agent_id, topics) | -| 2 | `llm/client.rs::generate_embedding` | Encode query into vector | Uses configured LLM (e.g., OpenAI, Ollama) to embed query | -| 3 | `qdrant.rs::semantic_search` | Perform ANN search | Retrieve top-K nearest neighbors above threshold | -| 4 | `memory/manager.rs::apply_filters` | Filter by metadata | Apply user, agent, topic, keyword constraints | -| 5 | `memory/manager.rs::rank_results` | Re-rank by composite score | Combine semantic score with importance, recency, relevance | -| 6 | `handlers.rs` | Return JSON response | Paginated list with scores, metadata, and snippets | - -#### Supported Query Modes -- **Semantic Only**: Free-text search ("What did I say about planning?") -- **Metadata Filtered**: List all memories for a given agent/topic -- **Hybrid**: "Find conversations about goals" → semantic + filter on `type=conversation` - -#### Performance Notes -- Default limit: 20 results -- Configurable scoring weights: `relevance_weight`, `importance_weight`, `recency_decay` -- Caching layer planned but not yet implemented +#### Technical Details +- **Config Loading Path Resolution**: + - Current directory → Home directory → System-wide locations + - Supports default values via `Default` trait (e.g., log level = INFO) +- **Auto-Detection Logic**: + - If embedding dimension not specified, infers from LLM model or tests dynamically + - Falls back to common dimensions (e.g., 1536 for OpenAI) +- **Service Binding**: + - All interfaces share the same `MemoryManager` instance wrapped in `Arc>` + - Tokio runtime manages concurrent access + +> ⚙️ **Operational Insight**: This modular initialization enables deployment flexibility—developers can run only needed interfaces (e.g., just MCP server). --- -### 2.3 Memory Optimization Process +### 2.3 Optimization Execution Workflow -A high-value workflow that improves memory quality by detecting and resolving issues such as duplication, redundancy, and poor structure. +Enables intelligent cleanup and enhancement of memory collections. -#### Execution Path ```mermaid -sequenceDiagram - participant U as User - participant O as Optimizer CLI - participant OD as OptimizationDetector - participant OP as OptimizerEngine - participant MM as MemoryManager - participant LLM as LLM Service - - U->>O: optimize --strategy deduplicate --preview - O->>OD: detect_issues(filters) - OD->>MM: list_all_memories() - MM-->>OD: memory batch - OD->>LLM: analyze_for_duplicates(batch) - LLM-->>OD: issue report - OD-->>O: preview plan - U->>O: confirm execution - O->>OP: execute_plan(plan) - OP->>LLM: rewrite/refactor memories - OP->>MM: update/delete affected memories - OP-->>O: optimization_report - O-->>U: Display summary and impact +graph TD + A[User Initiates Optimization] --> B[Select Strategy & Filters] + B --> C[Analyze Memory Collection] + C --> D[Identify Duplicates & Issues] + D --> E[Use LLM for Similarity Assessment] + E --> F[Generate Optimization Plan] + F --> G[Preview Changes & Confirm] + G --> H{Execute?} + H -->|Yes| I[Apply Changes via MemoryManager] + H -->|No| J[Cancel] + + I --> K[Merge Related Memories] + I --> L[Delete Redundant Entries] + I --> M[Update Memory Metadata] + + K --> N[Log Actions & Metrics] + L --> N + M --> N + + N --> O[Report Results & Statistics] + O --> P[Display Optimization Summary] + + style A fill:#4CAF50,stroke:#388E3C + style P fill:#2196F3,stroke:#1976D2 ``` -#### Key Subprocesses -1. **Issue Detection** - - Scan memories for: - - Exact or near-duplicate content - - Low-importance scores (< 0.3) - - Poorly structured entries - - Redundant information across related memories - - Implemented in `optimization_detector.rs` - -2. **Plan Generation** - - Build safe transformation plan: - - Merge duplicates - - Rewrite unclear entries - - Archive obsolete memories - - Preview mode shows proposed changes before execution - -3. **Execution Engine** - - Apply transformations using LLM rewriting prompts - - Batch updates to minimize API calls - - Track version history for rollback capability - -4. **Reporting** - - Generate JSON and Markdown reports - - Include statistics: # resolved, # deleted, # updated, estimated quality gain - -#### Configuration Options -| Parameter | Default | Purpose | -|---------|--------|--------| -| `--strategy` | `full` | One of: `deduplicate`, `quality`, `relevance`, `full` | -| `--filter-user` | all | Restrict scope to specific user | -| `--dry-run` | false | Show what would be done without applying changes | -| `--aggressive` | false | Enable deeper restructuring (riskier) | - -#### Business Value -- Prevents memory bloat and degradation over time. -- Maintains high signal-to-noise ratio in agent knowledge base. -- Enables trustworthiness of retrieved context. +#### Supported Strategies +| Strategy | Purpose | +|--------|---------| +| Full | Comprehensive scan and optimization | +| Incremental | Only recent memories | +| Deduplication | Find and merge near-duplicate entries | +| Quality | Remove low-importance or irrelevant memories | +| Relevance | Filter based on topic alignment | +| Space | Maximize storage efficiency | + +#### Preview Mode Safety +- Shows estimated impact before execution +- Requires explicit confirmation +- Supports dry-run analysis without modification + +> 🔐 **Security Note**: Interactive confirmation prevents accidental data loss during bulk operations. --- @@ -228,107 +191,82 @@ sequenceDiagram ### 3.1 Multi-Module Coordination Mechanisms -The system uses a layered architecture where higher-level interfaces delegate to a unified core engine. +| Relationship | Mechanism | Example | +|------------|-----------|--------| +| **Access Interface → Memory Management** | Direct function call via `MemoryManager` | CLI calls `.add_memory()` | +| **Memory Management → LLM Integration** | Async method invocation | `llm_client.generate_embedding()` | +| **Memory Management → Storage Integration** | Vector store abstraction | `QdrantStore.store_embedding()` | +| **Frontend → Backend** | RESTful API over HTTP | `/api/memory/search` | +| **Agent → System** | MCP protocol over stdio | `store_memory`, `query_memory` tools | -#### Inter-Module Dependencies +#### Inter-Process Communication Diagram ```mermaid -graph LR - CLI[cortex-mem-cli] --> CORE[cortex-mem-core] - API[cortex-mem-service] --> CORE - MCP[cortex-mem-mcp] --> CORE - EVAL[cortex-mem-evaluation] --> CORE - INSIGHTS[cortex-mem-insights] --> API - CORE --> LLM[LLM Client] - CORE --> VEC[Qdrant] - - classDef domain fill:#e0f7fa,stroke:#0097a7; - class CLI,API,MCP,EVAL,INSIGHTS domain; -``` - -> **Figure 2: Module Interaction Diagram** - -Each interface module acts as a facade: -- **CLI**: Terminal-first UX with rich formatting and interactivity -- **API**: RESTful interface for integration with external systems -- **MCP**: Protocol adapter for AI tool calling (e.g., LangChain, AutoGPT) -- **Insights**: Visualization dashboard built on top of API +sequenceDiagram + participant User + participant CLI as cortex-mem-cli + participant API as cortex-mem-service + participant MCP as cortex-mem-mcp + participant MM as MemoryManager + participant LLM as LLM Client + participant VS as Qdrant Vector Store -All route through `MemoryManager`, ensuring consistent business logic enforcement. + User->>CLI: add --content "Hello" + CLI->>MM: add_memory(content) + MM->>LLM: generate_embedding(text) + LLM-->>MM: embedding vector + MM->>VS: insert(embedding, metadata) + VS-->>MM: success + MM-->>CLI: memory ID + CLI-->>User: "Memory added: id=abc123" + + User->>API: POST /api/memory/search {query:"greeting"} + API->>MM: search(query) + MM->>LLM: generate_embedding(query) + MM->>VS: query_by_vector(embedding) + VS-->>MM: list of IDs + MM->>VS: get_metadata_batch(IDs) + MM-->>API: ranked results + API-->>User: JSON response +``` ### 3.2 State Management and Synchronization -#### Shared State Patterns -| Context | Mechanism | Example | -|-------|----------|--------| -| CLI App | `Arc` | Thread-safe sharing between event loop and background tasks | -| TUI App | Message Passing (`mpsc::UnboundedSender`) | Decouple UI rendering from memory persistence | -| Web Server | `AppState` with `Arc>` | Share manager across Axum handlers | -| Frontend | Svelte Stores (`writable`, `derived`) | Reactive state for dashboards | - -#### Lifecycle Coordination -In `cortex-mem-tars`, graceful shutdown ensures: -1. Exit TUI immediately on `/quit` -2. Continue processing pending memory saves in background -3. Log final session summary after persistence completes - -Implemented via Tokio task spawning and join handles. +#### Backend (Rust) +- **Thread Safety**: Shared state protected via `Arc>` or `Arc>` +- **Async Runtime**: Tokio handles concurrency with non-blocking I/O +- **Global Config**: Immutable after load, cloned where needed + +#### Frontend (Svelte) +- **Reactive Stores**: + - `memoryStore`: Holds list of memories with filters + - `optimizationStore`: Tracks job status and history + - `systemStore`: Monitors backend health +- **Derived Stores**: + ```ts + export const optimizationStatus = derived( + optimizationStore, + ($store) => $store.job?.status || 'idle' + ); + ``` ### 3.3 Data Passing and Sharing -#### Internal Data Flow -```mermaid -flowchart LR - Input --> Normalization --> Embedding --> Storage --> Indexing --> Queryable -``` - -Data transformations include: -- UTF-8 normalization and truncation (max 16k tokens) -- Automatic topic extraction from content -- Importance scoring via LLM classification -- Vector indexing within Qdrant - -#### Cross-Layer Contracts -Defined via: -- Rust structs (`MemoryRecord`, `Filters`, `OptimizationPlan`) -- TypeScript interfaces (`ApiMemory`, `OptimizationJobStatus`) -- Serde serialization for inter-process communication - -Ensures consistency between CLI, API, and frontend layers. +| Layer | Mechanism | Format | +|------|----------|--------| +| Internal (Rust) | Function arguments, structs | Native types (`Memory`, `Filters`) | +| External (API) | JSON over HTTP | Defined in `types.ts` | +| CLI ↔ Core | In-process calls | Strongly-typed Rust structs | +| Agent ↔ MCP | JSON-RPC over stdio | MCP-compliant tool schema | ### 3.4 Execution Control and Scheduling -#### Asynchronous Processing Model -All heavy operations run asynchronously: -- Embedding generation -- Vector search -- Optimization jobs -- Batch imports - -Using **Tokio** runtime with: -- Task spawning for long-running operations -- Timeouts enforced per operation (configurable) -- Backpressure via bounded channels - -#### Job Tracking -Optimization jobs tracked in-memory (future: persistent store): -- Job ID generation -- Status polling endpoint (`GET /optimization/status/:id`) -- Cancellation support via cancellation tokens - -Example job lifecycle: -```mermaid -stateDiagram-v2 - [*] --> Idle - Idle --> Analyzing: start_optimization() - Analyzing --> Executing: issues_detected - Executing --> Completed: all_applied - Executing --> Failed: error_during_apply - Analyzing --> Cancelled: cancel_request - Executing --> Cancelled: cancel_request - Failed --> [*] - Completed --> [*] - Cancelled --> [*] -``` +- **Manual Triggering**: User commands via CLI or Web UI +- **Scheduled Jobs**: Planned via external schedulers (not built-in yet) +- **Concurrent Processing**: + - Multiple CLI/API requests handled concurrently via Tokio + - Batch operations use rate limiting (1s delay between batches) + +> 🔄 **Performance Tip**: Use batch operations for large-scale imports to reduce overhead. --- @@ -336,66 +274,54 @@ stateDiagram-v2 ### 4.1 Error Detection and Handling -#### Common Failure Points -| Layer | Potential Errors | Handling Strategy | -|------|------------------|-------------------| -| CLI Parsing | Invalid args | Clap validation + help display | -| LLM Call | Rate limiting, timeout, auth failure | Retry with exponential backoff; fallback to cached embedding if available | -| Qdrant DB | Connection loss, index corruption | Graceful degradation; log warning, retry on next attempt | -| Memory Write | Duplicate key, schema mismatch | Idempotent insert; deduplicate before write | -| Optimization | Invalid plan, partial failure | Atomic transaction simulation; roll back failed batches | - -#### Centralized Logging -Uses `tracing_subscriber` with structured logs: -```rust -tracing::error!(error = ?e, memory_id, "Failed to delete memory"); -``` -Log levels: INFO (default), DEBUG (verbose), ERROR (critical) +| Component | Strategy | +|---------|----------| +| **CLI** | Structured tracing logs + user-friendly messages | +| **HTTP API** | Centralized error handler returns `{success: false, error: {...}}` | +| **MCP Server** | Translates domain errors to `ErrorData` per MCP spec | +| **Frontend** | Try-catch blocks with fallback rendering | + +#### Common Errors and Responses +| Error Type | Handling | +|----------|---------| +| `Memory Not Found` | Return 404; suggest alternatives if similar exist | +| `LLM Rate Limit` | Retry with exponential backoff; fall back to cached embeddings | +| `Qdrant Unavailable` | Log error; return cached results if possible | +| `Invalid Configuration` | Fail fast at startup with descriptive message | +| `JSON Parse Failure` | Graceful degradation; use defaults or skip item | ### 4.2 Exception Recovery Mechanisms -#### Resilience Features -- **Retry Logic**: For transient failures (network, rate limits) -- **Fallback Embeddings**: Use last-known-good embedding during outages -- **Safe Mode**: Disable optimization when LLM unavailable -- **Data Validation**: Validate memory integrity before and after operations - -#### Rollback Capabilities -- Memory updates preserve previous versions (soft-delete model) -- Optimization plans can be reverted using audit trail -- Backup export via `cortex-mem-cli list --format=json > backup.json` +- **Graceful Degradation**: + - When LLM fails, use basic keyword matching instead of semantic search + - On API failure, show last known good state +- **Retry Logic**: + - Python evaluation scripts include retry loops + - HTTP clients implement automatic retries (configurable) +- **Fallback Modes**: + - CLI optimization has “preview” mode to avoid unintended changes + - Web UI shows mock data when backend is unreachable ### 4.3 Fault Tolerance Strategy Design -| Risk | Mitigation | +| Aspect | Strategy | |------|----------| -| Single Point of Failure | Stateless services; Qdrant supports clustering | -| Data Loss | Regular backups encouraged; WAL enabled in Qdrant | -| LLM Outage | Cache recent embeddings; degrade to keyword-only search | -| High Load | Request throttling; queue large jobs | -| Misconfiguration | Schema validation on config load; defaults provided | +| **Data Loss Prevention** | Final shutdown phase ensures pending writes complete | +| **Service Isolation** | Each interface runs independently; one crash doesn’t affect others | +| **Idempotent Operations** | Add operations check for duplicates before insertion | +| **Backup Support** | Optimization includes optional backup creation before major changes | ### 4.4 Failure Retry and Degradation -#### Retry Policy -Configurable in `config.toml`: -```toml -[llm.retry] -max_attempts = 3 -initial_backoff_ms = 500 -max_backoff_ms = 5000 -jitter_factor = 0.1 -``` - -Applied automatically in `client.rs`. - -#### Degradation Modes -When LLM service is unreachable: -- **Search**: Fall back to metadata-only filtering -- **Add**: Skip embedding, mark as "pending embedding" -- **Optimize**: Disable LLM-dependent strategies, allow manual cleanup only +- **Rate Limit Handling**: + - Detected via HTTP 429 or LLM client feedback + - Automatically switches to lower-frequency mode + - Delays next request using jittered exponential backoff +- **Degraded Mode Features**: + - Disable advanced features (e.g., deduplication) when LLM unavailable + - Fall back to exact match search when embedding generation fails -Indicated in system status dashboard. +> 💡 **Best Practice**: Always validate configuration early using `validate_config()` utilities to prevent runtime failures. --- @@ -403,133 +329,128 @@ Indicated in system status dashboard. ### 5.1 Core Algorithm Processes -#### Embedding Pipeline +#### Memory Creation Pipeline ```rust -async fn create_memory(&self, content: String) -> Result { - let embedding = self.llm_client.generate_embedding(&content).await?; - let memory = MemoryRecord::new(content, embedding, self.user_id); - self.vector_store.upsert(memory).await?; - Ok(memory.id) +async fn create_memory(content: &str) -> Result { + let memory_type = classify_content(content); + let facts = extractor.extract_facts(content).await?; + let embedding = llm_client.embed(content).await?; + let metadata = MemoryMetadata::new(user_id, agent_id, memory_type); + + let memory = Memory::builder() + .content(content) + .embedding(embedding) + .metadata(metadata) + .facts(facts) + .build(); + + memory_manager.store(memory).await } ``` -- Uses sentence-transformers or OpenAI models -- Dimension: typically 384–1536 floats -- Distance metric: Cosine similarity - -#### Deduplication Algorithm -1. Group memories by user + agent -2. Compute pairwise cosine distances -3. Cluster similar vectors (threshold: 0.92) -4. Select representative memory (highest importance + recency) -5. Rewrite others to link or merge -Uses hierarchical agglomerative clustering (HAC). - -#### Importance Scoring -Uses few-shot prompting: +#### Optimization Decision Hierarchy ```text -Rate this memory's importance from 0.0 to 1.0: -- Is it actionable? -- Does it contain personal facts? -- Will it be useful later? - -Content: "{content}" -→ Score: +Preference Order: +IGNORE > MERGE > UPDATE > CREATE + +Rules: +1. If new fact matches existing closely → IGNORE +2. If related but complementary → MERGE (summarize) +3. If outdated/incomplete → UPDATE +4. Otherwise → CREATE new memory ``` -Score normalized and combined with length, source, and frequency. ### 5.2 Data Processing Pipelines -#### Ingestion Pipeline +#### Ingestion Flow ```mermaid flowchart LR - RawInput --> Parser --> Cleaner --> Classifier --> Embedder --> Indexer --> Stored + RawInput --> Parser{是否为对话} + Parser -->|"是"| RoleSplit[拆分为用户和助手] + Parser -->|"否"| DirectStore + RoleSplit --> BatchProcessor + BatchProcessor --> Embedder + Embedder --> Extractor + Extractor --> Classifier + Classifier --> Storage ``` -Steps: -1. **Parse**: Detect conversation turns (`User:` / `Assistant:`) -2. **Clean**: Remove noise, normalize whitespace -3. **Classify**: Assign `type` (fact, conversation, goal, etc.) -4. **Extract Topics**: Keyword extraction via TF-IDF + LLM suggestion -5. **Generate Embedding** -6. **Index**: Insert into Qdrant with payload (metadata) - -#### Search Pipeline +#### Search Flow ```mermaid flowchart LR - Query --> Embed --> ANN --> Filter --> Rerank --> Format --> Return + Query --> EmbedQuery + EmbedQuery --> VectorSearch["Find top-k similar vectors"] + VectorSearch --> FetchMetadata + FetchMetadata --> ApplyFilters["Filter by user_id, agent_id, topics"] + ApplyFilters --> RankResults["Score by relevance + recency"] + RankResults --> FormatOutput ``` -Post-processing includes: -- Snippet generation (context window around match) -- Highlighting query terms -- Recency boost: `score *= exp(-λ * age_in_days)` - ### 5.3 Business Rule Execution -#### Memory Retention Policies -| Type | Default TTL | Configurable | -|------|------------|-------------| -| Conversation | 90 days | Yes | -| Fact | Permanent | Yes | -| Temporary Note | 7 days | Yes | -| Goal | Until completion | Auto-archive | +| Rule | Enforcement Point | +|------|-------------------| +| No duplicate memories within 5 minutes | At ingestion time via timestamp + hash check | +| Maximum 100 characters in preview | During formatting in frontend/backend | +| Importance score decay over time | Applied during search ranking | +| Only owner can delete memory | Checked in `MemoryManager.delete()` using `user_id` | +| Confirmation required for bulk delete | Enforced in CLI command logic | -Enforced via scheduled cleanup job. +### 5.4 Technical Implementation Details -#### Access Control Rules -Currently based on: -- `user_id` (required) -- `agent_id` (optional filter) -- Future: RBAC roles (planned) +#### Asynchronous Architecture +- All critical operations are `async fn` +- Tokio runtime powers CLI, API, and MCP servers +- Non-blocking I/O ensures responsiveness even under load -All queries implicitly scoped to requester’s identity. +#### Memory Intelligence Loop +Uses LLM-driven updater to decide actions: +```rust +let decision = llm_client.prompt_with_schema::( + "Given these existing memories and new input, what should we do?", + context +).await?; +``` -### 5.4 Technical Implementation Details +#### Internationalization (i18n) +- Built with Svelte stores and reactive `$t` function +- Supports English, Chinese, Japanese +- Fallback to English on missing keys +- Language preference persisted in `localStorage` -#### Core Components -| File | Responsibility | -|------|----------------| -| `memory/manager.rs` | Primary orchestrator; exposes public API | -| `llm/client.rs` | Handles authentication, retries, batching | -| `vector_store/qdrant.rs` | Implements Qdrant gRPC client bindings | -| `optimization_detector.rs` | Analyzes memory corpus for issues | -| `optimizer.rs` | Executes refactoring plans | - -#### Concurrency Model -- **Tokio Runtime**: Multi-threaded, work-stealing scheduler -- **No Global Mutexes**: Per-operation locking -- **Channel-Based Communication**: Between UI and worker threads - -#### Configuration System -Built with: -- `serde` for deserialization -- `config.toml` as primary source -- Environment variable overrides -- Defaults via `impl Default` trait - -Supports hot-reload in development mode. - -#### Performance Optimization Strategies -| Area | Technique | -|------|----------| -| Latency | Async I/O, connection pooling | -| Throughput | Batch operations, bulk indexing | -| Scalability | Stateless services, horizontal scaling | -| Efficiency | Embedding caching, lazy loading | -| Observability | Tracing spans, metrics export | +#### Terminal UI (TARS Example) +- Built with `ratatui` + `crossterm` +- Three-panel layout: conversation (75%), input (25%), logs (right sidebar) +- Real-time streaming responses with cursor animation +- Sophisticated UTF-8 handling for cursor positioning + +#### Shutdown Sequence (Critical Reliability Feature) +In `examples/cortex-mem-tars/src/main.rs`: +```rust +// After UI exits, continue processing memory saves +drop(ui_tx); // Signal UI shutdown +while let Ok(log) = log_rx.try_recv() { + process_log_for_memory_storage(&log); +} +// Ensure all pending memory operations complete +``` -Planned enhancements: -- Redis cache layer for frequent queries -- Background embedding queue -- Incremental optimization (vs full scan) +> ✅ **Reliability Insight**: This design guarantees no memory loss due to premature termination. --- ## Conclusion -The **cortex-mem** system delivers a robust, extensible framework for AI agent memory management. Its core workflows—memory management, search, and optimization—are deeply integrated, consistently implemented, and resilient to failure. The architecture promotes reuse through a shared core engine while supporting diverse frontends and integration points. +The **Cortex-Mem** system delivers a robust, extensible framework for managing AI agent memories across diverse usage scenarios. Its architecture emphasizes: + +- **Modularity**: Clear separation between access interfaces and core logic +- **Resilience**: Comprehensive error handling, graceful degradation, and safe shutdown +- **Intelligence**: LLM-powered content understanding and autonomous optimization +- **Observability**: Rich logging, monitoring endpoints, and interactive dashboards -This document serves as a complete guide for developers, operators, and researchers working with the system, providing both high-level understanding and deep technical insight into its operation. +This documentation provides full operational visibility into the system’s workflows, enabling developers, operators, and integrators to effectively deploy, maintain, and extend its capabilities. -**Generated from research materials as of 2025-12-18 11:29:14 UTC** +**Generated on**: 2025-12-30 19:19:37 UTC +**Documentation Version**: 1.0 +**System Name**: Cortex-Mem +**Primary Users**: AI Agents, Developers, System Administrators \ No newline at end of file diff --git a/litho.docs/4.Deep-Exploration/Access Interface Domain.md b/litho.docs/4.Deep-Exploration/Access Interface Domain.md new file mode 100644 index 0000000..a3a4a20 --- /dev/null +++ b/litho.docs/4.Deep-Exploration/Access Interface Domain.md @@ -0,0 +1,422 @@ +# Technical Documentation: Access Interface Domain + +**Generation Time:** 2025-12-30 11:26:39 (UTC) +**Document Version:** 1.0 +**System:** Cortex-Mem – AI Agent Memory Management System + +--- + +## 1. Introduction + +The **Access Interface Domain** in the `cortex-mem` system provides multiple entry points for interacting with the persistent memory management capabilities of AI agents. It enables diverse user types—including developers, system administrators, and intelligent software agents—to store, retrieve, search, and optimize memories through tailored interfaces. + +This domain serves as the primary interaction layer between end users/agents and the core memory engine (`cortex-mem-core`), abstracting complex operations into accessible protocols while maintaining consistency across different access methods. + +### Key Objectives +- Support heterogeneous integration scenarios (CLI, API, agent tools, UI) +- Provide consistent behavior via centralized configuration +- Enable both human and machine-driven interactions +- Facilitate monitoring, debugging, and operational control + +--- + +## 2. Architecture Overview + +The Access Interface Domain follows a **multi-interface facade pattern**, where each sub-interface acts as a specialized gateway to the shared business logic implemented in `cortex-mem-core`. All interfaces depend directly on the `MemoryManager` for executing memory operations. + +```mermaid +graph TD + A[Access Interface Domain] --> B[CLI Interface] + A --> C[HTTP API Service] + A --> D[MCP Protocol Interface] + A --> E[Web Dashboard] + + B -->|Uses| Core[cortex-mem-core] + C -->|Uses| Core + D -->|Uses| Core + E -->|API Calls| C + E -->|Direct Use| Core + + style A fill:#2196F3,stroke:#1976D2,color:white + style Core fill:#4CAF50,stroke:#388E3C,color:white +``` + +> **Note**: The Web Dashboard primarily communicates via the HTTP API but may also use direct core access for advanced features. + +### Interaction Flow Pattern +```mermaid +sequenceDiagram + participant User + participant CLI as CLI Interface + participant HTTP_API as HTTP API Service + participant MCP as MCP Protocol Interface + participant Web_Dashboard as Web Dashboard + participant Memory_System as MemoryManager (Core) + + User->>CLI: Execute command (e.g., add, search) + CLI->>Memory_System: Forward request + Memory_System-->>CLI: Return result + CLI-->>User: Print output + + User->>HTTP_API: Send HTTP request + HTTP_API->>Memory_System: Handle route & forward + Memory_System-->>HTTP_API: Response + HTTP_API-->>User: JSON response + + AI_Agent->>MCP: Invoke MCP tool (store/query) + MCP->>Memory_System: Process call + Memory_System-->>MCP: Result + MCP-->>AI_Agent: Return structured data + + User->>Web_Dashboard: Browser navigation + Web_Dashboard->>HTTP_API: Fetch data via REST + HTTP_API-->>Web_Dashboard: JSON payload + Web_Dashboard-->>User: Rendered UI +``` + +--- + +## 3. Submodules and Implementation Details + +The Access Interface Domain consists of four distinct submodules, each designed for specific usage patterns and target audiences. + +### 3.1 CLI Interface + +#### Purpose +Provides a command-line tool for direct interaction by developers and operators. Ideal for scripting, batch operations, and local development. + +#### Entry Point +- **File**: `cortex-mem-cli/src/main.rs` +- **Framework**: [Clap](https://crates.io/crates/clap) for declarative argument parsing + +#### Supported Commands +| Command | Functionality | +|--------|---------------| +| `add` | Store new memory with metadata | +| `search` | Semantic + metadata-filtered retrieval | +| `list` | List memories with filters | +| `delete` | Remove memory by ID | +| `optimize` | Trigger optimization workflows | +| `optimize-status` | Check ongoing job status | + +#### Code Structure +```rust +// Example: AddCommand execution flow +pub async fn execute( + &self, + content: String, + user_id: Option, + agent_id: Option, + memory_type: String, +) -> Result<(), Box> { + let metadata = build_metadata(user_id, agent_id, memory_type); + + if is_conversation(&content) { + let messages = parse_conversation_content(&content, &user_id, &agent_id); + self.memory_manager.add_memory(&messages, metadata).await?; + } else { + self.memory_manager.store(content, metadata).await?; + } +} +``` + +#### Conversation Parsing Logic +Handles multi-turn dialogues from CLI input: +- Detects lines starting with `User:` or `Assistant:` +- Splits content into role-based messages +- Falls back to single-user message if no roles detected + +> **Example Input**: +``` +User: What's the capital of France? +Assistant: The capital of France is Paris. +``` + +Parsed into two `Message` objects with appropriate roles. + +#### Initialization Sequence +1. Initialize tracing (`tracing_subscriber`) +2. Parse CLI arguments using Clap +3. Load config from `config.toml` +4. Auto-detect vector store and LLM client +5. Create `MemoryManager` +6. Route command to handler + +--- + +### 3.2 HTTP API Service + +#### Purpose +Exposes RESTful endpoints for programmatic access. Enables integration with external applications, microservices, and web clients. + +#### Entry Point +- **File**: `cortex-mem-service/src/main.rs` +- **Framework**: [Axum](https://crates.io/crates/axum) for routing and middleware + +#### Endpoint Summary +| Method | Path | Description | +|-------|------|-------------| +| GET | `/health` | System health check | +| POST | `/memories` | Create memory | +| GET | `/memories` | List memories | +| POST | `/memories/search` | Search memories (semantic + filters) | +| GET | `/memories/{id}` | Retrieve specific memory | +| PUT | `/memories/{id}` | Update memory | +| DELETE | `/memories/{id}` | Delete memory | +| POST | `/optimization` | Start optimization job | +| GET | `/optimization/{job_id}` | Get optimization status | +| GET | `/llm/status` | LLM service status | + +#### Request Handling Pattern +```rust +pub async fn create_memory( + State(state): State, + Json(request): Json, +) -> Result, (StatusCode, Json)> { + let metadata = build_metadata_from_request(&request); + + if is_conversation_request(&request.content) { + let messages = parse_conversation_content( + &request.content, + &request.user_id, + &request.agent_id + ); + match state.memory_manager.add_memory(&messages, metadata).await { ... } + } else { + match state.memory_manager.store(request.content, metadata).await { ... } + } +} +``` + +#### Shared State Model +```rust +#[derive(Clone)] +pub struct AppState { + pub memory_manager: Arc, + pub optimization_jobs: Arc>>, +} +``` + +All routes receive this state via Axum’s dependency injection mechanism. + +#### Middleware +- CORS enabled permissively during development +- Structured error handling with standardized responses +- Tracing integration for observability + +--- + +### 3.3 MCP Protocol Interface + +#### Purpose +Implements the **Memory Control Protocol (MCP)** for seamless integration with AI agents. Allows agents to treat memory operations as callable tools. + +#### Entry Point +- **Binary**: `cortex-mem-mcp/src/main.rs` +- **Library**: `cortex-mem-mcp/src/lib.rs` +- **Transport**: Standard I/O (stdio) for compatibility with agent frameworks + +#### Implemented Tools +| Tool Name | Function | +|----------|---------| +| `store_memory` | Save a new memory | +| `query_memory` | Search memories semantically | +| `list_memories` | List memories with filters | +| `get_memory` | Retrieve memory by ID | + +#### Server Handler Implementation +```rust +impl MemoryMcpService { + async fn store_memory( + &self, + arguments: &Map, + ) -> Result { + let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id); + match self.operations.store_memory(payload).await { + Ok(response) => Ok(CallToolResult::success(vec![Content::text( + serde_json::to_string_pretty(&response).unwrap() + )])), + Err(e) => Err(self.tools_error_to_mcp_error(e)) + } + } + + // Similar implementations for query_memory, list_memories, get_memory +} +``` + +#### Initialization +- Loads configuration from default or specified path +- Initializes `MemoryManager` with auto-detected components +- Sets up `MemoryOperations` wrapper +- Serves over stdio using `rmcp::transport::stdio` + +#### Integration Example (Agent Side) +An AI agent can invoke: +```json +{ + "tool": "store_memory", + "arguments": { + "content": "The user prefers vegan meals.", + "user_id": "usr_123", + "memory_type": "factual" + } +} +``` + +And receive confirmation of successful storage. + +--- + +### 3.4 Web Dashboard + +#### Purpose +Provides a visual interface for monitoring, managing, and analyzing memory collections. Designed for system administrators and developers needing insight into memory quality and performance. + +#### Stack +- **Frontend Framework**: [Svelte](https://svelte.dev/) +- **Backend Server**: [Elysia](https://elysiajs.com/) (TypeScript) +- **Routing**: File-based SvelteKit routing +- **Styling**: Tailwind CSS + +#### Entry Points +- **Server**: `cortex-mem-insights/src/server/index.ts` +- **Layout**: `cortex-mem-insights/src/routes/+layout.svelte` + +#### Layout Component (`+layout.svelte`) +```svelte + + +
+ +
+ +
+
+ +
+
+``` + +#### Features +- Real-time system health visualization +- Memory search and browsing +- Optimization job tracking +- Statistics dashboard (duplicates, retention, usage trends) +- Internationalization support via `$lib/i18n` + +#### API Integration +Communicates with the backend via: +- `/api/memory/*` – CRUD and search operations +- `/api/optimization/*` – Optimization lifecycle +- `/api/system/*` – Health and configuration + +Built using modular Elysia plugins: +```ts +.use(memoryRoutes) +.use(optimizationRoutes) +.use(systemRoutes) +``` + +--- + +## 4. Cross-Cutting Concerns + +### 4.1 Configuration Management +All interfaces share a common configuration model defined in `cortex-mem-config`, loaded from `config.toml`. + +Key settings include: +- Qdrant connection details +- LLM provider (OpenAI) credentials +- Server host/port (for HTTP/MCP services) +- Logging level +- Memory retention policies + +Each interface reads the same config file, ensuring uniform behavior. + +### 4.2 Error Handling and Logging +- Unified logging via `tracing` crate +- Structured JSON logs for machine readability +- Human-friendly CLI output formatting +- HTTP error codes and standardized error payloads +- MCP-compliant error reporting + +### 4.3 Security Considerations +- No built-in authentication in current version (assumes trusted environment) +- Sensitive data protection relies on transport-level security +- Future roadmap likely includes API keys and RBAC + +--- + +## 5. Integration with Core Domains + +The Access Interface Domain depends heavily on other domains: + +| From | To | Type | Purpose | +|------|----|------|--------| +| Access Interface | Memory Management | Service Call | Execute CRUD, search, optimization | +| Access Interface | Configuration Management | Configuration Dependency | Load runtime settings | +| Access Interface | LLM Integration | Indirect | Embedding generation, analysis | +| Access Interface | Storage Integration | Indirect | Vector persistence | + +> **Dependency Strength**: 9.5 (Very Strong) + +All interfaces delegate actual memory processing to `MemoryManager`, adhering to the principle of separation of concerns. + +--- + +## 6. Usage Scenarios + +| User Type | Preferred Interface | Use Case | +|---------|---------------------|----------| +| Developer | CLI / HTTP API | Scripting, testing, integration | +| AI Agent | MCP Protocol | Contextual memory access during reasoning | +| Operator | Web Dashboard | Monitoring, maintenance, optimization | +| Application | HTTP API | Embedded agent memory layer | + +--- + +## 7. Development and Extension Guide + +### Adding a New Command (CLI) +1. Define command struct in `commands/mod.rs` +2. Implement `execute()` method calling `MemoryManager` +3. Add variant to `Commands` enum in `main.rs` +4. Wire up in `main()` match block + +### Adding a New API Endpoint +1. Define handler function in `handlers.rs` +2. Add route in `main.rs` router +3. Define request/response models in `models.rs` +4. Ensure proper error mapping + +### Extending MCP Tools +1. Add new method to `MemoryMcpService` +2. Register in `ServerHandler` trait implementation +3. Update tool definitions via `get_mcp_tool_definitions()` + +--- + +## 8. Conclusion + +The **Access Interface Domain** is a critical component of the `cortex-mem` architecture, enabling flexible and secure interaction with AI agent memory systems. By providing four complementary interfaces—CLI, HTTP API, MCP, and Web Dashboard—it supports a wide range of use cases from automated agent workflows to human-operated administration. + +Its design emphasizes: +- **Consistency**: Shared core logic ensures uniform behavior +- **Extensibility**: Modular structure allows adding new interfaces +- **Interoperability**: Supports both human and machine consumers +- **Observability**: Rich logging and monitoring capabilities + +Future enhancements could include: +- Authentication and authorization layers +- WebSockets for real-time updates +- gRPC interface for high-performance integrations +- Plugin system for custom interface extensions + +This domain exemplifies modern API-first design principles applied to AI infrastructure, making persistent memory accessible, reliable, and maintainable. \ No newline at end of file diff --git a/litho.docs/4.Deep-Exploration/Configuration Management Domain.md b/litho.docs/4.Deep-Exploration/Configuration Management Domain.md new file mode 100644 index 0000000..6c9cac9 --- /dev/null +++ b/litho.docs/4.Deep-Exploration/Configuration Management Domain.md @@ -0,0 +1,444 @@ +# Configuration Management Domain Technical Documentation + +## 1. Overview + +The Configuration Management Domain serves as the centralized configuration system for the Cortex-Mem platform, providing a unified approach to manage application settings across all components. This domain ensures consistent behavior and proper initialization of the memory management system by defining comprehensive configuration schemas and implementing robust loading, validation, and auto-detection mechanisms. + +As an Infrastructure Domain with high importance (8.0/10), the Configuration Management system acts as the single source of truth for settings that govern various subsystems including vector database connectivity, LLM integration, HTTP server parameters, embedding services, and memory management policies. The system is implemented primarily in Rust with supporting Python utilities for validation, following a modular design that enables extensibility while maintaining type safety. + +## 2. Architecture and Design + +### 2.1 Component Structure + +The Configuration Management Domain consists of three primary components: + +- **Config Structure**: Defines the schema and data types for configuration through Rust structs with serde serialization +- **Config Loading**: Handles file parsing and error propagation using TOML format +- **Config Validation**: Ensures configuration integrity through both structural and content validation + +The core implementation resides in `cortex-mem-config/src/lib.rs`, which exports a comprehensive set of configuration structs that are consumed by other components during initialization. + +### 2.2 Key Data Structures + +The configuration system is built around a hierarchical structure of Rust structs that represent different subsystem configurations: + +```rust +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + pub qdrant: QdrantConfig, + pub llm: LLMConfig, + pub server: ServerConfig, + pub embedding: EmbeddingConfig, + pub memory: MemoryConfig, + pub logging: LoggingConfig, +} +``` + +Each subsystem has its own dedicated configuration struct with specific fields relevant to its operation: + +- **QdrantConfig**: Manages vector database connection parameters +- **LLMConfig**: Handles language model service credentials and behavior +- **ServerConfig**: Controls HTTP server binding and CORS policies +- **EmbeddingConfig**: Configures embedding generation service +- **MemoryConfig**: Defines memory management policies and thresholds +- **LoggingConfig**: Specifies logging behavior and output destinations + +### 2.3 Design Principles + +The configuration system adheres to several key design principles: + +1. **Type Safety**: Leverages Rust's strong typing system to prevent invalid configurations at compile time +2. **Default Values**: Implements sensible defaults through the `Default` trait for optional parameters +3. **Extensibility**: Supports adding new configuration sections without breaking existing code +4. **Validation**: Provides multi-layered validation to ensure configuration correctness +5. **Auto-detection**: Includes intelligent features like automatic embedding dimension detection + +## 3. Implementation Details + +### 3.1 Configuration Schema + +The configuration schema is defined using Rust's derive macros for Serde, enabling seamless serialization between TOML files and in-memory data structures. The main `Config` struct composes multiple subsystem configurations, creating a comprehensive hierarchy that covers all aspects of the system. + +Key implementation features include: + +- **Serde Integration**: Uses `#[derive(Serialize, Deserialize)]` for automatic TOML parsing +- **Error Handling**: Employs `anyhow::Result` for rich error propagation with context +- **Path Abstraction**: Accepts generic path types through `AsRef` for flexibility +- **Clone Support**: Implements `Clone` trait to enable configuration sharing across components + +### 3.2 Default Values Implementation + +The system implements default values for optional parameters through Rust's `Default` trait, ensuring that required functionality works even when users don't specify all settings: + +```rust +impl Default for MemoryConfig { + fn default() -> Self { + MemoryConfig { + max_memories: 10000, + similarity_threshold: 0.65, + max_search_results: 50, + memory_ttl_hours: None, + auto_summary_threshold: 32768, + auto_enhance: true, + deduplicate: true, + merge_threshold: 0.75, + search_similarity_threshold: Some(0.70), + } + } +} + +impl Default for LoggingConfig { + fn default() -> Self { + LoggingConfig { + enabled: false, + log_directory: "logs".to_string(), + level: "info".to_string(), + } + } +} +``` + +This approach provides sensible defaults while still allowing users to override them through the configuration file. + +### 3.3 Auto-detection Mechanism + +One of the advanced features of the configuration system is the ability to auto-detect embedding dimensions when not explicitly specified. This is particularly valuable because embedding dimensions vary between different LLM models and services. + +The auto-detection workflow is implemented in `cortex-mem-core/src/init/mod.rs`: + +1. When `embedding_dim` is not specified in the Qdrant configuration +2. A temporary LLM client is created using the provided API credentials +3. A test embedding is generated for a sample text ("test") +4. The dimension of the resulting embedding vector is detected +5. The configuration is updated with the detected dimension + +```rust +pub async fn create_auto_config( + base_config: &QdrantConfig, + llm_client: &dyn LLMClient, +) -> Result { + let mut config = base_config.clone(); + + if config.embedding_dim.is_none() { + info!("Auto-detecting embedding dimension for configuration..."); + let test_embedding = llm_client.embed(\"test\").await?; + let detected_dim = test_embedding.len(); + info!(\"Detected embedding dimension: {}\", detected_dim); + config.embedding_dim = Some(detected_dim); + } + + Ok(config) +} +``` + +This feature eliminates configuration errors related to mismatched embedding dimensions and improves user experience by reducing setup complexity. + +## 4. Configuration Workflow + +### 4.1 Configuration Loading Process + +The configuration loading process follows a well-defined sequence: + +```mermaid +graph TD + A[Start] --> B{Config File Exists?} + B -->|No| C[Return Error] + B -->|Yes| D[Parse TOML File] + D --> E[Validate Required Sections] + E --> F[Validate Required Fields] + F --> G[Load Subsystem Configs] + G --> H[Apply Default Values if Needed] + H --> I[Auto-Detect Embedding Dimension?] + I -->|Yes| J[Infer from LLM Client] + J --> K[Create Final Config] + I -->|No| K + K --> L[Initialize Components] + L --> M[End] +``` + +### 4.2 Sequence of Operations + +The detailed sequence of operations during configuration loading: + +```mermaid +sequenceDiagram + participant User + participant ConfigModule + participant FileSystem + participant Validator + participant LLMClient + participant VectorStore + + User->>ConfigModule: Request config load + ConfigModule->>FileSystem: Read config.toml + FileSystem-->>ConfigModule: Return file content + ConfigModule->>ConfigModule: Parse TOML to Config struct + ConfigModule->>Validator: Validate required sections/fields + Validator-->>ConfigModule: Validation result + alt If embedding_dim not specified + ConfigModule->>LLMClient: Create temporary client + LLMClient-->>ConfigModule: Return client + ConfigModule->>LLMClient: Request test embedding + LLMClient-->>ConfigModule: Return embedding dimension + ConfigModule->>ConfigModule: Update config with detected dimension + end + ConfigModule->>VectorStore: Initialize with config + VectorStore-->>ConfigModule: Return initialized store + ConfigModule-->>User: Return ready-to-use components +``` + +## 5. Configuration File Format + +### 5.1 TOML Configuration Structure + +The system uses TOML (Tom's Obvious, Minimal Language) as the configuration file format due to its readability and support for complex data structures. The main configuration file (`config.toml`) contains the following sections: + +```toml +# Main configuration for the cortex-mem system + +[qdrant] +url = "http://localhost:6334" +collection_name = "cortex-mem-hewlett_drawn" +# embedding_dim = 1024 # Optional, will be auto-detected if not specified +timeout_secs = 30 + +[llm] +api_base_url = "https://wanqing-api.corp.kuaishou.com/api/gateway/v1/endpoints" +api_key = "fs2wzco3o7haz38df1jo4vavnvauxtuz3f0b" +model_efficient = "ep-i4abhq-1764595896785685523" +temperature = 0.1 +max_tokens = 4096 + +[server] +host = "0.0.0.0" +port = 3000 +cors_origins = ["*"] + +[embedding] +api_base_url = "https://wanqing-api.corp.kuaishou.com/api/gateway/v1/endpoints" +api_key = "fs2wzco3o7haz38df1jo4vavnvauxtuz3f0b" +model_name = "ep-9kf01g-1762237999831608613" +batch_size = 10 +timeout_secs = 30 + +[memory] +max_memories = 10000 +max_search_results = 50 +# memory_ttl_hours = 24 # Optional +auto_summary_threshold = 4096 +auto_enhance = true +deduplicate = true +similarity_threshold = 0.65 +merge_threshold = 0.75 +search_similarity_threshold = 0.5 + +[logging] +enabled = true +log_directory = "logs" +level = "debug" +``` + +### 5.2 Required vs. Optional Parameters + +The configuration system distinguishes between required and optional parameters: + +**Required Parameters:** +- `qdrant.url`: Vector database endpoint URL +- `qdrant.collection_name`: Name of the collection in Qdrant +- `llm.api_base_url`: LLM service API endpoint +- `llm.api_key`: Authentication key for LLM service +- `llm.model_efficient`: Model identifier for efficient operations +- `embedding.api_base_url`: Embedding service API endpoint +- `embedding.api_key`: Authentication key for embedding service +- `embedding.model_name`: Model identifier for embeddings + +**Optional Parameters with Defaults:** +- `qdrant.embedding_dim`: Auto-detected if not specified +- `memory.memory_ttl_hours`: No expiration if not specified +- `memory.search_similarity_threshold`: Defaults to 0.70 +- `logging.enabled`: Defaults to false +- `logging.level`: Defaults to "info" + +## 6. Validation System + +### 6.1 Multi-Layered Validation Approach + +The configuration system employs a multi-layered validation strategy to ensure configuration integrity: + +1. **Structural Validation**: Performed automatically by Serde during TOML deserialization +2. **Content Validation**: Implemented through custom Python utilities in `examples/lomoco-evaluation/src/cortex_mem/config_utils.py` +3. **Runtime Validation**: Conducted during component initialization + +### 6.2 Validation Implementation + +The Python-based validation utilities provide comprehensive checking of configuration completeness: + +```python +def validate_config(config_path: str) -> bool: + """Validate that config file exists and has required settings.""" + if not os.path.exists(config_path): + print(f"Config file not found: {config_path}") + return False + + try: + with open(config_path, 'r') as f: + content = f.read() + + # Check for required sections + required_sections = ["llm", "embedding", "qdrant", "memory"] + missing_sections = [] + + for section in required_sections: + if f"[{section}]" not in content: + missing_sections.append(section) + + if missing_sections: + print(f"Missing required sections in config: {missing_sections}") + return False + + # Check for required fields in each section + import toml + config_data = toml.load(config_path) + + # Check llm section + if "llm" in config_data: + llm = config_data["llm"] + required_llm_fields = ["api_key", "api_base_url", "model_efficient"] + missing_llm = [field for field in required_llm_fields if field not in llm] + if missing_llm: + print(f"Missing fields in [llm] section: {missing_llm}") + return False + + # Similar checks for embedding, qdrant, and other sections + return True + + except Exception as e: + print(f"Error validating config: {e}") + return False +``` + +The validation system checks for: +- Existence of the configuration file +- Presence of required sections (`[llm]`, `[embedding]`, `[qdrant]`, `[memory]`) +- Required fields within each section +- Valid data types and formats + +### 6.3 OpenAI-Specific Validation + +A specialized validation function checks OpenAI configuration specifically: + +```python +def check_openai_config(config_path: str) -> bool: + """Check if OpenAI configuration is properly set.""" + try: + import toml + config_data = toml.load(config_path) + + # Check llm section + if "llm" not in config_data: + print("Missing [llm] section in config") + return False + + llm = config_data["llm"] + if "api_key" not in llm or not llm["api_key"]: + print("OpenAI API key not set in [llm] section") + return False + + if "api_base_url" not in llm or not llm["api_base_url"]: + print("OpenAI API base URL not set in [llm] section") + return False + + # Check embedding section + if "embedding" not in config_data: + print("Missing [embedding] section in config") + return False + + embedding = config_data["embedding"] + if "api_key" not in embedding or not embedding["api_key"]: + print("OpenAI API key not set in [embedding] section") + return False + + return True + + except Exception as e: + print(f"Error checking OpenAI config: {e}") + return False +``` + +## 7. Integration with Other Domains + +### 7.1 Configuration Dependencies + +The Configuration Management Domain serves as a foundational component that other domains depend on for their operation: + +| Dependent Domain | Configuration Usage | +|------------------|----------------------| +| **Storage Integration Domain** | Uses Qdrant configuration for vector database connectivity | +| **LLM Integration Domain** | Uses LLM and embedding configurations for service authentication and behavior | +| **Memory Optimization Domain** | Uses memory configuration for optimization thresholds and strategies | +| **Access Interface Domain** | Uses server and logging configurations for interface behavior | + +### 7.2 Initialization Flow + +During system startup, the configuration system plays a critical role in the initialization workflow: + +```mermaid +graph TD + A[Load Configuration] --> B[Initialize Tracing & Logging] + B --> C{Auto-detect Components} + C --> D[Detect Vector Store] + C --> E[Detect LLM Client] + C --> F[Determine Embedding Dimension] + + D --> G[Create MemoryManager] + E --> G + F --> G + + G --> H[Expose Interfaces] + H --> I[CLI Interface] + H --> J[HTTP API Service] + H --> K[MCP Server] + H --> L[Web Dashboard] +``` + +The configuration is loaded first, then used to initialize tracing and logging systems, followed by auto-detection of components based on the provided settings. + +## 8. Best Practices and Recommendations + +### 8.1 Configuration Management Best Practices + +1. **Environment-Specific Configuration**: Use different configuration files for development, testing, and production environments +2. **Secret Management**: Never commit API keys to version control; use environment variables or secret management tools +3. **Version Control**: Keep configuration files under version control (without secrets) to track changes +4. **Documentation**: Document all configuration options and their effects +5. **Testing**: Validate configuration changes in non-production environments before deployment + +### 8.2 Security Considerations + +1. **API Key Protection**: Ensure API keys are stored securely and have appropriate access controls +2. **Network Security**: Use HTTPS for all external service communications +3. **Input Validation**: Validate all configuration inputs to prevent injection attacks +4. **Least Privilege**: Configure API keys with minimal required permissions +5. **Audit Logging**: Enable logging to monitor configuration changes and access patterns + +### 8.3 Performance Optimization + +1. **Caching**: Cache configuration data in memory to avoid repeated file I/O +2. **Connection Pooling**: Use connection pooling for database and API connections +3. **Batch Processing**: Configure appropriate batch sizes for embedding generation +4. **Timeout Settings**: Set reasonable timeouts to prevent hanging operations +5. **Resource Limits**: Configure memory limits to prevent resource exhaustion + +## 9. Future Enhancements + +Potential improvements to the Configuration Management Domain include: + +1. **Dynamic Reloading**: Support for reloading configuration without restarting the system +2. **Remote Configuration**: Integration with configuration servers for centralized management +3. **Configuration Versioning**: Support for versioned configurations and rollback capabilities +4. **Schema Validation**: JSON Schema-based validation for additional integrity checks +5. **Environment Variables**: Enhanced support for environment variable overrides +6. **Configuration Templates**: Support for template-based configuration generation +7. **Validation Rules Engine**: More sophisticated validation rules based on inter-parameter dependencies + +These enhancements would further improve the flexibility, security, and maintainability of the configuration system while supporting more complex deployment scenarios. \ No newline at end of file diff --git a/litho.docs/4.Deep-Exploration/LLM Integration Domain.md b/litho.docs/4.Deep-Exploration/LLM Integration Domain.md new file mode 100644 index 0000000..43127d6 --- /dev/null +++ b/litho.docs/4.Deep-Exploration/LLM Integration Domain.md @@ -0,0 +1,335 @@ +# LLM Integration Domain Technical Documentation + +## 1. Overview + +The **LLM Integration Domain** is a core component of the Cortex-Mem system responsible for managing interactions with Large Language Models (LLMs) to extract insights from content and enable intelligent decision-making for memory operations. This domain serves as the cognitive engine that transforms unstructured text into structured knowledge, enabling advanced memory management capabilities. + +This documentation provides comprehensive technical details about the architecture, implementation, and functionality of the LLM Integration Domain based on the analysis of source code and system research materials. + +### Key Characteristics +- **Domain Type**: Core Business Domain +- **Importance Score**: 9.0/10.0 +- **Complexity Level**: High (8.5/10.0) +- **Primary Responsibility**: Enabling intelligent processing of memory content through LLM-powered analysis, extraction, and decision-making + +```mermaid +graph TD + A[LLM Client] --> B[Information Extraction] + A --> C[Memory Intelligence] + B --> D[Extract Structured Facts] + B --> E[Extract Keywords] + B --> F[Extract Entities] + C --> G[Analyze Content] + C --> H[Assess Similarity] + C --> I[Recommend Operations] + A --> J[Health Check] + A --> K[Rate Limiting] +``` + +## 2. Architecture and Components + +The LLM Integration Domain consists of three primary sub-modules that work together to provide comprehensive LLM capabilities: + +### 2.1 LLM Client Module + +The `LLMClient` is the foundational component that manages communication with external LLM services, primarily OpenAI. It implements a robust client interface with support for both traditional text completion and modern structured extraction capabilities. + +#### Key Features: +- **Multi-capability Interface**: Supports text completion, embedding generation, and structured data extraction +- **Fallback Mechanisms**: Implements graceful degradation when structured extraction fails +- **Rate Limiting**: Includes built-in rate limiting (1 second delay between batch operations) +- **Health Monitoring**: Provides health checking through embedding requests +- **Concurrent Operation**: Designed to be cloned and used concurrently in asynchronous contexts + +#### Trait Definition: +```rust +#[async_trait] +pub trait LLMClient: Send + Sync + dyn_clone::DynClone { + // Core capabilities + async fn complete(&self, prompt: &str) -> Result; + async fn embed(&self, text: &str) -> Result>; + async fn embed_batch(&self, texts: &[String]) -> Result>>; + + // Information extraction + async fn extract_keywords(&self, content: &str) -> Result>; + async fn summarize(&self, content: &str, max_length: Option) -> Result; + + // Health and monitoring + async fn health_check(&self) -> Result; + + // Structured extraction methods + async fn extract_structured_facts(&self, prompt: &str) -> Result; + async fn extract_detailed_facts(&self, prompt: &str) -> Result; + async fn classify_memory(&self, prompt: &str) -> Result; + async fn score_importance(&self, prompt: &str) -> Result; +} +``` + +#### Implementation Details: +The `OpenAILLMClient` implementation uses the RIG framework to interact with OpenAI services, providing both completion and embedding models. The client is configured through centralized configuration (`LLMConfig` and `EmbeddingConfig`) which specifies API keys, endpoints, model names, temperature settings, and token limits. + +Key architectural decisions include: +- **Separation of Concerns**: Different models are used for completion vs. embedding tasks +- **Error Resilience**: Comprehensive error handling with fallback mechanisms +- **Performance Optimization**: Batch processing with controlled rate limiting +- **Debug Support**: Conditional sleep statements in debug builds to prevent rate limiting issues during development + +### 2.2 Information Extraction Module + +The Information Extraction module leverages the LLM Client to transform unstructured content into structured knowledge representations. This module focuses on extracting specific types of information from memory content. + +#### Primary Capabilities: +- **Fact Extraction**: Identifies and extracts important facts from conversations +- **Keyword Identification**: Extracts key terms and phrases from content +- **Entity Recognition**: Detects named entities such as people, organizations, and locations +- **Language Detection**: Automatically detects the language of input content +- **Conversation Analysis**: Analyzes conversation dynamics including topics, sentiment, and user intent + +#### Data Structures: +The module defines several structured response types using Serde serialization and JSON Schema generation: + +```rust +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct StructuredFactExtraction { + pub facts: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct DetailedFactExtraction { + pub facts: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct KeywordExtraction { + pub keywords: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct EntityExtraction { + pub entities: Vec, +} +``` + +#### Processing Workflow: +1. **Prompt Construction**: Builds specialized prompts for different extraction tasks +2. **Structured Extraction**: Uses RIG's extractor_completions_api for reliable structured output +3. **Fallback Processing**: Falls back to traditional completion if structured extraction fails +4. **Response Parsing**: Parses and validates the structured responses +5. **Result Normalization**: Standardizes the extracted information for downstream use + +The module implements sophisticated prompt engineering techniques, including: +- Role-specific prompts for user vs. assistant messages +- Context-aware extraction strategies +- Language-preserving extraction (facts are returned in the same language as input) + +### 2.3 Memory Intelligence Module + +The Memory Intelligence module applies LLM analysis to make strategic decisions about memory operations. This module represents the highest level of cognitive processing in the system, using LLM insights to guide memory lifecycle management. + +#### Key Functions: +- **Content Analysis**: Evaluates memory content for importance, relevance, and quality +- **Similarity Assessment**: Determines semantic similarity between memories for deduplication +- **Operation Recommendation**: Recommends optimal actions (create, update, merge, delete) for memory management +- **Optimization Planning**: Generates plans for memory collection improvement + +#### Decision-Making Process: +The module implements a sophisticated decision framework that considers multiple factors when recommending memory operations: + +```mermaid +sequenceDiagram + participant User + participant LLMClient + participant InformationExtraction + participant MemoryIntelligence + + User->>LLMClient: Request text completion + LLMClient->>LLMClient: Process request with fallback mechanism + LLMClient-->>User: Return completion result + + User->>LLMClient: Request structured extraction + LLMClient->>LLMClient: Use rig extractor or fallback to completion + LLMClient-->>User: Return structured data + + InformationExtraction->>LLMClient: Extract keywords/facts/entities + LLMClient-->>InformationExtraction: Return extracted information + + MemoryIntelligence->>LLMClient: Analyze content for memory decisions + LLMClient-->>MemoryIntelligence: Return analysis results +``` + +## 3. Integration Patterns + +### 3.1 Configuration Integration + +The LLM Integration Domain depends on the Configuration Management Domain for critical settings: + +```rust +pub struct OpenAILLMClient { + completion_model: Agent, + completion_model_name: String, + embedding_model: OpenAIEmbeddingModel, + client: Client, +} + +impl OpenAILLMClient { + pub fn new(llm_config: &LLMConfig, embedding_config: &EmbeddingConfig) -> Result { + let client = Client::builder(&llm_config.api_key) + .base_url(&llm_config.api_base_url) + .build(); + + // Configuration-driven model selection + let completion_model: Agent = client + .completion_model(&llm_config.model_efficient) + .completions_api() + .into_agent_builder() + .temperature(llm_config.temperature as f64) + .max_tokens(llm_config.max_tokens as u64) + .build(); + } +} +``` + +Configuration parameters include: +- API keys and base URLs +- Model selection (efficient vs. powerful) +- Temperature and token limits +- Embedding model specifications + +### 3.2 Interaction with Other Domains + +The LLM Integration Domain interacts with several other domains in the system: + +#### With Memory Management Domain: +- Provides embedding generation for semantic search +- Enables content analysis for memory classification +- Supports intelligent retrieval through semantic understanding + +#### With Memory Optimization Domain: +- Powers duplicate detection through similarity assessment +- Enables quality scoring for optimization decisions +- Supports merging recommendations based on content analysis + +#### With Access Interface Domain: +- Processes natural language queries from CLI +- Interprets search requests from HTTP API +- Handles agent commands through MCP interface + +## 4. Technical Implementation Details + +### 4.1 Error Handling and Resilience + +The implementation includes comprehensive error handling strategies: + +```rust +async fn extract_keywords(&self, content: &str) -> Result> { + let prompt = self.build_keyword_prompt(content); + + match self.extract_keywords_structured(&prompt).await { + Ok(keyword_extraction) => { + debug!("Extracted {} keywords using rig extractor", + keyword_extraction.keywords.len()); + Ok(keyword_extraction.keywords) + } + Err(e) => { + // Fallback to traditional method if extractor fails + debug!("Rig extractor failed, falling back: {}", e); + + #[cfg(debug_assertions)] + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + + let response = self.complete(&prompt).await?; + let keywords = self.parse_keywords(&response); + Ok(keywords) + } + } +} +``` + +Key resilience features: +- **Graceful Degradation**: Falls back to traditional completion when structured extraction fails +- **Rate Limiting**: Implements 1-second delays between batch operations +- **Comprehensive Logging**: Detailed debug logging for troubleshooting +- **Input Validation**: Validates inputs before processing + +### 4.2 Performance Considerations + +The implementation addresses performance through several mechanisms: + +1. **Batch Processing**: Efficient handling of multiple embedding requests +2. **Caching Strategy**: While not explicitly implemented, the design supports future caching layers +3. **Asynchronous Operations**: Fully async implementation for non-blocking operation +4. **Resource Management**: Proper cleanup of resources and connections + +### 4.3 Security and Privacy + +The implementation includes security considerations: + +- **API Key Protection**: Keys are passed through secure configuration +- **Input Sanitization**: Removes code blocks and potentially harmful content +- **Data Minimization**: Only extracts necessary information +- **Privacy Awareness**: Prompts emphasize not revealing model information + +## 5. Usage Examples + +### 5.1 Creating an LLM Client + +```rust +let llm_client = create_llm_client(&llm_config, &embedding_config)?; +``` + +### 5.2 Extracting Information from Content + +```rust +// Extract keywords +let keywords = llm_client.extract_keywords("I love hiking in the mountains").await?; + +// Generate embeddings +let embedding = llm_client.embed("memory content").await?; + +// Extract structured facts +let facts = llm_client.extract_structured_facts(&prompt).await?; +``` + +### 5.3 Using in Memory Processing Workflow + +```rust +// In memory creation process +let embedding = llm_client.embed(&content).await?; +let keywords = llm_client.extract_keywords(&content).await?; +let entities = llm_client.extract_entities(&prompt).await?; +``` + +## 6. Best Practices and Recommendations + +### 6.1 Configuration Guidelines + +1. **Model Selection**: Use efficient models for high-volume operations +2. **Temperature Settings**: Lower temperatures for more consistent extraction +3. **Token Limits**: Set appropriate limits based on expected content length +4. **Rate Limiting**: Configure according to your LLM provider's limits + +### 6.2 Performance Optimization + +1. **Batch Requests**: Group embedding requests when possible +2. **Connection Pooling**: Reuse client instances across operations +3. **Caching**: Implement caching for frequently accessed content +4. **Monitoring**: Track API usage and costs + +### 6.3 Error Handling + +1. **Implement Retry Logic**: For transient failures +2. **Monitor Health**: Regularly check service availability +3. **Graceful Degradation**: Provide fallback behavior when LLM services are unavailable +4. **Detailed Logging**: Capture sufficient context for debugging + +## 7. Future Enhancements + +Potential improvements to the LLM Integration Domain: + +1. **Multiple Provider Support**: Extend beyond OpenAI to include other LLM providers +2. **Advanced Caching**: Implement intelligent caching of embeddings and extractions +3. **Adaptive Prompting**: Dynamically adjust prompts based on content characteristics +4. **Cost Optimization**: Implement strategies to minimize API costs +5. **Enhanced Security**: Add encryption for sensitive content processing + +The LLM Integration Domain represents a sophisticated implementation of LLM-powered intelligence in a memory management system, providing the cognitive capabilities that enable advanced AI agent functionality. \ No newline at end of file diff --git a/litho.docs/4.Deep-Exploration/Memory Management Domain.md b/litho.docs/4.Deep-Exploration/Memory Management Domain.md index 3fd5acd..ae2513f 100644 --- a/litho.docs/4.Deep-Exploration/Memory Management Domain.md +++ b/litho.docs/4.Deep-Exploration/Memory Management Domain.md @@ -1,260 +1,422 @@ -# Memory Management Domain Technical Implementation Documentation +# Memory Management Domain Technical Documentation -## 1. Overview and Architecture +## 1. Overview -The **Memory Management Domain** serves as the central orchestrator for all memory-related operations in the `cortex-mem` system, providing a comprehensive lifecycle management solution for AI agent memories. This domain implements a modular, service-oriented architecture with clear separation of concerns, enabling intelligent storage, retrieval, optimization, and analysis of persistent knowledge. +The **Memory Management Domain** serves as the central orchestrator for all memory operations within the Cortex-Mem system, providing a comprehensive solution for AI agent memory persistence, retrieval, and optimization. This domain enables intelligent software agents to maintain context across interactions by managing structured knowledge through advanced processing pipelines that leverage large language models (LLMs) and vector-based storage. -### Core Responsibilities -- Orchestrate CRUD operations for memory entities -- Manage advanced processing pipelines (extraction, classification, deduplication) -- Coordinate LLM-driven decision making for memory updates -- Provide semantic search capabilities with relevance ranking -- Enable batch operations and transactional integrity +At its core, the Memory Manager integrates multiple specialized components including vector storage, LLM services, fact extraction, importance evaluation, duplicate detection, and content classification. The architecture follows a dependency injection pattern where external services are provided at construction time, enabling flexible composition and testability while maintaining separation of concerns. + +### Key Characteristics +- **Modular Design**: Clear separation between core logic and supporting processors +- **LLM-Augmented Processing**: Intelligent metadata generation using language models +- **Semantic Capabilities**: Vector-based similarity search with hybrid scoring +- **Lifecycle Management**: Full CRUD operations with automated enhancement +- **Optimization Ready**: Built-in support for deduplication and quality improvement + +## 2. Architecture Diagram + +```mermaid +graph TD + A[External Services] --> B[MemoryManager] + B --> C[VectorStore] + B --> D[LLMClient] + B --> E[FactExtractor] + B --> F[MemoryUpdater] + B --> G[ImportanceEvaluator] + B --> H[DuplicateDetector] + B --> I[MemoryClassifier] + C --> J[(Persistent Storage)] + D --> K[Language Model API] + E --> L[Fact Extraction] + F --> M[Memory Update Logic] + G --> N[Importance Scoring] + H --> O[Deduplication] + I --> P[Content Classification] + B --> Q[Core Operations] + Q --> R[CRUD Operations] + Q --> S[Search & Retrieval] + Q --> T[Statistics & Health] +``` + +## 3. Core Components and Interfaces + +### 3.1 MemoryManager + +The `MemoryManager` struct is the primary entry point for all memory operations, coordinating interactions between various subsystems: -### Architectural Pattern -The domain follows a **composition pattern** where the `MemoryManager` class acts as the primary facade, integrating specialized components through dependency injection: ```rust pub struct MemoryManager { vector_store: Box, llm_client: Box, - fact_extractor: Box, - memory_updater: Box, - importance_evaluator: Box, - duplicate_detector: Box, - memory_classifier: Box + config: MemoryConfig, + fact_extractor: Box, + memory_updater: Box, + importance_evaluator: Box, + duplicate_detector: Box, + memory_classifier: Box, } ``` -This design enables pluggable implementations while maintaining loose coupling between components. +#### Construction Pattern +The manager uses dependency injection to receive essential services: +```rust +impl MemoryManager { + pub fn new( + vector_store: Box, + llm_client: Box, + config: MemoryConfig, + ) -> Self { /* ... */ } +} +``` ---- +This approach allows for flexible configuration and testing, with internal processors created from cloned references to the injected dependencies. -## 2. Key Components and Their Interactions +### 3.2 Key Interfaces -### 2.1 MemoryManager - Central Orchestrator +#### VectorStore Interface +Provides persistent storage capabilities: +- `insert(&self, memory: &Memory) -> Result<()>`: Store a memory record +- `search(&self, query_vector: &[f32], filters: &Filters, limit: usize) -> Result>`: Semantic similarity search +- `get(&self, id: &str) -> Result>`: Retrieve by ID +- `update(&self, memory: &Memory) -> Result<()>`: Update existing record +- `delete(&self, id: &str) -> Result<()>`: Remove from storage +- `list(&self, filters: &Filters, limit: Option) -> Result>`: Filtered retrieval +- `health_check(&self) -> Result`: Database connectivity check -The `MemoryManager` (implemented in `cortex-mem-core/src/memory/manager.rs`) serves as the single entry point for all memory operations, coordinating interactions between various subsystems. +#### LLMClient Interface +Enables interaction with language model services: +- `complete(&self, prompt: &str) -> Result`: Text generation +- `embed(&self, text: &str) -> Result>`: Generate embeddings +- `classify_memory(&self, prompt: &str) -> Result`: Content categorization +- `extract_keywords(&self, content: &str) -> Result>`: Keyword extraction +- `summarize(&self, content: &str, max_length: Option) -> Result`: Content summarization +- `health_check(&self) -> Result`: Service availability check -#### Primary Functions: -- **Request Routing**: Directs incoming requests to appropriate handlers based on content type and operation -- **Dependency Injection**: Manages instances of LLM clients, vector stores, extractors, and evaluators -- **Transaction Coordination**: Ensures atomicity across multiple operations (create/update/delete) +#### Specialized Processors +| Processor | Purpose | +|---------|--------| +| **FactExtractor** | Extract structured facts from conversations | +| **MemoryUpdater** | Determine optimal actions (CREATE/UPDATE/MERGE/DELETE) | +| **ImportanceEvaluator** | Score memory value on 0-1 scale | +| **DuplicateDetector** | Identify and merge redundant memories | +| **MemoryClassifier** | Categorize content type and extract entities/topics | -#### Critical Methods: -| Method | Purpose | Implementation File | -|-------|--------|-------------------| -| `add_memory()` | Process conversation messages into structured facts | manager.rs | -| `store()` | Store simple content with metadata enhancement | manager.rs | -| `search_memories()` | Execute semantic searches with filtering | manager.rs | -| `list()` | Retrieve memories with pagination and filters | manager.rs | +## 4. Core Functionalities -### 2.2 Interaction Flow +### 4.1 Enhanced Memory Creation -```mermaid -sequenceDiagram - participant Client - participant Handler - participant MemoryManager - participant LLMClient - participant VectorStore - - Client->>Handler: create_memory(request) - activate Handler - Handler->>MemoryManager: add_memory(messages, metadata) - activate MemoryManager - - alt Simple Content - MemoryManager->>LLMClient: embed(content) - activate LLMClient - LLMClient-->>MemoryManager: embedding - deactivate LLMClient - MemoryManager->>MemoryManager: generate_hash() - MemoryManager->>VectorStore: insert(memory) - else Conversation - MemoryManager->>FactExtractor: extract_facts(messages) - activate FactExtractor - FactExtractor-->>MemoryManager: extracted_facts - deactivate FactExtractor - - loop For each fact - MemoryManager->>LLMClient: embed(fact.content) - LLMClient-->>MemoryManager: embedding - MemoryManager->>VectorStore: search(similar_memories) - VectorStore-->>MemoryManager: results - MemoryManager->>MemoryUpdater: update_memories() - activate MemoryUpdater - MemoryUpdater->>LLMClient: complete(prompt) - LLMClient-->>MemoryUpdater: decisions - MemoryUpdater-->>MemoryManager: UpdateResult - deactivate MemoryUpdater - - alt CREATE - MemoryManager->>MemoryManager: create_memory() - MemoryManager->>VectorStore: insert() - else UPDATE - MemoryManager->>VectorStore: update() - else MERGE - MemoryManager->>MemoryManager: merge_memories() - MemoryManager->>VectorStore: update/delete() - end - end - end - - MemoryManager-->>Handler: MemoryResult[] - deactivate MemoryManager - Handler-->>Client: response - deactivate Handler -``` +The `enhance_memory` method adds rich metadata through LLM-powered analysis: ---- +```rust +async fn enhance_memory(&self, memory: &mut Memory) -> Result<()> { + // Extract keywords + if let Ok(keywords) = self.llm_client.extract_keywords(&memory.content).await { + memory.metadata.custom.insert("keywords", serde_json::Value::Array( + keywords.into_iter().map(serde_json::Value::String).collect() + )); + } -## 3. Advanced Processing Pipeline + // Generate summary for long content + if memory.content.len() > self.config.auto_summary_threshold { + if let Ok(summary) = self.llm_client.summarize(&memory.content, Some(200)).await { + memory.metadata.custom.insert("summary", serde_json::Value::String(summary)); + } + } -### 3.1 Fact Extraction System + // Classify memory type and extract metadata + if let Ok(memory_type) = self.memory_classifier.classify_memory(&memory.content).await { + memory.metadata.memory_type = memory_type; + } -Implemented in `cortex-mem-core/src/memory/extractor.rs`, this component extracts meaningful information from conversational context using dual-channel analysis: + // Extract entities and topics + if let Ok(entities) = self.memory_classifier.extract_entities(&memory.content).await { + memory.metadata.entities = entities; + } -#### Extraction Strategies: -- **DualChannel**: Extract both user and assistant facts -- **UserOnly**: Focus on user-provided information only -- **AssistantOnly**: Capture assistant-generated insights -- **ProceduralMemory**: Specialized for step-by-step processes + if let Ok(topics) = self.memory_classifier.extract_topics(&memory.content).await { + memory.metadata.topics = topics; + } -#### Fact Structure: -```rust -pub struct ExtractedFact { - pub content: String, - pub importance: f32, - pub category: FactCategory, - pub entities: Vec, - pub language: Option, - pub source_role: String // "user" or "assistant" -} + // Evaluate importance + if let Ok(importance) = self.importance_evaluator.evaluate_importance(memory).await { + memory.metadata.importance_score = importance; + } -pub enum FactCategory { - Personal, // Personal information about users - Preference, // User preferences and likes/dislikes - Factual, // General factual information - Procedural, // How-to information and procedures - Contextual, // Context about ongoing conversations + // Check for duplicates and merge if necessary + if let Ok(duplicates) = self.duplicate_detector.detect_duplicates(memory).await { + if !duplicates.is_empty() { + let mut all_memories = vec![memory.clone()]; + all_memories.extend(duplicates); + + if let Ok(merged_memory) = self.duplicate_detector.merge_memories(&all_memories).await { + *memory = merged_memory; + + // Remove old duplicates + for duplicate in &all_memories[1..] { + let _ = self.vector_store.delete(&duplicate.id).await; + } + } + } + } } ``` -#### Prompt Engineering: -Uses carefully crafted prompts to prevent hallucinations and ensure fidelity: -```text -[IMPORTANT]: GENERATE FACTS SOLELY BASED ON THE USER'S MESSAGES. -DO NOT INCLUDE INFORMATION FROM ASSISTANT OR SYSTEM MESSAGES. -``` +Key features include: +- Automatic keyword extraction and summarization +- Content classification into six types: Conversational, Procedural, Factual, Semantic, Episodic, Personal +- Entity and topic recognition +- Importance scoring using hybrid rule-based and LLM approaches +- Duplicate detection and automatic merging -### 3.2 Memory Classification System +### 4.2 Conversation-Based Memory Processing -Located in `cortex-mem-core/src/memory/classification.rs`, this module categorizes memories into distinct types using hybrid approaches. +The `add_memory` method handles conversation inputs through a sophisticated pipeline: -#### Supported Types (`types.rs`): ```rust -pub enum MemoryType { - Conversational, - Procedural, - Factual, - Semantic, - Episodic, - Personal, -} -``` +pub async fn add_memory( + &self, + messages: &[Message], + metadata: MemoryMetadata, +) -> Result> { + // Handle procedural memory special case + if metadata.agent_id.is_some() && metadata.memory_type == MemoryType::Procedural { + return self.create_procedural_memory(messages, metadata).await; + } -#### Classification Strategies: -| Strategy | Use Case | Threshold | -|---------|---------|-----------| -| **LLM-based** | High accuracy classification | Default | -| **Rule-based** | Fast processing | Content length < 100 chars | -| **Hybrid** | Balance speed and accuracy | Configurable threshold | + // Primary fact extraction + let extracted_facts = self.fact_extractor.extract_facts(messages).await?; + let mut final_extracted_facts = extracted_facts; -#### Keyword Matching (Multilingual Support): -```rust -// Chinese keywords -"我喜欢", "我擅长", "我的名字叫", "我是", "我住在" + // Fallback strategies + if final_extracted_facts.is_empty() { + // Try user-focused extraction + let user_messages: Vec<_> = messages.iter().filter(|msg| msg.role == "user").cloned().collect(); + + if !user_messages.is_empty() { + if let Ok(user_facts) = self.fact_extractor.extract_user_facts(&user_messages).await { + if !user_facts.is_empty() { + final_extracted_facts = user_facts; + } + } + } -// English keywords -"i like", "my name", "i live", "i prefer", "i work" -``` + // Individual message extraction + if final_extracted_facts.is_empty() { + let mut single_message_facts = Vec::new(); + for message in messages { + if let Ok(mut facts) = self.fact_extractor.extract_facts_from_text(&message.content).await { + for fact in &mut facts { + fact.source_role = message.role.clone(); + } + single_message_facts.extend(facts); + } + } + if !single_message_facts.is_empty() { + final_extracted_facts = single_message_facts; + } + } -### 3.3 Importance Evaluation Framework + // Ultimate fallback - store raw user content + if final_extracted_facts.is_empty() { + let user_content = messages + .iter() + .filter(|msg| msg.role == "user") + .map(|msg| format!("用户: {}", msg.content)) + .collect::>() + .join("\n"); + + if !user_content.trim().is_empty() { + let memory_id = self.store(user_content.clone(), metadata).await?; + return Ok(vec![/* ... */]); + } + } + } + + // Process each fact through update pipeline + for fact in &final_extracted_facts { + let query_embedding = self.llm_client.embed(&fact.content).await?; + let existing_memories = self.vector_store.search_with_threshold( + &query_embedding, + &filters, + 5, + self.config.search_similarity_threshold, + ).await?; + + let update_result = self.memory_updater.update_memories( + &[fact.clone()], + &existing_memories, + &metadata + ).await?; + + // Apply resulting actions + for action in &update_result.actions_performed { + match action { + MemoryAction::Create { content, metadata } => { + let memory_id = self.store(content.clone(), metadata.clone()).await?; + // Record result + } + MemoryAction::Update { id, content } => { + self.update(id, content.clone()).await?; + // Record result + } + MemoryAction::Merge { target_id, source_ids, merged_content } => { + self.update(target_id, merged_content.clone()).await?; + for source_id in source_ids { + self.vector_store.delete(source_id).await?; + } + // Record result + } + MemoryAction::Delete { id } => { + self.vector_store.delete(id).await?; + // Record result + } + } + } + } +} +``` -Defined in `cortex-mem-core/src/memory/importance.rs`, this system scores memories on a 0-1 scale using three-tiered approach. +Processing includes multiple fallback strategies: +1. Primary fact extraction using dual-channel analysis +2. User-focused extraction as first fallback +3. Individual message processing as second fallback +4. Raw user content storage as ultimate fallback -#### Scoring Criteria: -| Score Range | Category | Characteristics | -|-----------|---------|----------------| -| 0.0-0.2 | Trivial | Small talk, temporary states | -| 0.2-0.4 | Low | Minor preferences, casual mentions | -| 0.4-0.6 | Medium | Useful context, moderate preferences | -| 0.6-0.8 | High | Key facts, strong preferences | -| 0.8-1.0 | Critical | Core identity, essential information | +### 4.3 Search Optimization -#### Evaluation Factors: -- Content length -- Memory type weighting -- Presence of important keywords -- Contextual significance -- Emotional indicators +The search functionality combines semantic similarity with importance weighting: -#### Hybrid Approach: ```rust -fn evaluate_importance(&self, memory: &Memory) -> Result { - let rule_score = self.evaluate_by_content_length(&memory.content); - let keyword_bonus = self.evaluate_by_keywords(&memory.content); - let type_weight = self.evaluate_by_memory_type(&memory.metadata.memory_type); +// In implementation... +async fn search_with_threshold( + &self, + query: &str, + filters: &Filters, + limit: usize, + threshold: f32 +) -> Result> { + let query_embedding = self.llm_client.embed(query).await?; + let similar_memories = self.vector_store.search(&query_embedding, filters, limit).await?; - // Only use LLM evaluation for potentially important memories - if (rule_score + keyword_bonus + type_weight) > 0.5 && self.auto_enhance { - return self.llm_evaluation(memory).await; + // Apply threshold filtering + let filtered_memories: Vec = similar_memories + .into_iter() + .filter(|scored_memory| scored_memory.score >= threshold) + .collect(); + + // Enhance with importance scores + let mut enhanced_results = Vec::new(); + for mut scored_memory in filtered_memories { + let combined_score = self.combine_scores( + scored_memory.score, + scored_memory.memory.metadata.importance_score + ); + scored_memory.score = combined_score; + enhanced_results.push(scored_memory); } - - (rule_score + keyword_bonus + type_weight).min(1.0) + + // Sort by combined score + enhanced_results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap()); + Ok(enhanced_results) } ``` -### 3.4 Update Strategy Engine +Search features: +- Configurable similarity thresholds +- Hybrid scoring combining embedding similarity and importance +- Metadata filtering capabilities +- Ranked results presentation + +### 4.4 Procedural Memory Support -Implemented in `cortex-mem-core/src/memory/updater.rs`, this component makes intelligent decisions about how to handle new information. +Special handling exists for procedural memories that preserve action-result sequences: -#### Action Hierarchy: ```rust -pub enum MemoryAction { - Ignore { reason: String }, // > Priority - Merge { target_id: String }, // - Update { id: String }, // - Create {}, // Lowest priority +async fn create_procedural_memory( + &self, + messages: &[Message], + metadata: MemoryMetadata +) -> Result> { + // Use specialized system prompt for procedural memory + let prompt = format!( + "{}\n\n{}", + PROCEDURAL_MEMORY_SYSTEM_PROMPT, + parse_messages(messages) + ); + + // Extract procedural facts + let procedural_facts = self.fact_extractor.extract_facts_filtered( + messages, + &["user", "assistant"] + ).await?; + + // Create sequential memory entries + let mut results = Vec::new(); + for (i, fact) in procedural_facts.iter().enumerate() { + let mut fact_metadata = metadata.clone(); + fact_metadata.custom.insert( + "sequence_position".to_string(), + serde_json::Value::Number(i.into()) + ); + + let memory_id = self.store(fact.content.clone(), fact_metadata).await?; + results.push(/* ... */); + } + + Ok(results) } ``` -#### Decision Logic: -```text -PREFERENCE HIERARCHY: -- Prefer IGNORE over UPDATE/MERGE to prevent duplication -- Use MERGE for related but redundant facts -- Only CREATE when information is truly unique -- Consider information density: consolidate small related facts -``` +### 4.5 Comprehensive Statistics -#### LLM Prompt Design: -```text -For each fact, decide one of: -3. IGNORE - Redundant or already covered -2. MERGE - Related/complementary information -1. UPDATE - Adds genuinely new substantial information -0. CREATE - Completely novel and valuable -``` +Detailed analytics are available through the `get_stats` method: ---- +```rust +pub async fn get_stats(&self) -> Result { + let all_memories = self.vector_store.list(&Filters::default(), None).await?; + + let stats = MemoryStats { + total_count: all_memories.len(), + by_type: all_memories.iter().fold(HashMap::new(), |mut acc, mem| { + *acc.entry(format!("{:?}", mem.metadata.memory_type)).or_insert(0) += 1; + acc + }), + by_user: all_memories.iter().fold(HashMap::new(), |mut acc, mem| { + if let Some(ref user_id) = mem.metadata.user_id { + *acc.entry(user_id.clone()).or_insert(0) += 1; + } + acc + }), + by_agent: all_memories.iter().fold(HashMap::new(), |mut acc, mem| { + if let Some(ref agent_id) = mem.metadata.agent_id { + *acc.entry(agent_id.clone()).or_insert(0) += 1; + } + acc + }), + average_importance: all_memories.iter() + .map(|m| m.metadata.importance_score) + .sum::() / all_memories.len() as f32, + date_range: if !all_memories.is_empty() { + let mut timestamps: Vec<_> = all_memories.iter() + .map(|m| m.created_at.timestamp()) + .collect(); + timestamps.sort(); + Some((timestamps[0], timestamps[timestamps.len()-1])) + } else { + None + }, + }; + + Ok(stats) +} +``` -## 4. Data Model and Storage +## 5. Data Structures -### 4.1 Core Data Structures (`types.rs`) +### 5.1 Memory Structure -#### Memory Entity: ```rust +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Memory { pub id: String, pub content: String, @@ -265,8 +427,10 @@ pub struct Memory { } ``` -#### Metadata Schema: +### 5.2 Memory Metadata + ```rust +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct MemoryMetadata { pub user_id: Option, pub agent_id: Option, @@ -282,285 +446,285 @@ pub struct MemoryMetadata { } ``` -#### Search Filters: +### 5.3 Memory Types + +```rust +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum MemoryType { + Conversational, + Procedural, + Factual, + Semantic, + Episodic, + Personal, +} +``` + +### 5.4 Search Filters + ```rust +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Filters { pub user_id: Option, pub agent_id: Option, pub run_id: Option, + pub actor_id: Option, + pub memory_type: Option, pub min_importance: Option, pub max_importance: Option, pub created_after: Option>, + pub created_before: Option>, + pub updated_after: Option>, + pub updated_before: Option>, pub entities: Option>, pub topics: Option>, pub custom: HashMap, } ``` -### 4.2 Vector Store Integration - -The Qdrant integration (`qdrant.rs`) provides persistent storage with semantic search capabilities. +## 6. Implementation Details -#### Collection Management: -- Auto-detection of embedding dimensions -- Schema validation and migration -- Index optimization for high-dimensional vectors +### 6.1 Dependency Injection Pattern -#### Query Operations: -- Exact match via UUID -- Semantic similarity search -- Filtered queries by metadata fields -- Batch operations support +The system employs a clean dependency injection pattern where the `MemoryManager` receives external services at construction time and creates specialized processors: -#### Embedding Generation: ```rust -async fn embed(&self, text: &str) -> Result> { - let builder = EmbeddingsBuilder::new(self.embedding_model.clone()) - .document(text) - .map_err(|e| MemoryError::LLM(e.to_string()))?; - - let embeddings = builder.build().await.map_err(|e| MemoryError::LLM(e.to_string()))?; - - Ok(embeddings.first().unwrap().1.vec.iter().map(|&x| x as f32).collect()) +impl MemoryManager { + pub fn new( + vector_store: Box, + llm_client: Box, + config: MemoryConfig, + ) -> Self { + let fact_extractor = create_fact_extractor(dyn_clone::clone_box(llm_client.as_ref())); + let memory_updater = create_memory_updater( + dyn_clone::clone_box(llm_client.as_ref()), + dyn_clone::clone_box(vector_store.as_ref()), + config.similarity_threshold, + config.merge_threshold, + ); + let importance_evaluator = create_importance_evaluator( + dyn_clone::clone_box(llm_client.as_ref()), + config.auto_enhance, + Some(0.5), + ); + let duplicate_detector = create_duplicate_detector( + dyn_clone::clone_box(vector_store.as_ref()), + dyn_clone::clone_box(llm_client.as_ref()), + config.auto_enhance, + config.similarity_threshold, + config.merge_threshold, + ); + let memory_classifier = create_memory_classifier( + dyn_clone::clone_box(llm_client.as_ref()), + config.auto_enhance, + Some(100), + ); + + Self { + vector_store, + llm_client, + config, + fact_extractor, + memory_updater, + importance_evaluator, + duplicate_detector, + memory_classifier, + } + } } ``` ---- - -## 5. Operational Workflows +### 6.2 Factory Functions -### 5.1 Memory Creation Workflow +Processor creation is handled through factory functions that select appropriate implementations based on configuration: -```mermaid -graph TD - A[External Request] --> B{Request Type} - B -->|CRUD Operation| C[MemoryManager] - B -->|Search| D[MemoryManager] - B -->|Batch Operation| E[MemoryManager] - - C --> F[Extract Parameters] - C --> G[Validate Input] - - F --> H{Content Type} - H -->|Simple| I[Create Direct Memory] - H -->|Conversation| J[Extract Facts] - - J --> K[Find Similar Memories] - K --> L[MemoryUpdater Decision] - L --> M{Action} - M -->|CREATE| N[Create New Memory] - M -->|UPDATE| O[Update Existing Memory] - M -->|MERGE| P[Merge Memories] - M -->|IGNORE| Q[Skip Storage] - - I --> R[Generate Embedding] - N --> R - O --> R - P --> R - - R --> S[Enhance Metadata] - S --> T[Store in VectorDB] -``` - -### 5.2 Search Workflow - -```mermaid -flowchart TD - A[User/Application] --> B{Initiate Search} - B --> C[Send Search Request with Query] - C --> D[Generate Query Embedding via LLM] - D --> E[Semantic Search in Qdrant DB] - E --> F[Apply Metadata Filters] - F --> G[Rank by Similarity + Importance] - G --> H[Return Ranked Matching Memories] - H --> A -``` - -#### Ranking Algorithm: -```python -final_score = (similarity_score * 0.7) + (importance_score * 0.3) +```rust +// Example from importance.rs +pub fn create_importance_evaluator( + llm_client: Box, + use_llm: bool, + hybrid_threshold: Option, +) -> Box { + match (use_llm, hybrid_threshold) { + (true, Some(threshold)) => Box::new(HybridImportanceEvaluator::new(llm_client, threshold)), + (true, None) => Box::new(LLMImportanceEvaluator::new(llm_client)), + (false, _) => Box::new(RuleBasedImportanceEvaluator::new()), + } +} ``` -Configurable weights allow tuning precision vs. relevance trade-offs. - ---- - -## 6. Configuration and Extensibility +Similar patterns exist for other processors, allowing runtime selection between LLM-enhanced and rule-based approaches. -### 6.1 Configuration Model +### 6.3 Error Handling Strategy -Centralized configuration via TOML files supports: -- Server settings (host, port) -- Qdrant connection parameters -- LLM provider configuration -- Memory behavior tuning (thresholds, timeouts) -- Optimization strategies +The system implements comprehensive error handling throughout: -Key configuration files identified: -- `config.toml` (main configuration) -- `examples/cortex-mem-evaluation/config/*.toml` (evaluation configurations) - -### 6.2 Extension Points - -#### Pluggable Components: -1. **New Vector Stores**: Implement `VectorStore` trait -2. **Additional LLM Providers**: Implement `LLMClient` trait -3. **Custom Detectors**: Extend optimization detection logic -4. **New Interfaces**: Add gRPC, WebSocket, or other protocols - -#### Trait-Based Design: ```rust -#[async_trait] -pub trait MemoryUpdater: Send + Sync { - async fn update_memories( - &self, - facts: &[ExtractedFact], - existing_memories: &[ScoredMemory], - metadata: &MemoryMetadata, - ) -> Result; +pub async fn create_memory(&self, content: String, metadata: MemoryMetadata) -> Result { + if content.trim().is_empty() { + return Err(MemoryError::Validation( + "Content cannot be empty when creating memory".to_string(), + )); + } + + // ... rest of implementation } ``` -This enables runtime swapping of implementations based on configuration. +Error types include: +- `MemoryError::Validation` - Input validation failures +- `MemoryError::Storage` - Persistent storage issues +- `MemoryError::LLM` - Language model service errors +- `MemoryError::Parse` - Data parsing/formatting errors +- `MemoryError::NotFound` - Resource not found ---- +## 7. Sequence Diagrams -## 7. Quality Assurance and Reliability Features +### 7.1 Memory Addition Flow -### 7.1 Deduplication System - -The `deduplication.rs` module prevents information redundancy through: - -#### Detection Methods: -- **Hash Matching**: SHA-256 comparison for exact duplicates -- **Semantic Similarity**: Cosine similarity on embeddings (>0.8 threshold) -- **Content Overlap**: Jaccard index for word overlap -- **Metadata Alignment**: Entity/topic matching - -#### Merging Logic: -```rust -async fn merge_memories(&self, memories: &[Memory]) -> Result { - let prompt = self.create_merge_prompt(memories); - let merged_content = self.llm_client.complete(&prompt).await?; +```mermaid +sequenceDiagram + participant Client + participant MemoryManager + participant VectorStore + participant LLMClient + participant FactExtractor + participant MemoryUpdater + participant DuplicateDetector + participant MemoryClassifier + participant ImportanceEvaluator - // Preserve highest importance score - let importance_score = memories.iter() - .map(|m| m.metadata.importance_score) - .max_by(|a,b| a.partial_cmp(b).unwrap()) - .unwrap_or(0.5); - - Ok(Memory::new(merged_content, embedding, metadata)) -} + Client->>MemoryManager: add_memory(messages, metadata) + MemoryManager->>FactExtractor: extract_facts(messages) + alt No facts extracted + MemoryManager->>FactExtractor: extract_user_facts(user_messages) + end + MemoryManager->>VectorStore: search(existing_memories) + MemoryManager->>MemoryUpdater: update_memories(facts, existing_memories, metadata) + loop For each action + alt Action == CREATE + MemoryManager->>MemoryManager: create_memory(content, metadata) + MemoryManager->>MemoryClassifier: classify_memory(content) + MemoryManager->>ImportanceEvaluator: evaluate_importance(memory) + MemoryManager->>DuplicateDetector: detect_duplicates(memory) + alt Has duplicates + MemoryManager->>DuplicateDetector: merge_memories(all_memories) + MemoryManager->>VectorStore: insert(merged_memory) + MemoryManager->>VectorStore: delete(duplicate_ids) + else No duplicates + MemoryManager->>VectorStore: insert(new_memory) + end + else Action == UPDATE + MemoryManager->>VectorStore: update(updated_memory) + else Action == MERGE + MemoryManager->>DuplicateDetector: merge_memories(memories) + MemoryManager->>VectorStore: update(merged_memory) + MemoryManager->>VectorStore: delete(source_ids) + else Action == DELETE + MemoryManager->>VectorStore: delete(memory_id) + end + end + MemoryManager->>Client: return actions_performed + + Client->>MemoryManager: store(content, metadata) + MemoryManager->>DuplicateDetector: detect_duplicates(content) + alt Is duplicate + MemoryManager-->>Client: return existing_id + else Not duplicate + MemoryManager->>MemoryManager: create_memory(content, metadata) + MemoryManager->>VectorStore: insert(memory) + MemoryManager-->>Client: return new_id + end + + Client->>MemoryManager: search(query, filters) + MemoryManager->>LLMClient: embed(query) + MemoryManager->>VectorStore: search(query_vector, filters) + MemoryManager->>ImportanceEvaluator: evaluate_importance(memory) + MemoryManager->>MemoryManager: combine_scores(similarity, importance) + MemoryManager->>MemoryManager: sort_results(combined_scores) + MemoryManager-->>Client: return scored_memories ``` -### 7.2 Error Handling and Recovery +## 8. Configuration Options -Robust error handling includes: -- Graceful degradation when LLM services are unavailable -- Retry mechanisms for transient failures -- Comprehensive logging via `tracing` crate -- Health checks for dependent services +The system supports extensive configuration through `MemoryConfig`: -#### Health Check Endpoint: ```rust -pub async fn health_check() -> HealthStatus { - HealthStatus { - overall: vector_store_healthy && llm_service_healthy, - vector_store: vector_store_status, - llm_service: llm_status, - } +pub struct MemoryConfig { + pub auto_enhance: bool, + pub auto_summary_threshold: usize, + pub similarity_threshold: f32, + pub merge_threshold: f32, + pub search_similarity_threshold: f32, + // Additional configuration fields... } ``` ---- - -## 8. Practical Usage Examples +Key parameters: +- `auto_enhance`: Enable/disable LLM-powered metadata enhancement +- `auto_summary_threshold`: Minimum content length for automatic summarization +- `similarity_threshold`: Base threshold for determining memory similarity +- `merge_threshold`: Threshold for automatic memory merging +- `search_similarity_threshold`: Minimum similarity for search results -### 8.1 Creating a Memory via API +## 9. Best Practices and Recommendations -**HTTP Request:** -```http -POST /memories -Content-Type: application/json +### 9.1 Usage Patterns -{ - "content": "I'm a software engineer who loves Rust programming", - "user_id": "user_123", - "memory_type": "personal" -} +#### Direct Memory Storage +For simple content storage without conversation analysis: +```rust +let memory_id = memory_manager.store("User prefers dark mode interface".to_string(), metadata).await?; ``` -**Processing Steps:** -1. Hash generated: `SHA-256("I'm a software engineer...")` -2. Embedding created via LLM -3. Classification: Detected as "Personal" type -4. Entities extracted: ["software engineer", "Rust"] -5. Importance scored: 0.75 (high due to personal preference) -6. Stored in Qdrant with full metadata - -### 8.2 Searching Memories - -**Query:** -```http -POST /memories/search -{ - "query": "What do I like?", - "filters": { - "user_id": "user_123", - "min_importance": 0.5 - } -} -``` +#### Conversation Processing +For extracting meaningful information from dialogues: +```rust +let messages = vec![ + Message { role: "user".to_string(), content: "I really like hiking in the mountains".to_string(), name: None }, + Message { role: "assistant".to_string(), content: "That sounds like a great outdoor activity!".to_string(), name: None }, +]; -**Response:** -```json -{ - "results": [ - { - "memory": { - "id": "mem_abc", - "content": "I love Rust programming", - "metadata": { - "importance_score": 0.75, - "entities": ["Rust"] - } - }, - "score": 0.92 - } - ] -} +let results = memory_manager.add_memory(&messages, metadata).await?; ``` ---- - -## 9. Recommendations for Enhancement +#### Semantic Search +For finding relevant memories: +```rust +let filters = Filters { + user_id: Some("user123".to_string()), + min_importance: Some(0.5), + ..Default::default() +}; -While the current implementation demonstrates strong architectural foundations, several improvements could enhance its capabilities: +let results = memory_manager.search("outdoor activities", &filters, 5).await?; +``` -### 9.1 Immediate Improvements -1. **Add Caching Layer**: Implement Redis cache for frequently accessed memories to reduce LLM/vector DB load -2. **Streaming APIs**: Introduce streaming responses for large result sets and long-running optimizations -3. **Authentication Framework**: Add JWT-based authentication and authorization controls -4. **Enhanced Monitoring**: Integrate Prometheus metrics and OpenTelemetry tracing +### 9.2 Performance Considerations -### 9.2 Long-Term Enhancements -1. **Cross-Memory Reasoning**: Enable inference across multiple memories to derive new insights -2. **Temporal Decay Model**: Implement automatic importance decay over time unless reinforced -3. **Collaborative Filtering**: Allow sharing of anonymized patterns across users for improved recommendations -4. **Multi-modal Support**: Extend to handle image, audio, and video content types +1. **Batch Operations**: Process multiple facts together when possible +2. **Filter Early**: Use metadata filters to reduce search scope +3. **Threshold Tuning**: Adjust similarity thresholds based on use case requirements +4. **Caching**: Consider caching frequent queries or expensive LLM operations +5. **Asynchronous Processing**: Use async methods to avoid blocking -### 9.3 Testing and Validation -1. **Comprehensive Test Suite**: Develop unit and integration tests covering edge cases -2. **Synthetic Dataset Generator**: Create tools for generating realistic test data -3. **Performance Benchmarking**: Establish baseline performance metrics under varying loads -4. **Security Auditing**: Conduct regular security reviews and penetration testing +### 9.3 Integration Guidelines ---- +1. **Error Handling**: Always handle potential `Result` returns appropriately +2. **Resource Management**: Ensure proper cleanup of connections and resources +3. **Configuration Validation**: Validate configuration settings before initialization +4. **Monitoring**: Implement logging and monitoring for critical operations +5. **Testing**: Use mock implementations for unit testing -## Conclusion +## 10. Conclusion -The Memory Management Domain in `cortex-mem` represents a sophisticated, production-ready implementation of AI agent memory systems. Its modular architecture combines cutting-edge technologies—vector databases, large language models, and intelligent automation—to deliver reliable, scalable memory management capabilities. +The Memory Management Domain provides a robust foundation for AI agent memory systems, combining traditional CRUD operations with advanced LLM-powered intelligence. Its modular architecture enables both direct memory manipulation and sophisticated conversation-based knowledge extraction, making it suitable for complex agent workflows. -By leveraging LLM-powered metadata enrichment, smart deduplication, and importance-aware retrieval, the system effectively balances automation with control, making it suitable for both research experimentation and enterprise deployment scenarios. The well-defined interfaces and extensible design ensure adaptability to evolving requirements while maintaining high cohesion within the domain. +Key strengths include: +- **Intelligent Enhancement**: Automatic metadata generation improves searchability +- **Quality Control**: Built-in deduplication maintains memory collection integrity +- **Flexible Access**: Multiple interfaces accommodate different use cases +- **Extensible Design**: Clean abstractions allow for future enhancements +- **Production Ready**: Comprehensive error handling and monitoring capabilities -With continued investment in reliability features, monitoring, and security controls, this foundation can support increasingly complex AI applications requiring robust long-term contextual awareness. \ No newline at end of file +The implementation demonstrates best practices in Rust development, leveraging traits for abstraction, async/await for concurrency, and proper error handling throughout. By following the documented patterns and recommendations, developers can effectively integrate this memory management system into their AI applications. \ No newline at end of file diff --git a/litho.docs/4.Deep-Exploration/Memory Optimization Domain.md b/litho.docs/4.Deep-Exploration/Memory Optimization Domain.md new file mode 100644 index 0000000..dc1dbc1 --- /dev/null +++ b/litho.docs/4.Deep-Exploration/Memory Optimization Domain.md @@ -0,0 +1,280 @@ +# Memory Optimization Domain Technical Documentation + +**Generation Time**: 2024-12-15 09:36:16 UTC +**System Version**: cortex-mem v1.0.0 + +## 1. Overview + +The **Memory Optimization Domain** is a core component of the Cortex-Mem system responsible for maintaining memory quality, efficiency, and relevance through systematic analysis and improvement processes. This domain enables AI agents to maintain high-quality contextual memory by identifying and resolving issues such as duplicates, low-quality entries, outdated information, and inefficient storage patterns. + +The optimization engine follows a comprehensive four-phase process (Detection → Analysis → Execution → Reporting) with progressive status updates from 0% to 100%. It supports both actual execution and dry-run preview modes, ensuring safe operation while providing detailed insights into potential improvements. + +## 2. Architecture Components + +### 2.1 Core Components + +#### 2.1.1 DefaultMemoryOptimizer +The central coordinator implementing the `MemoryOptimizer` trait, responsible for managing the complete optimization workflow: + +```rust +pub struct DefaultMemoryOptimizer { + memory_manager: Arc, + config: OptimizationConfig, + detector: Arc, + analyzer: Arc, + executor: Arc, + reporter: Arc, + running_optimizations: tokio::sync::RwLock>, +} +``` + +This component orchestrates interactions between specialized sub-components using async/await patterns and maintains concurrent optimization task tracking via `RwLock>`. + +#### 2.1.2 OptimizationDetector +Responsible for identifying memory issues based on configurable thresholds: +- **Duplicate detection**: Uses vector similarity threshold (default: 0.85) +- **Quality assessment**: Identifies low-quality memories (threshold: 0.4) +- **Time decay analysis**: Flags potentially outdated entries (>180 days) +- **Classification issues**: Detects poorly categorized memories + +#### 2.1.3 OptimizationAnalyzer +Analyzes detected issues and formulates optimization strategies: +- Evaluates issue severity (Low/Medium/High) +- Generates actionable recommendations +- Creates comprehensive optimization plans +- Supports conservative mode for risk-averse operations + +#### 2.1.4 ExecutionEngine +Executes optimization actions with robust error handling: +- Batch processing support (configurable batch size) +- Concurrent task execution (max_concurrent_tasks: 4) +- Retry mechanism (up to 3 attempts) +- Transactional integrity for memory operations + +#### 2.1.5 ResultReporter +Generates detailed reports and metrics: +- Comprehensive logging with tracing integration +- Performance metrics collection +- Structured result formatting +- Optional file-based logging + +## 3. Key Data Structures + +### 3.1 OptimizationRequest +Defines parameters for optimization operations: + +| Field | Type | Description | +|-------|------|-------------| +| `optimization_id` | Option | Unique identifier for tracking | +| `strategy` | OptimizationStrategy | Selected optimization approach | +| `filters` | OptimizationFilters | Scope constraints | +| `aggressive` | bool | Risk tolerance level | +| `dry_run` | bool | Preview mode flag | +| `timeout_minutes` | Option | Execution time limit | + +### 3.2 OptimizationStrategy +Enumeration of available optimization approaches: + +```rust +enum OptimizationStrategy { + Full, // Comprehensive optimization + Incremental, // Recent changes only + Deduplication, // Focus on duplicate removal + Relevance, // Improve relevance scoring + Quality, // Enhance content quality + Space, // Optimize storage efficiency +} +``` + +### 3.3 OptimizationPlan +Structured representation of proposed optimizations: + +| Field | Description | +|-------|-------------| +| `optimization_id` | Unique plan identifier | +| `strategy` | Applied optimization strategy | +| `created_at` | Timestamp of plan creation | +| `estimated_duration_minutes` | Predicted execution time | +| `issues` | Detected problems list | +| `actions` | Recommended operations | +| `filters` | Applied scope filters | + +### 3.4 OptimizationAction +Available action types in optimization plans: + +- `Merge`: Combine related memories +- `Delete`: Remove redundant entries +- `Update`: Modify existing memory +- `Reclassify`: Change memory type +- `Archive`: Move to archive storage + +## 4. Workflow Process + +### 4.1 Sequence Diagram +```mermaid +sequenceDiagram + participant UI as Optimization UI + participant Store as OptimizationStore + participant Optimizer as MemoryOptimizer + participant Detector as OptimizationDetector + participant Analyzer as OptimizationAnalyzer + participant Executor as ExecutionEngine + participant Reporter as ResultReporter + + UI->>Store: runOptimization() + Store->>Optimizer: optimize(request) + + Optimizer->>Optimizer: Initialize status + Optimizer->>Detector: detect_issues(filters) + Detector-->>Optimizer: issues[] + + Optimizer->>Analyzer: create_optimization_plan(issues, strategy) + Analyzer-->>Optimizer: OptimizationPlan + + alt Dry Run + Optimizer->>Optimizer: create_dry_run_result() + else Actual Execution + Optimizer->>Executor: execute_plan(plan) + Executor-->>Optimizer: OptimizationResult + end + + Optimizer->>Reporter: report_optimization_result(result) + Reporter-->>Optimizer: success + Optimizer-->>Store: result + Store-->>UI: Update UI +``` + +### 4.2 Flowchart +```mermaid +graph TD + A[Start Optimization] --> B{Dry Run?} + B -->|Yes| C[Create Dry Run Result] + B -->|No| D[Execute Plan] + C --> E[Report Result] + D --> E + E --> F[Update Status] + F --> G[Complete] + + subgraph Phases + H[Detection - 20%] + I[Analysis - 40%] + J[Execution - 80%] + K[Reporting - 100%] + end + + A --> H --> I --> J --> K --> G +``` + +## 5. Implementation Details + +### 5.1 Trait-Based Architecture +The system implements a clean separation of concerns through Rust traits: + +```rust +#[async_trait] +pub trait MemoryOptimizer: Send + Sync { + async fn optimize(&self, request: &OptimizationRequest) -> Result; + async fn create_optimization_plan(&self, strategy: OptimizationStrategy) -> Result; + async fn get_optimization_status(&self) -> Result>; + async fn cancel_optimization(&self, optimization_id: &str) -> Result<()>; +} +``` + +### 5.2 Configuration Management +Components use builder patterns with default configurations: + +```rust +// Example configuration structure +struct OptimizationDetectorConfig { + duplicate_threshold: f32, // Default: 0.85 + quality_threshold: f32, // Default: 0.4 + time_decay_days: u32, // Default: 180 + max_issues_per_type: usize, // Default: 1000 +} +``` + +### 5.3 Error Handling and Logging +Comprehensive error handling with structured logging: + +- Custom `Result` types for domain-specific errors +- Tracing integration for detailed diagnostics +- Graceful degradation for partial failures +- Detailed error messages in results + +### 5.4 Concurrency Model +Thread-safe design using Arc-wrapped components: + +```rust +running_optimizations: tokio::sync::RwLock> +``` + +Supports multiple concurrent optimization jobs with proper synchronization. + +## 6. Integration Points + +### 6.1 Internal Dependencies +- **MemoryManager**: Primary interface for memory CRUD operations +- **LLM Integration**: For semantic analysis and similarity assessment +- **Vector Store**: Access to embedding data for duplicate detection + +### 6.2 External Interfaces +- **CLI Interface**: Direct command-line access +- **HTTP API Service**: RESTful programmatic access +- **Web Dashboard**: Visual monitoring and control +- **MCP Protocol**: Agent-to-agent optimization coordination + +## 7. Usage Patterns + +### 7.1 Basic Optimization +```bash +cortex-mem optimize --strategy full --user-id user123 --dry-run +``` + +### 7.2 Programmatic Access +```typescript +const store = createOptimizationStore(); +await store.runOptimization({ + strategy: 'deduplication', + filters: { user_id: 'user123' }, + dry_run: true +}); +``` + +### 7.3 Monitoring Progress +```rust +let status = optimizer.get_optimization_status().await?; +for s in status { + println!("Job {}: {}%", s.id, s.progress); +} +``` + +## 8. Best Practices + +### 8.1 Safety Considerations +- Always use `dry_run` mode first to preview changes +- Implement appropriate backup procedures before major optimizations +- Monitor system performance during intensive operations +- Use incremental strategies for production environments + +### 8.2 Performance Optimization +- Schedule heavy optimizations during off-peak hours +- Use targeted filters to reduce scope +- Adjust batch sizes based on system resources +- Monitor memory usage during execution + +### 8.3 Configuration Guidelines +- Tune duplicate threshold based on use case (0.7-0.9 typical range) +- Adjust quality thresholds according to content standards +- Set appropriate timeout values for long-running operations +- Enable detailed logging for troubleshooting + +## 9. Metrics and Monitoring + +The system provides comprehensive metrics including: +- Issues found by type and severity +- Actions performed count +- Execution duration +- Success/failure rates +- Resource utilization statistics + +These metrics are accessible through both API endpoints and the web dashboard, enabling continuous monitoring of memory health and optimization effectiveness. \ No newline at end of file diff --git a/litho.docs/4.Deep-Exploration/Storage Integration Domain.md b/litho.docs/4.Deep-Exploration/Storage Integration Domain.md new file mode 100644 index 0000000..a984bd6 --- /dev/null +++ b/litho.docs/4.Deep-Exploration/Storage Integration Domain.md @@ -0,0 +1,504 @@ +# Storage Integration Domain Technical Documentation + +## 1. Overview + +The **Storage Integration Domain** is a critical infrastructure component within the Cortex-Mem system, responsible for persistent storage and retrieval of AI agent memories using vector database technology. This domain provides reliable, high-performance access to memory data through vector-based operations, enabling semantic search capabilities that are essential for intelligent agent behavior. + +As a key enabler of the system's core functionality, the Storage Integration Domain handles all interactions with the Qdrant vector database, managing the complete lifecycle of memory records including creation, search, update, and deletion operations. The domain ensures data consistency, schema integrity, and efficient query performance while abstracting the complexities of vector database operations from higher-level business logic components. + +### Key Characteristics +- **Primary Function**: Persistent storage and retrieval of memory vectors and metadata +- **Core Technology**: Qdrant vector database integration +- **Architecture Pattern**: Repository pattern with trait-based abstraction +- **Data Model**: Vector embeddings with rich metadata payload +- **Critical Dependencies**: Configuration Management, LLM Integration domains + +## 2. Architecture and Design + +### 2.1 Component Structure + +The Storage Integration Domain follows a clean separation of concerns between interface abstraction and concrete implementation: + +``` +┌─────────────────┐ ┌──────────────────────────────┐ +│ │ │ │ +│ VectorStore │<───>│ QdrantVectorStore │ +│ (Trait) │ │ (Implementation) │ +│ │ │ │ +└─────────────────┘ └──────────────────────────────┘ + │ + ▼ + ┌─────────────────┐ + │ │ + │ Qdrant DB │ + │ (External) │ + │ │ + └─────────────────┘ +``` + +#### Core Components: +- **`VectorStore` Trait**: Abstract interface defining the contract for vector storage operations +- **`QdrantVectorStore` Struct**: Concrete implementation handling Qdrant-specific operations +- **Qdrant Client**: Underlying Rust client library for Qdrant API communication + +### 2.2 Key Design Principles + +#### Abstraction Through Traits +The domain employs Rust's trait system to define a clear contract for vector storage operations, enabling potential future support for alternative vector databases: + +```rust +#[async_trait] +pub trait VectorStore: Send + Sync + dyn_clone::DynClone { + async fn insert(&self, memory: &Memory) -> Result<()>; + async fn search(&self, query_vector: &[f32], filters: &Filters, limit: usize) -> Result>; + // ... other methods +} +``` + +This design allows the system to maintain flexibility while currently focusing on Qdrant as the primary backend. + +#### Auto-Configuration and Schema Management +The implementation includes sophisticated auto-configuration capabilities that detect embedding dimensions and ensure collection schema compatibility: + +- **Auto-detection**: Dynamically determines embedding dimension by testing with the LLM client +- **Schema Verification**: Validates collection configuration matches expected dimensions +- **Automatic Provisioning**: Creates collections if they don't exist with proper configuration + +#### Type Safety and Error Handling +The domain implements comprehensive error handling through the `MemoryError` enum, providing meaningful error messages for various failure scenarios: + +```rust +#[derive(Error, Debug)] +pub enum MemoryError { + #[error("Vector store error: {0}")] + VectorStore(#[from] qdrant_client::QdrantError), + #[error("Configuration error: {0}")] + Config(String), + #[error("Parse error: {0}")] + Parse(String), +} +``` + +## 3. Technical Implementation Details + +### 3.1 Core Operations + +#### 3.1.1 Insert Operation +The insert operation persists a memory record in the vector database: + +```rust +async fn insert(&self, memory: &Memory) -> Result<()> { + let point = self.memory_to_point(memory); + + let upsert_request = UpsertPoints { + collection_name: self.collection_name.clone(), + points: vec![point], + ..Default::default() + }; + + self.client.upsert_points(upsert_request).await?; + Ok(()) +} +``` + +Key aspects: +- Converts `Memory` objects to Qdrant `PointStruct` format +- Uses upsert semantics to handle both create and update operations +- Includes comprehensive logging for debugging purposes + +#### 3.1.2 Search Operation +The search operation enables semantic retrieval of memories based on vector similarity: + +```rust +async fn search_with_threshold( + &self, + query_vector: &[f32], + filters: &Filters, + limit: usize, + score_threshold: Option, +) -> Result> { + let filter = self.filters_to_qdrant_filter(filters); + + let search_points = SearchPoints { + collection_name: self.collection_name.clone(), + vector: query_vector.to_vec(), + limit: limit as u64, + filter, + with_payload: Some(true.into()), + with_vectors: Some(true.into()), + score_threshold: score_threshold.map(|t| t as f32), + ..Default::default() + }; + + let response = self.client.search_points(search_points).await?; + // Convert results to ScoredMemory objects +} +``` + +Features: +- Supports similarity threshold filtering to return only relevant results +- Combines vector similarity with structured metadata filtering +- Returns scored results ordered by relevance + +### 3.2 Data Mapping and Serialization + +#### 3.2.1 Memory-to-Point Conversion +The domain handles bidirectional conversion between domain objects and database representations: + +```rust +fn memory_to_point(&self, memory: &Memory) -> PointStruct { + let mut payload = HashMap::new(); + + // Basic fields + payload.insert("content".to_string(), memory.content.clone().into()); + payload.insert("created_at".to_string(), memory.created_at.to_rfc3339().into()); + + // Metadata fields + if let Some(user_id) = &memory.metadata.user_id { + payload.insert("user_id".to_string(), user_id.clone().into()); + } + + // Enum types converted to strings + let memory_type_str = format!("{:?}", memory.metadata.memory_type); + payload.insert("memory_type".to_string(), memory_type_str.into()); + + // Complex types serialized to JSON + if !memory.metadata.entities.is_empty() { + let entities_json = serde_json::to_string(&memory.metadata.entities).unwrap_or_default(); + payload.insert("entities".to_string(), entities_json.into()); + } + + PointStruct::new(memory.id.clone(), memory.embedding.clone(), payload) +} +``` + +#### 3.2.2 Payload Structure +The Qdrant payload includes both direct values and serialized complex types: + +| Field | Type | Description | +|-------|------|-------------| +| `content` | string | Original memory text content | +| `created_at` | string (RFC3339) | Creation timestamp | +| `updated_at` | string (RFC3339) | Last update timestamp | +| `user_id`, `agent_id`, etc. | string | Identifier fields | +| `memory_type` | string | Enum value as string | +| `importance_score` | number | Float value for salience | +| `entities`, `topics` | string (JSON) | Serialized arrays | +| `custom_*` | various | Custom metadata fields | + +### 3.3 Filtering System + +The domain implements a comprehensive filtering system that translates application-level filters to Qdrant conditions: + +```rust +fn filters_to_qdrant_filter(&self, filters: &Filters) -> Option { + let mut conditions = Vec::new(); + + // Exact match conditions + if let Some(user_id) = &filters.user_id { + conditions.push(field_condition("user_id", user_id)); + } + + // Range conditions + if let Some(min_importance) = filters.min_importance { + conditions.push(range_condition("importance_score", min_importance as f64, None)); + } + + // Array membership conditions + if let Some(topics) = &filters.topics { + let topic_conditions: Vec = topics.iter() + .map(|topic| field_condition_with_text_match("topics", topic)) + .collect(); + + conditions.push(filter_condition_with_should(topic_conditions)); + } + + if conditions.is_empty() { + None + } else { + Some(Filter { must: conditions, ..Default::default() }) + } +} +``` + +Supported filter types include: +- **Exact matching**: User ID, agent ID, run ID, memory type +- **Range queries**: Importance score, timestamps +- **Array membership**: Entities, topics +- **Custom fields**: Arbitrary key-value pairs with keyword matching + +### 3.4 Health Monitoring + +The domain includes health checking capabilities to monitor database connectivity: + +```rust +async fn health_check(&self) -> Result { + match self.client.health_check().await { + Ok(_) => Ok(true), + Err(e) => { + error!("Qdrant health check failed: {}", e); + Ok(false) + } + } +} +``` + +This allows higher-level components to detect and respond to storage system issues. + +## 4. Configuration and Initialization + +### 4.1 Configuration Parameters + +The domain is configured through the `qdrant` section in `config.toml`: + +```toml +[qdrant] +url = "http://localhost:6334" +collection_name = "cortex-mem-hewlett_drawn" +# embedding_dim = 1024 # Optional, auto-detected if omitted +timeout_secs = 30 +``` + +Key configuration options: +- **url**: Qdrant server endpoint +- **collection_name**: Target collection for memory storage +- **embedding_dim**: Expected embedding dimension (optional) +- **timeout_secs**: Request timeout duration + +### 4.2 Initialization Process + +The initialization process follows this sequence: + +1. **Configuration Loading**: Read Qdrant settings from config file +2. **Client Creation**: Initialize Qdrant client with connection parameters +3. **Dimension Detection**: + - Use configured dimension if specified + - Otherwise, auto-detect by generating test embedding via LLM client +4. **Collection Provisioning**: + - Check if collection exists + - Create with proper dimension and cosine distance metric if missing + - Verify existing collection has compatible schema +5. **Health Verification**: Confirm connectivity before returning instance + +Two factory methods support different initialization scenarios: + +```rust +// Standard initialization with explicit configuration +pub async fn new(config: &QdrantConfig) -> Result + +// Enhanced initialization with auto-detection capabilities +pub async fn new_with_llm_client( + config: &QdrantConfig, + llm_client: &dyn crate::llm::LLMClient, +) -> Result +``` + +## 5. Integration Points + +### 5.1 Upstream Dependencies + +#### Configuration Management Domain +The Storage Integration Domain depends on the Configuration Management Domain for its operational parameters: + +```rust +use crate::config::QdrantConfig; +// ... +let client = Qdrant::from_url(&config.url).build()?; +``` + +This dependency ensures consistent configuration across the system and supports environment-specific deployments. + +#### LLM Integration Domain +During initialization with auto-detection, the domain interacts with the LLM client to determine embedding dimensions: + +```rust +if store.embedding_dim.is_none() { + let test_embedding = llm_client.embed("test").await?; + let detected_dim = test_embedding.len(); + store.embedding_dim = Some(detected_dim); +} +``` + +This tight integration ensures compatibility between the embedding model and vector database schema. + +### 5.2 Downstream Consumers + +#### Memory Management Domain +The primary consumer of the Storage Integration Domain is the Memory Management Domain, which uses it for persistence operations: + +```rust +// In memory manager +self.vector_store.insert(memory).await?; +let results = self.vector_store.search(query_vector, &filters, limit).await?; +``` + +This relationship enables the core memory lifecycle operations while maintaining separation of concerns. + +#### Access Interface Domain +All access interfaces (CLI, HTTP API, MCP) ultimately rely on storage operations for data persistence, creating an indirect dependency chain through the Memory Management Domain. + +## 6. Operational Considerations + +### 6.1 Performance Characteristics + +#### Latency Factors +- **Network latency**: Between application and Qdrant server +- **Vector dimension**: Higher dimensions increase computation time +- **Collection size**: Larger collections may impact search performance +- **Filter complexity**: Complex filters require more processing + +#### Optimization Strategies +- **Indexing**: Qdrant automatically creates indexes for efficient search +- **Batch operations**: Support for bulk inserts/updates +- **Caching**: Potential for higher-level caching of frequent queries +- **Connection pooling**: Reuse of client connections + +### 6.2 Reliability and Resilience + +#### Error Handling +The domain implements robust error handling for various failure scenarios: + +- **Connection failures**: Retry logic with exponential backoff +- **Serialization errors**: Graceful handling of malformed data +- **Schema mismatches**: Clear error messages for dimension conflicts +- **Rate limiting**: Respect for server rate limits + +#### Recovery Procedures +- **Automatic reconnection**: Client handles transient network issues +- **Idempotent operations**: Safe retry of failed operations +- **Health monitoring**: Early detection of service degradation + +### 6.3 Scalability Considerations + +#### Horizontal Scaling +- **Stateless service layer**: Multiple instances can share same database +- **Sharding support**: Qdrant supports collection sharding for large datasets +- **Load balancing**: External load balancers can distribute requests + +#### Capacity Planning +- **Storage requirements**: Approximately 1KB per memory record plus index overhead +- **Memory usage**: Qdrant requires sufficient RAM for vector indexing +- **Compute resources**: CPU-intensive during indexing and search operations + +## 7. Usage Examples + +### 7.1 Basic Operations + +#### Creating a New Instance +```rust +let config = QdrantConfig { + url: "http://localhost:6334".to_string(), + collection_name: "my_memories".to_string(), + embedding_dim: Some(1024), + timeout_secs: 30, +}; + +let store = QdrantVectorStore::new(&config).await?; +``` + +#### Inserting a Memory +```rust +let memory = Memory::new( + "Hello, world!".to_string(), + vec![0.1, 0.2, 0.3], // embedding + MemoryMetadata::new(MemoryType::Conversational), +); + +store.insert(&memory).await?; +``` + +#### Searching Memories +```rust +let query_embedding = vec![0.15, 0.25, 0.35]; // from LLM +let filters = Filters::new().for_user("user123"); + +let results = store + .search(&query_embedding, &filters, 10) + .await?; + +for scored_memory in results { + println!("Score: {}, Content: {}", + scored_memory.score, + scored_memory.memory.content); +} +``` + +### 7.2 Advanced Scenarios + +#### Filtered Semantic Search +```rust +let filters = Filters::new() + .with_memory_type(MemoryType::Factual) + .with_min_importance(0.7) + .with_topics(vec!["science".to_string()]) + .with_custom_field("category", "research"); + +let results = store + .search_with_threshold( + &query_embedding, + &filters, + 20, + Some(0.6) // minimum similarity score + ) + .await?; +``` + +#### Batch Operations +While not explicitly shown in the current implementation, the underlying Qdrant client supports batch operations that could be exposed through enhanced APIs: + +```rust +// Conceptual batch insert +let batch_request = UpsertPoints { + points: memories.iter().map(|m| store.memory_to_point(m)).collect(), + // ... other parameters +}; +``` + +## 8. Future Enhancements + +### 8.1 Planned Improvements + +#### Alternative Backend Support +The trait-based design positions the system well for supporting additional vector databases: + +- **Weaviate**: GraphQL-native vector database +- **Pinecone**: Fully managed vector database service +- **Chroma**: Lightweight, embeddable vector store + +#### Enhanced Query Capabilities +Potential additions include: +- **Hybrid search**: Combining keyword and vector search +- **Faceted navigation**: Aggregation-based exploration +- **Time-series optimizations**: Specialized handling for temporal data + +#### Advanced Management Features +- **Automated compaction**: Periodic optimization of storage +- **Backup/restore**: Comprehensive data protection +- **Migration tools**: Schema evolution support + +### 8.2 Integration Opportunities + +#### Multi-tenancy Support +Enhancements to support isolated storage for multiple tenants: +- **Namespace isolation**: Separate collections per tenant +- **Access control**: Integration with authentication systems +- **Resource quotas**: Per-tenant usage limits + +#### Hybrid Storage Patterns +Combining vector storage with traditional databases: +- **Cold storage**: Archiving older memories to cheaper storage +- **Materialized views**: Pre-computed aggregations for analytics +- **Change data capture**: Real-time synchronization with other systems + +## 9. Summary + +The Storage Integration Domain serves as the persistence backbone of the Cortex-Mem system, providing reliable, high-performance access to AI agent memories through vector database technology. Its thoughtful design balances abstraction with practical implementation details, ensuring both flexibility and performance. + +Key strengths of the current implementation include: +- **Clean separation of concerns** through trait-based abstraction +- **Robust auto-configuration** that handles dimension detection and schema management +- **Comprehensive filtering** that combines vector similarity with structured queries +- **Resilient error handling** with meaningful diagnostics +- **Tight integration** with the broader system architecture + +The domain successfully addresses the core requirements of AI agent memory management while providing a solid foundation for future enhancements and scalability. Its role as the persistent storage layer makes it a critical component in enabling intelligent, context-aware agent behavior across extended interaction sequences. \ No newline at end of file diff --git a/litho.docs/5.Boundary-Interfaces.md b/litho.docs/5.Boundary-Interfaces.md index f49bdbd..5ee7ef7 100644 --- a/litho.docs/5.Boundary-Interfaces.md +++ b/litho.docs/5.Boundary-Interfaces.md @@ -6,497 +6,262 @@ This document describes the system's external invocation interfaces, including C ### cortex-mem-cli -**Description**: Rust Agent Memory System CLI that provides commands for adding, searching, listing, and deleting memories, as well as optimization-related operations. +**Description**: Main CLI interface for memory management operations **Source File**: `cortex-mem-cli/src/main.rs` **Arguments**: -- `config` (PathBuf): optional - Path to the configuration file, defaults to config.toml (default: `config.toml`) - -**Options**: - -- `content, c`(String): required - Content to store as memory -- `user_id, u`(String): optional - User ID for the memory -- `agent_id, a`(String): optional - Agent ID for the memory -- `memory_type, t`(String): optional - Memory type (conversational, procedural, factual) (default: `conversational`) -- `query, q`(String): optional - Search query (optional - if not provided, will use only metadata filters) -- `topics`(Vec): optional - Topics filter (comma-separated) -- `keywords`(Vec): optional - Keywords filter (comma-separated) -- `limit, l`(usize): optional - Maximum number of results (default: `10`) -- `id`(String): required - Memory ID to delete +- `command` (Commands): required - Subcommand to execute (add, delete, list, search) +- `config` (PathBuf): optional - Path to configuration file (default: `config.toml`) **Usage Examples**: ```bash -# Add a new memory\ncortex-mem-cli add --content "Meeting notes" --user-id user123 --memory-type conversational +cortex-mem-cli add --content "Meeting notes" --user-id user123 ``` ```bash -# Search for memories\ncortex-mem-cli search --query "project status" --user-id user123 --limit 5 +cortex-mem-cli search "project timeline" --user-id user123 ``` ```bash -# List all memories for a user\ncortex-mem-cli list --user-id user123 -``` - -```bash -# Delete a memory\ncortex-mem-cli delete mem_abc123 -``` - -```bash -# Optimize memory database\ncortex-mem-cli optimize +cortex-mem-cli list --agent-id agent456 --limit 10 ``` ### cortex-mem-mcp -**Description**: MCP server for Cortex Memo memory management system that serves as an MCP service over stdio with configuration and agent identifier options. +**Description**: MCP server for memory operations over stdio transport **Source File**: `cortex-mem-mcp/src/main.rs` **Arguments**: -- `config` (PathBuf): optional - Path to the configuration file (default: `config.toml`) -- `agent` (String): optional - Agent identifier for memory operations +- `config` (PathBuf): optional - Path to the TOML configuration file (default: `config.toml`) +- `agent` (String): optional - Optional agent identifier used for scoping memory operations **Usage Examples**: ```bash -# Start MCP server with default config\ncortex-mem-mcp +cortex-mem-mcp --config /path/to/config.toml ``` ```bash -# Start MCP server with custom config and agent ID\ncortex-mem-mcp --config /path/to/config.toml --agent my-agent-001 +cortex-mem-mcp --agent agent123 ``` -### cortex-mem-evaluation +### cortex-mem-tars -**Description**: Evaluation framework for assessing Cortex-Mem core capabilities including recall, effectiveness, performance, and dataset generation/validation. +**Description**: Terminal-based interactive AI assistant with memory integration -**Source File**: `examples/cortex-mem-evaluation/src/main.rs` +**Source File**: `examples/cortex-mem-tars/src/main.rs` -**Options**: +**Arguments**: -- `config, c`(PathBuf): optional - Configuration file path (default: `config/evaluation_config.toml`) -- `output_dir, o`(PathBuf): optional - Output directory for results (default: `results`) -- `dataset_type, d`(String): optional - Data set type: recall, effectiveness, all (default: `all`) -- `size, s`(usize): optional - Dataset size (default: `100`) -- `use_lab_data`(bool): optional - Whether to use laboratory data (default: `true`) -- `dataset_path, p`(PathBuf): required - Path to the dataset -- `dataset_type, t`(String): required - Dataset type: recall, effectiveness +- `config` (PathBuf): optional - Configuration file path parameter (default: `config.toml`) **Usage Examples**: ```bash -# Run full evaluation\ncortex-mem-evaluation run --config config/evaluation_config.toml --output-dir results -``` - -```bash -# Run recall evaluation only\ncortex-mem-evaluation recall --config config/evaluation_config.toml -``` - -```bash -# Generate test dataset\ncortex-mem-evaluation generate-dataset --dataset-type all --size 1000 -``` - -```bash -# Validate dataset\ncortex-mem-evaluation validate-dataset --dataset-path data/test_dataset.json --dataset-type recall +cortex-mem-tars --config config.prod.toml ``` ### cortex-mem-service -**Description**: HTTP Service for Rust Agent Memory System that provides RESTful API endpoints for memory management and optimization tasks. +**Description**: HTTP service for memory operations **Source File**: `cortex-mem-service/src/main.rs` -**Arguments**: - -- `config` (PathBuf): optional - Path to the configuration file (default: `config.toml`) - **Usage Examples**: ```bash -# Start HTTP service with default config\ncortex-mem-service -``` - -```bash -# Start HTTP service with custom config\ncortex-mem-service --config /path/to/custom-config.toml -``` - -### multi-round-interactive - -**Description**: Multi-round interactive conversation with a memory-enabled agent using TUI interface, supporting graceful shutdown with background memory persistence. - -**Source File**: `examples/cortex-mem-tars/src/main.rs` - -**Arguments**: - -- `config` (PathBuf): optional - Path to the configuration file (default: `config.toml`) - -**Usage Examples**: - -```bash -# Start interactive session with default config\nmulti-round-interactive -``` - -```bash -# Start interactive session with custom config\nmulti-round-interactive --config /path/to/config.toml +cortex-mem-service ``` ## API Interfaces ### GET /health -**Description**: Health check endpoint to verify system health including vector store and LLM service connectivity. +**Description**: Health check endpoint that verifies the overall system health including vector store and LLM service **Source File**: `cortex-mem-service/src/handlers.rs` -**Response Format**: {\n "status": "healthy" | "unhealthy",\n "vector_store": boolean,\n "llm_service": boolean,\n "timestamp": string\n} +**Response Format**: Json ### POST /memories -**Description**: Create a new memory with content and optional metadata such as user ID, agent ID, and memory type. - -**Source File**: `cortex-mem-service/src/handlers.rs` - -**Request Format**: {\n "content": string,\n "user_id"?: string,\n "agent_id"?: string,\n "run_id"?: string,\n "actor_id"?: string,\n "role"?: string,\n "memory_type"?: string,\n "custom"?: Record\n} - -**Response Format**: {\n "message": string,\n "id": string\n} - -### GET /memories - -**Description**: List memories with optional filtering by user ID, agent ID, run ID, actor ID, memory type, and limit. +**Description**: Creates a new memory with support for both simple content and conversation-based procedural memory **Source File**: `cortex-mem-service/src/handlers.rs` -**Request Format**: ?user_id=string&agent_id=string&run_id=string&actor_id=string&memory_type=string&limit=number - -**Response Format**: {\n "total": number,\n "memories": Array<{\n "id": string,\n "content": string,\n "metadata": {\n "user_id"?: string,\n "agent_id"?: string,\n "run_id"?: string,\n "actor_id"?: string,\n "role"?: string,\n "memory_type": string,\n "hash": string,\n "custom": Record\n },\n "created_at": string,\n "updated_at": string\n }>\n} - -### POST /memories/search +**Request Format**: Json -**Description**: Search memories by semantic similarity using a query string with optional filters and similarity threshold. - -**Source File**: `cortex-mem-service/src/handlers.rs` - -**Request Format**: {\n "query": string,\n "user_id"?: string,\n "agent_id"?: string,\n "run_id"?: string,\n "actor_id"?: string,\n "memory_type"?: string,\n "limit"?: number,\n "similarity_threshold"?: number\n} - -**Response Format**: {\n "total": number,\n "results": Array<{\n "memory": MemoryResponse,\n "score": number\n }>\n} +**Response Format**: Json ### GET /memories/{id} -**Description**: Retrieve a specific memory by its unique identifier. +**Description**: Retrieves a specific memory by its ID **Source File**: `cortex-mem-service/src/handlers.rs` -**Response Format**: {\n "id": string,\n "content": string,\n "metadata": {\n "user_id"?: string,\n "agent_id"?: string,\n "run_id"?: string,\n "actor_id"?: string,\n "role"?: string,\n "memory_type": string,\n "hash": string,\n "custom": Record\n },\n "created_at": string,\n "updated_at": string\n} +**Response Format**: Json ### PUT /memories/{id} -**Description**: Update the content of an existing memory identified by its ID. +**Description**: Updates an existing memory with new content **Source File**: `cortex-mem-service/src/handlers.rs` -**Request Format**: {\n "content": string\n} +**Request Format**: Json -**Response Format**: {\n "message": string,\n "id": string\n} +**Response Format**: Json ### DELETE /memories/{id} -**Description**: Delete a memory by its unique identifier. +**Description**: Deletes a memory by its ID **Source File**: `cortex-mem-service/src/handlers.rs` -**Response Format**: {\n "message": string,\n "id": string\n} - -### POST /optimization - -**Description**: Start a new memory optimization task with optional parameters for memory type, user/agent IDs, and optimization strategy. +**Response Format**: Json -**Source File**: `cortex-mem-service/src/optimization_handlers.rs` +### POST /search -**Request Format**: {\n "memory_type"?: string,\n "user_id"?: string,\n "agent_id"?: string,\n "run_id"?: string,\n "actor_id"?: string,\n "similarity_threshold"?: number,\n "dry_run"?: boolean,\n "verbose"?: boolean,\n "strategy"?: string,\n "aggressive"?: boolean,\n "timeout_minutes"?: number\n} +**Description**: Searches memories using similarity matching with optional filters -**Response Format**: {\n "success": boolean,\n "data"?: any,\n "error"?: {\n "code": string,\n "message": string\n },\n "timestamp": string\n} +**Source File**: `cortex-mem-service/src/handlers.rs` -### GET /optimization/{jobId} +**Request Format**: Json -**Description**: Get the current status of a specific optimization job by its job identifier. +**Response Format**: Json -**Source File**: `cortex-mem-service/src/optimization_handlers.rs` +### GET /memories -**Response Format**: {\n "success": boolean,\n "data"?: {\n "job_id": string,\n "status": string,\n "progress": number,\n "current_phase": string,\n "logs": string[],\n "result"?: OptimizationResult,\n "start_time": string,\n "end_time"?: string,\n "duration"?: number\n },\n "error"?: {\n "code": string,\n "message": string\n },\n "timestamp": string\n} +**Description**: Lists memories with optional filtering and pagination -### POST /optimization/{jobId}/cancel +**Source File**: `cortex-mem-service/src/handlers.rs` -**Description**: Cancel a running optimization job identified by its job ID. +**Request Format**: Query -**Source File**: `cortex-mem-service/src/optimization_handlers.rs` +**Response Format**: Json -**Response Format**: {\n "success": boolean,\n "data"?: {\n "job_id": string,\n "message": string,\n "status": "cancelled",\n "cancelled_at": string\n },\n "error"?: {\n "code": string,\n "message": string\n },\n "timestamp": string\n} +### POST /batch-delete -### GET /optimization/history +**Description**: Performs batch deletion of multiple memories -**Description**: Retrieve historical records of optimization jobs with optional pagination and filtering. +**Source File**: `cortex-mem-service/src/handlers.rs` -**Source File**: `cortex-mem-service/src/optimization_handlers.rs` +**Request Format**: Json -**Request Format**: ?limit=number&offset=number&status=string&start_date=string&end_date=string +**Response Format**: Json -**Response Format**: {\n "success": boolean,\n "data"?: {\n "total": number,\n "history": Array<{\n "job_id": string,\n "status": string,\n "start_time": string,\n "end_time"?: string,\n "duration"?: number,\n "logs_count": number,\n "has_result": boolean\n }>,\n "pagination": {\n "limit": number,\n "offset": number,\n "total": number\n }\n },\n "error"?: {\n "code": string,\n "message": string\n },\n "timestamp": string\n} +### POST /batch-update -### GET /optimization/statistics +**Description**: Performs batch update of multiple memories -**Description**: Get statistical information about optimization jobs including success rates, durations, and processed memories. +**Source File**: `cortex-mem-service/src/handlers.rs` -**Source File**: `cortex-mem-service/src/optimization_handlers.rs` +**Request Format**: Json -**Response Format**: {\n "success": boolean,\n "data"?: {\n "total_jobs": number,\n "successful_jobs": number,\n "failed_jobs": number,\n "cancelled_jobs": number,\n "total_memories_processed": number,\n "avg_duration": number,\n "last_run"?: string\n },\n "error"?: {\n "code": string,\n "message": string\n },\n "timestamp": string\n} +**Response Format**: Json -### POST /optimization/analyze +### POST /optimization -**Description**: Analyze potential optimization opportunities in preview mode without making changes (dry-run). +**Description**: Initiates a new optimization task with the specified parameters **Source File**: `cortex-mem-service/src/optimization_handlers.rs` -**Request Format**: {\n "memory_type"?: string,\n "user_id"?: string,\n "agent_id"?: string,\n "run_id"?: string,\n "actor_id"?: string,\n "similarity_threshold"?: number\n} +**Request Format**: Json -**Response Format**: {\n "success": boolean,\n "data"?: {\n "issues": Array,\n "summary": {\n "total_issues": number,\n "total_affected_memories": number,\n "estimated_savings_mb": number,\n "estimated_duration_minutes": number\n },\n "recommendations": Array<{\n "type": string,\n "action": string,\n "priority": string\n }>\n },\n "error"?: {\n "code": string,\n "message": string\n },\n "timestamp": string\n} +**Response Format**: Json -### POST /optimization/cleanup +### GET /optimization/{job_id} -**Description**: Clean up old optimization history records based on age criteria. +**Description**: Retrieves the current status and progress of a specific optimization job **Source File**: `cortex-mem-service/src/optimization_handlers.rs` -**Request Format**: {\n "max_age_days": number\n} - -**Response Format**: {\n "success": boolean,\n "data"?: {\n "deleted": number,\n "remaining": number,\n "message": string\n },\n "error"?: {\n "code": string,\n "message": string\n },\n "timestamp": string\n} - -### GET /llm/status - -**Description**: Get detailed status of LLM services including completion and embedding model availability and latency. - -**Source File**: `cortex-mem-service/src/handlers.rs` - -**Response Format**: {\n "overall_status": string,\n "completion_model": {\n "available": boolean,\n "provider": string,\n "model_name": string,\n "latency_ms"?: number,\n "error_message"?: string,\n "last_check": string\n },\n "embedding_model": {\n "available": boolean,\n "provider": string,\n "model_name": string,\n "latency_ms"?: number,\n "error_message"?: string,\n "last_check": string\n },\n "timestamp": string\n} +**Response Format**: Json -### GET /llm/health-check +### POST /optimization/{job_id}/cancel -**Description**: Simple health check for LLM service availability. +**Description**: Cancels a running optimization job if it hasn't completed -**Source File**: `cortex-mem-service/src/handlers.rs` +**Source File**: `cortex-mem-service/src/optimization_handlers.rs` -**Response Format**: {\n "completion_model_available": boolean,\n "embedding_model_available": boolean,\n "timestamp": string\n} +**Response Format**: Json ## Router Routes ### / -**Description**: Dashboard page showing overall system status and key metrics. - -**Source File**: `cortex-mem-insights/src/routes/+layout.svelte` - -### /memories - -**Description**: Memory Browser page for viewing, searching, and managing stored memories. +**Description**: Dashboard page component that manages dashboard state, loads data on mount, calculates derived metrics, handles errors with fallback data -**Source File**: `cortex-mem-insights/src/routes/+layout.svelte` - -### /analytics - -**Description**: Analytics page for visualizing memory usage patterns, statistics, and trends. - -**Source File**: `cortex-mem-insights/src/routes/+layout.svelte` +**Source File**: `cortex-mem-insights/src/routes/+page.svelte` ### /optimization -**Description**: Optimization Panel page for managing memory optimization tasks and viewing their status. +**Description**: Optimization dashboard component that manages complete optimization lifecycle (initiation, execution, monitoring), provides UI for strategy selection and configuration -**Source File**: `cortex-mem-insights/src/routes/+layout.svelte` +**Source File**: `cortex-mem-insights/src/routes/optimization/+page.svelte` -### /monitor +### /layout -**Description**: System Monitor page for monitoring real-time system health, performance metrics, and logs. +**Description**: Layout component providing consistent structure across all pages, imports global styles, tracks current route path via $page store **Source File**: `cortex-mem-insights/src/routes/+layout.svelte` ## Integration Suggestions -### CLI - -Integrate CLI tools into automated workflows and scripts for batch memory operations and system maintenance. - -**Example Code**: - -``` -#!/bin/bash\n# Script to regularly optimize memory database\necho "Starting daily memory optimization..."\ncortex-mem-cli optimize --config /etc/cortex-mem/config.toml\necho "Memory optimization completed at $(date)" -``` - -**Best Practices**: - -- Use configuration files instead of command-line arguments for production deployments -- Implement proper error handling when calling CLI tools programmatically -- Schedule optimization tasks during low-usage periods to minimize impact on performance -- Monitor exit codes and log outputs for automated task debugging - ### API -Integrate with the REST API for building custom applications and dashboards that interact with the memory system. +Use the TypeScript client for seamless integration with the Cortex Memory Service API **Example Code**: ``` -import axios from 'axios'; - -// Configure API client -const api = axios.create({ - baseURL: 'http://localhost:3000', - timeout: 10000, -}); - -// Function to search memories -async function searchMemories(query, filters = {}) { - try { - const response = await api.post('/memories/search', { - query, - ...filters - }); - return response.data; - } catch (error) { - console.error('Search failed:', error); - throw error; - } -} - -// Function to create a memory -async function createMemory(content, metadata = {}) { - try { - const response = await api.post('/memories', { - content, - ...metadata - }); - return response.data; - } catch (error) { - console.error('Create memory failed:', error); - throw error; - } -} +import { cortexMemService } from './integrations/cortex-mem';\n\nconst client = new cortexMemService('http://localhost:8080');\nconst response = await client.listMemories({ user_id: 'user123', limit: 10 }); ``` **Best Practices**: -- Use environment variables for API base URLs to support different deployment environments -- Implement retry logic with exponential backoff for transient failures -- Handle authentication if added in future versions -- Validate responses before processing to ensure data integrity -- Monitor API usage and implement rate limiting in client code +- Use environment variables for API endpoints +- Implement proper error handling around API calls +- Cache responses when appropriate to reduce latency +- Use typed interfaces for type safety -### Config +### CLI -Configure the system through TOML configuration files to customize behavior across different environments. +Integrate CLI commands into your deployment pipelines or scripts **Example Code**: ``` -# config.toml -[server] -host = "0.0.0.0" -port = 3000 -cors_origins = ["http://localhost:5173", "https://your-app.com"] - -[qdrant] -url = "http://localhost:6334" -collection_name = "memories" -embedding_dim = 1536 -timeout_secs = 30 - -[llm] -api_base_url = "https://api.openai.com/v1" -api_key = "${LLM_API_KEY}" -model_efficient = "gpt-3.5-turbo" -temperature = 0.7 -max_tokens = 1000 - -[memory] -max_memories = 10000 -similarity_threshold = 0.65 -max_search_results = 50 -auto_summary_threshold = 32768 -auto_enhance = true -deduplicate = true -merge_threshold = 0.75 -search_similarity_threshold = 0.70 - -[logging] -enabled = true -log_directory = "logs" -level = "info" +#!/bin/bash\n# Script to backup memories regularly\nUSER_ID="${1:-user123}"\nBACKUP_DIR="/backups/memories/$(date +%Y%m%d)"\n\nmkdir -p "$BACKUP_DIR"\n\ncortex-mem-cli list --user-id "$USER_ID" --format json > "$BACKUP_DIR/memories.json"\necho "Backup completed: $BACKUP_DIR/memories.json" ``` **Best Practices**: -- Use environment variable substitution in configuration files for sensitive data -- Keep separate configuration files for development, staging, and production environments -- Set appropriate similarity thresholds based on your use case (higher for precision, lower for recall) -- Configure CORS origins properly to allow legitimate frontend applications while preventing unauthorized access -- Regularly review and update configuration settings as system requirements evolve +- Use configuration files for complex setups +- Implement logging in wrapper scripts +- Handle exit codes appropriately +- Use absolute paths in production environments -### Security +### Monitoring -Secure the system against common vulnerabilities by implementing proper authentication, input validation, and secure configuration practices. +Set up monitoring for system health and performance metrics **Example Code**: ``` -# Secure configuration example - -# 1. Use environment variables for secrets -cat > .env << 'EOL' -LLM_API_KEY=sk-your-secret-key-here -DATABASE_PASSWORD=strong-db-password -EOL - -# 2. Update config.toml to use environment variables -[llm] -api_key = "${LLM_API_KEY}" # Will be loaded from environment - -# 3. Set proper file permissions -chmod 600 .env # Read/write for owner only -chmod 644 config.toml # Readable by owner, readable by group/others - -# 4. Example nginx reverse proxy configuration for additional security -server { - listen 80; - server_name cortex-mem.example.com; - - location / { - # Rate limiting: max 100 requests per minute per IP - limit_req zone=api_limit burst=20 nodelay; - - # Forward to backend service - proxy_pass http://localhost:3000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - - # Security headers - add_header X-Frame-Options "DENY"; - add_header X-Content-Type-Options "nosniff"; - add_header Content-Security-Policy "default-src 'self';"; - } -} +import { systemApi } from '$lib/api/client';\n\nsetInterval(async () => {\n try {\n const status = await systemApi.getStatus();\n if (status.overall_status !== 'healthy') {\n // Send alert or notification\n console.error('System health degraded:', status);\n }\n } catch (error) {\n console.error('Failed to check system status:', error);\n }\n}, 30000); // Check every 30 seconds ``` **Best Practices**: -- Never commit API keys or other secrets to version control -- Use HTTPS in production to encrypt data in transit -- Implement rate limiting to prevent abuse -- Validate and sanitize all inputs to prevent injection attacks -- Keep dependencies updated to address known security vulnerabilities -- Use role-based access control when available -- Regularly audit system logs for suspicious activity -- Implement proper CORS policies to restrict origin access +- Monitor both application and system metrics +- Set up alerts for critical failures +- Log monitoring data for trend analysis +- Use exponential backoff for retry logic --- diff --git a/litho.docs/__Litho_Summary_Brief__.md b/litho.docs/__Litho_Summary_Brief__.md index ab89ea9..51ca4d3 100644 --- a/litho.docs/__Litho_Summary_Brief__.md +++ b/litho.docs/__Litho_Summary_Brief__.md @@ -1,24 +1,24 @@ # Project Analysis Brief Report -Generation Time: 2025-12-18 03:35:17 UTC +Generation Time: 2025-12-30 11:34:01 UTC ## Execution Overview -**Total Execution Time**: 1148.89 seconds +**Total Execution Time**: 1761.37 seconds **Phase Timing**: -- Documentation: 531.05s (46.2%) -- Research: 389.49s (33.9%) -- Preprocessing: 228.35s (19.9%) +- Documentation: 1075.94s (61.1%) +- Research: 660.22s (37.5%) +- Preprocessing: 25.21s (1.4%) - Output: 0.00s (0.0%) ## Cache Effectiveness Overview -**Cache Hit Rate**: 82.1% 🟢 Excellent -**Time Saved**: 831.1 seconds -**Tokens Saved**: 249575 input + 108543 output = 358118 total -**Cost Savings**: $0.2010 -**Efficiency Improvement**: 0.7x -**Cost-Benefit**: $0.000175/second +**Cache Hit Rate**: 86.8% 🟢 Excellent +**Time Saved**: 881.0 seconds +**Tokens Saved**: 290514 input + 116766 output = 407280 total +**Cost Savings**: $0.2280 +**Efficiency Improvement**: 0.5x +**Cost-Benefit**: $0.000129/second ## Research Data Overview @@ -33,27 +33,32 @@ Successfully collected four types of research materials according to Prompt temp ## Memory Storage Overview -**Total Storage Size**: 1693607 bytes +**Total Storage Size**: 1652867 bytes **Number of Storage Scopes**: 4 ### Main Storage Distribution (Top 3) -- **preprocess**: 1533048 bytes (90.5%) -- **documentation**: 99706 bytes (5.9%) -- **studies_research**: 60817 bytes (3.6%) +- **preprocess**: 1391134 bytes (84.2%) +- **documentation**: 166050 bytes (10.0%) +- **studies_research**: 95646 bytes (5.8%) ## Document Generation Overview -**Number of Generated Documents**: 5 +**Number of Generated Documents**: 10 **Document Types**: - - Boundary Interfaces - - Architecture Description - Core Workflows - Key Modules and Components Research Report_Memory Management Domain + - Boundary Interfaces - Project Overview + - Key Modules and Components Research Report_Access Interface Domain + - Key Modules and Components Research Report_Memory Optimization Domain + - Key Modules and Components Research Report_Configuration Management Domain + - Key Modules and Components Research Report_Storage Integration Domain + - Architecture Description + - Key Modules and Components Research Report_LLM Integration Domain ## Overall Assessment **Data Completeness**: 100.0% 🟢 Complete -**Cache Efficiency**: 82.1% 🟢 Efficient -**Execution Efficiency**: 1148.89s 🔴 Slow +**Cache Efficiency**: 86.8% 🟢 Efficient +**Execution Efficiency**: 1761.37s 🔴 Slow **Document Generation**: Completed 🟢 Success diff --git a/litho.docs/__Litho_Summary_Detail__.md b/litho.docs/__Litho_Summary_Detail__.md index bcfbb16..d19271d 100644 --- a/litho.docs/__Litho_Summary_Detail__.md +++ b/litho.docs/__Litho_Summary_Detail__.md @@ -1,31 +1,31 @@ # Project Analysis Summary Report (Full Version) -Generation Time: 2025-12-18 03:35:17 UTC +Generation Time: 2025-12-30 11:34:01 UTC ## Execution Timing Statistics -- **Total Execution Time**: 1148.89 seconds -- **Preprocessing Phase**: 228.35 seconds (19.9%) -- **Research Phase**: 389.49 seconds (33.9%) -- **Document Generation Phase**: 531.05 seconds (46.2%) +- **Total Execution Time**: 1761.37 seconds +- **Preprocessing Phase**: 25.21 seconds (1.4%) +- **Research Phase**: 660.22 seconds (37.5%) +- **Document Generation Phase**: 1075.94 seconds (61.1%) - **Output Phase**: 0.00 seconds (0.0%) - **Summary Generation Time**: 0.002 seconds ## Cache Performance Statistics and Savings ### Performance Metrics -- **Cache Hit Rate**: 82.1% -- **Total Operations**: 179 -- **Cache Hits**: 147 times -- **Cache Misses**: 32 times -- **Cache Writes**: 33 times +- **Cache Hit Rate**: 86.8% +- **Total Operations**: 174 +- **Cache Hits**: 151 times +- **Cache Misses**: 23 times +- **Cache Writes**: 24 times ### Savings -- **Inference Time Saved**: 831.1 seconds -- **Tokens Saved**: 249575 input + 108543 output = 358118 total -- **Estimated Cost Savings**: $0.2010 -- **Performance Improvement**: 82.1% -- **Efficiency Improvement Ratio**: 0.7x (saved time / actual execution time) +- **Inference Time Saved**: 881.0 seconds +- **Tokens Saved**: 290514 input + 116766 output = 407280 total +- **Estimated Cost Savings**: $0.2280 +- **Performance Improvement**: 86.8% +- **Efficiency Improvement Ratio**: 0.5x (saved time / actual execution time) ## Core Research Data Summary @@ -36,77 +36,71 @@ Provides core objectives, user roles, and system boundary information for the pr ```json { - "business_value": "Enables AI agents to maintain long-term context and knowledge across interactions, improving their effectiveness, consistency, and intelligence by providing reliable memory management capabilities with optimization, analysis, and visualization tools.", + "business_value": "Enables AI agents to maintain context across interactions through persistent memory storage, retrieval, and optimization capabilities, improving agent intelligence and user experience.", "confidence_score": 0.95, "external_systems": [ { - "description": "Vector database used for storing and retrieving memories using semantic search", + "description": "Vector database used for storing and retrieving memory embeddings", "interaction_type": "Database Storage", "name": "Qdrant" }, { - "description": "External language model APIs used for processing and understanding memory content", + "description": "LLM service used for text generation, embeddings, and memory analysis", "interaction_type": "API Integration", - "name": "LLM Services" + "name": "OpenAI" }, { - "description": "Web browsers and other HTTP clients that interact with the insights dashboard", - "interaction_type": "User Interface", - "name": "HTTP Clients" - }, - { - "description": "Terminal applications that use the CLI tools to interact with the memory system", - "interaction_type": "User Interface", - "name": "Command Line Interface" + "description": "Alternative memory system used for evaluation and comparison purposes", + "interaction_type": "Benchmarking", + "name": "LangMem" } ], - "project_description": "A comprehensive memory management system for AI agents that provides persistent, searchable, and optimizable memory storage with advanced features like semantic search, deduplication, optimization, and analytics.", + "project_description": "A comprehensive memory management system for AI agents that provides persistent, searchable, and optimizable memory storage with multiple access interfaces.", "project_name": "cortex-mem", "project_type": "FullStackApp", "system_boundary": { "excluded_components": [ - "Core LLM model training", - "Vector embedding model development", - "Operating system level resource management", - "Network infrastructure provisioning" + "Core AI agent logic", + "Application-specific business rules", + "User interface design for end applications", + "Network infrastructure management" ], "included_components": [ - "Memory CRUD operations", - "Semantic search with vector embeddings", - "Memory optimization and deduplication", - "Analytics and visualization dashboard", - "REST API service", - "Command-line interface", - "Evaluation framework" - ], - "scope": "The cortex-mem system provides a complete memory management solution for AI agents, including storage, retrieval, optimization, and analysis capabilities." + "Memory storage and retrieval", + "Semantic search capabilities", + "Memory optimization engine", + "Multiple access interfaces (CLI, API, MCP)", + "Web-based monitoring dashboard", + "Configuration management" + ], + "scope": "The cortex-mem system provides memory management capabilities for AI agents through multiple interfaces including CLI, HTTP API, MCP protocol, and web dashboard. It handles memory storage, retrieval, search, and optimization while integrating with external LLM and vector database services." }, "target_users": [ { - "description": "Software engineers building AI-powered applications that require persistent memory capabilities", - "name": "AI Agent Developers", + "description": "Intelligent software agents that require persistent memory to maintain context across conversations and tasks", + "name": "AI Agents", "needs": [ - "Integrate memory management into AI agents", - "Access memories through APIs and CLI", - "Monitor agent memory performance" + "Store conversation history", + "Retrieve relevant memories", + "Optimize memory usage" ] }, { - "description": "Operations personnel managing AI agent infrastructure", - "name": "System Administrators", + "description": "Software engineers integrating memory capabilities into AI applications", + "name": "Developers", "needs": [ - "Monitor system health and status", - "Optimize memory usage", - "Analyze memory patterns and trends" + "API access to memory functions", + "Configuration flexibility", + "Integration with existing systems" ] }, { - "description": "Researchers evaluating AI agent memory systems", - "name": "Research Scientists", + "description": "Operators managing AI agent infrastructure", + "name": "System Administrators", "needs": [ - "Benchmark memory system performance", - "Evaluate recall and effectiveness", - "Generate test datasets for validation" + "Monitoring tools", + "Optimization capabilities", + "Maintenance interfaces" ] } ] @@ -118,116 +112,158 @@ Provides high-level domain division, module relationships, and core business pro ```json { - "architecture_summary": "The cortex-mem system follows a modular, layered architecture with clear separation of concerns. It centers around a core memory management engine (cortex-mem-core) that provides fundamental capabilities for storing, retrieving, and optimizing memories using vector databases and LLMs. This core is exposed through multiple interfaces: a REST API service (cortex-mem-service), a command-line interface (cortex-mem-cli), an MCP protocol adapter (cortex-mem-mcp), and various example applications. A comprehensive insights dashboard (cortex-mem-insights) provides visualization and analytics. The system includes a sophisticated evaluation framework for testing performance and effectiveness. Configuration is centrally managed and supports TOML-based external configuration files.", + "architecture_summary": "The cortex-mem system follows a modular microservices architecture with clear separation of concerns across multiple access interfaces (CLI, HTTP API, MCP) and core memory processing logic. The system integrates external services like Qdrant for vector storage and OpenAI for LLM capabilities while providing multiple entry points for different user types. Frontend and backend components are cleanly separated with TypeScript-based web interface communicating via RESTful APIs to Rust-based backend services. Configuration is centralized and shared across components, enabling consistent behavior across different interfaces.", "business_flows": [ { - "description": "Core process for creating, retrieving, updating, and deleting memories in the system", - "entry_point": "HTTP API endpoint /memories or CLI command 'add'", + "description": "Process for creating new memories through various interfaces, involving content analysis, embedding generation, and storage.", + "entry_point": "User or agent initiates memory creation via CLI command, API call, or MCP tool invocation", "importance": 9.5, "involved_domains_count": 4, - "name": "Memory Management Process", + "name": "Memory Creation Process", "steps": [ { - "code_entry_point": "cortex-mem-service/src/handlers.rs:create_memory or cortex-mem-cli/src/commands/add.rs", - "domain_module": "Memory Management Domain", - "operation": "Receive create memory request via API or CLI", + "code_entry_point": "cortex-mem-cli/src/commands/add.rs", + "domain_module": "Access Interface Domain", + "operation": "Receive memory creation request with content from user", "step": 1, - "sub_module": "Memory Operations" + "sub_module": "CLI Interface" }, { - "code_entry_point": "cortex-mem-core/src/llm/client.rs", - "domain_module": "Memory Storage Domain", - "operation": "Generate embeddings for memory content using LLM client", + "code_entry_point": "cortex-mem-core/src/memory/classification.rs", + "domain_module": "Memory Management Domain", + "operation": "Classify memory type based on content and context", "step": 2, - "sub_module": "Vector Store Integration" + "sub_module": "Memory Types System" }, { - "code_entry_point": "cortex-mem-core/src/vector_store/qdrant.rs", - "domain_module": "Memory Storage Domain", - "operation": "Store memory with embeddings in Qdrant vector database", + "code_entry_point": "cortex-mem-core/src/llm/client.rs", + "domain_module": "LLM Integration Domain", + "operation": "Generate embedding for the memory content using LLM", "step": 3, - "sub_module": "Vector Store Integration" + "sub_module": "LLM Client" + }, + { + "code_entry_point": "cortex-mem-core/src/memory/extractor.rs", + "domain_module": "LLM Integration Domain", + "operation": "Extract facts, entities, and keywords from content", + "step": 4, + "sub_module": "Information Extraction" }, { "code_entry_point": "cortex-mem-core/src/memory/manager.rs", "domain_module": "Memory Management Domain", - "operation": "Return success response with memory ID to client", - "step": 4, - "sub_module": "Memory Lifecycle Management" + "operation": "Store memory with embedding, metadata, and extracted information", + "step": 5, + "sub_module": "Memory CRUD Operations" + }, + { + "code_entry_point": "cortex-mem-core/src/vector_store/qdrant.rs", + "domain_module": "Storage Integration Domain", + "operation": "Persist memory vector in Qdrant database", + "step": 6, + "sub_module": "Vector Store" } ] }, { - "description": "Process for analyzing and improving the quality of stored memories through deduplication, relevance tuning, and quality enhancement", - "entry_point": "HTTP API endpoint /optimization/start or CLI command 'optimize'", - "importance": 8.0, - "involved_domains_count": 4, - "name": "Memory Optimization Process", + "description": "Process for searching and retrieving relevant memories based on query, supporting both semantic and metadata-based search.", + "entry_point": "User or agent requests memory search via CLI, API, or MCP interface", + "importance": 9.0, + "involved_domains_count": 3, + "name": "Memory Retrieval Process", "steps": [ { - "code_entry_point": "cortex-mem-service/src/optimization_handlers.rs:start_optimization or cortex-mem-cli/src/commands/optimize.rs", - "domain_module": "Optimization Domain", - "operation": "Receive optimization request with optional filters and strategy", + "code_entry_point": "cortex-mem-service/src/handlers.rs", + "domain_module": "Access Interface Domain", + "operation": "Receive search request with query and optional filters", "step": 1, - "sub_module": "Optimization Orchestration" + "sub_module": "HTTP API Service" }, { - "code_entry_point": "cortex-mem-core/src/memory/optimization_detector.rs", - "domain_module": "Memory Analysis Domain", - "operation": "Analyze existing memories to detect duplicates, low-quality entries, and relevance issues", + "code_entry_point": "cortex-mem-core/src/llm/client.rs", + "domain_module": "LLM Integration Domain", + "operation": "Generate embedding for the search query", "step": 2, - "sub_module": "Memory Quality Assessment" + "sub_module": "LLM Client" }, { - "code_entry_point": "cortex-mem-core/src/memory/optimizer.rs", - "domain_module": "Optimization Domain", - "operation": "Execute optimization plan using LLM to improve memory quality and remove duplicates", + "code_entry_point": "cortex-mem-core/src/memory/manager.rs", + "domain_module": "Memory Management Domain", + "operation": "Search for similar memories using vector similarity", "step": 3, - "sub_module": "Optimization Execution" + "sub_module": "Semantic Search" }, { - "code_entry_point": "cortex-mem-core/src/memory/result_reporter.rs", - "domain_module": "Optimization Domain", - "operation": "Generate optimization report and return results to client", + "code_entry_point": "cortex-mem-core/src/memory/manager.rs", + "domain_module": "Memory Management Domain", + "operation": "Apply metadata filters to refine results", "step": 4, - "sub_module": "Optimization Reporting" + "sub_module": "Memory CRUD Operations" + }, + { + "code_entry_point": "cortex-mem-core/src/vector_store/qdrant.rs", + "domain_module": "Storage Integration Domain", + "operation": "Retrieve memory details from Qdrant database", + "step": 5, + "sub_module": "Vector Store" + }, + { + "code_entry_point": "cortex-mem-service/src/handlers.rs", + "domain_module": "Access Interface Domain", + "operation": "Return ranked search results to caller", + "step": 6, + "sub_module": "HTTP API Service" } ] }, { - "description": "Process for finding relevant memories based on semantic queries or metadata filters", - "entry_point": "HTTP API endpoint /memories/search or CLI command 'search'", - "importance": 9.0, - "involved_domains_count": 3, - "name": "Memory Search Process", + "description": "Process for analyzing and improving memory collection quality through deduplication, relevance filtering, and other optimization strategies.", + "entry_point": "User initiates optimization via CLI optimize command or web dashboard", + "importance": 8.5, + "involved_domains_count": 4, + "name": "Memory Optimization Process", "steps": [ { - "code_entry_point": "cortex-mem-service/src/handlers.rs:search_memories or cortex-mem-cli/src/commands/search.rs", - "domain_module": "Memory Management Domain", - "operation": "Receive search request with query text and/or metadata filters", + "code_entry_point": "cortex-mem-cli/src/commands/optimize.rs", + "domain_module": "Access Interface Domain", + "operation": "Receive optimization request with strategy and filters", "step": 1, - "sub_module": "Memory Operations" + "sub_module": "CLI Interface" }, { - "code_entry_point": "cortex-mem-core/src/llm/client.rs", - "domain_module": "Memory Storage Domain", - "operation": "Generate embedding for query text using LLM client", + "code_entry_point": "cortex-mem-core/src/memory/optimization_analyzer.rs", + "domain_module": "Memory Optimization Domain", + "operation": "Analyze memory collection for issues like duplicates and low-quality entries", "step": 2, - "sub_module": "Vector Store Integration" + "sub_module": "Optimization Engine" }, { - "code_entry_point": "cortex-mem-core/src/vector_store/qdrant.rs", - "domain_module": "Memory Storage Domain", - "operation": "Perform semantic search in Qdrant vector database using query embedding", + "code_entry_point": "cortex-mem-core/src/memory/updater.rs", + "domain_module": "LLM Integration Domain", + "operation": "Use LLM analysis to identify similar memories for potential merging", "step": 3, - "sub_module": "Vector Store Integration" + "sub_module": "Memory Intelligence" + }, + { + "code_entry_point": "cortex-mem-core/src/memory/optimization_plan.rs", + "domain_module": "Memory Optimization Domain", + "operation": "Generate optimization plan with recommended actions", + "step": 4, + "sub_module": "Optimization Strategies" }, { "code_entry_point": "cortex-mem-core/src/memory/manager.rs", "domain_module": "Memory Management Domain", - "operation": "Apply metadata filters and return ranked list of matching memories", - "step": 4, - "sub_module": "Memory Retrieval" + "operation": "Execute optimization plan by updating, merging, or deleting memories", + "step": 5, + "sub_module": "Memory CRUD Operations" + }, + { + "code_entry_point": "cortex-mem-insights/src/lib/stores/optimization.ts", + "domain_module": "Access Interface Domain", + "operation": "Report optimization results and statistics to user", + "step": 6, + "sub_module": "Optimization UI" } ] } @@ -236,13 +272,11 @@ Provides high-level domain division, module relationships, and core business pro "domain_modules": [ { "code_paths": [ - "cortex-mem-core/src/memory/manager.rs", - "cortex-mem-core/src/memory/updater.rs", - "cortex-mem-core/src/memory/classification.rs", - "cortex-mem-core/src/memory/importance.rs" + "cortex-mem-core/src/memory/", + "cortex-mem-core/src/types.rs" ], - "complexity": 8.0, - "description": "Core domain responsible for managing the lifecycle of memories including creation, retrieval, update, deletion, and basic operations. This domain provides the primary interface for interacting with stored memories and handles the orchestration of memory-related operations.", + "complexity": 9.0, + "description": "Core domain responsible for managing the lifecycle of memories including creation, retrieval, updating, and deletion. This domain handles both direct memory operations and complex scenarios involving semantic search and metadata filtering.", "domain_type": "Core Business Domain", "importance": 9.5, "name": "Memory Management Domain", @@ -251,73 +285,377 @@ Provides high-level domain division, module relationships, and core business pro "code_paths": [ "cortex-mem-core/src/memory/manager.rs", "cortex-mem-service/src/handlers.rs", - "cortex-mem-cli/src/commands/*.rs" + "cortex-mem-cli/src/commands/add.rs", + "cortex-mem-cli/src/commands/delete.rs" ], - "description": "Handles CRUD operations for memories and basic manipulation functions", - "importance": 9.0, + "description": "Handles basic create, read, update, and delete operations for memory entries", + "importance": 9.5, "key_functions": [ "Create memory", - "Retrieve memory by ID", + "Retrieve memory", "Update memory", - "Delete memory", - "List memories with filtering" + "Delete memory" ], - "name": "Memory Operations" + "name": "Memory CRUD Operations" }, { "code_paths": [ "cortex-mem-core/src/memory/manager.rs", - "cortex-mem-core/src/memory/updater.rs" + "cortex-mem-core/src/vector_store/qdrant.rs", + "cortex-mem-cli/src/commands/search.rs" + ], + "description": "Provides semantic search capabilities using vector embeddings and similarity matching", + "importance": 9.0, + "key_functions": [ + "Vector-based search", + "Metadata filtering", + "Relevance scoring" + ], + "name": "Semantic Search" + }, + { + "code_paths": [ + "cortex-mem-core/src/types.rs", + "cortex-mem-core/src/memory/classification.rs" + ], + "description": "Manages classification and handling of different memory types (conversational, procedural, factual, etc.)", + "importance": 8.5, + "key_functions": [ + "Type classification", + "Content parsing", + "Metadata extraction" + ], + "name": "Memory Types System" + } + ] + }, + { + "code_paths": [ + "cortex-mem-cli/", + "cortex-mem-service/", + "cortex-mem-mcp/", + "cortex-mem-insights/" + ], + "complexity": 8.0, + "description": "Provides multiple interfaces for interacting with the memory system, catering to different user types and integration scenarios.", + "domain_type": "Tool Support Domain", + "importance": 8.5, + "name": "Access Interface Domain", + "sub_modules": [ + { + "code_paths": [ + "cortex-mem-cli/src/main.rs", + "cortex-mem-cli/src/commands/" ], - "description": "Manages the full lifecycle of memories from creation to archival or deletion", + "description": "Command-line interface for direct interaction with memory system", "importance": 8.5, "key_functions": [ - "Memory versioning", - "Status tracking", - "Archival policies", + "Command parsing", + "Interactive workflows", "Batch operations" ], - "name": "Memory Lifecycle Management" + "name": "CLI Interface" }, { "code_paths": [ - "cortex-mem-core/src/memory/manager.rs", + "cortex-mem-service/src/main.rs", "cortex-mem-service/src/handlers.rs" ], - "description": "Specialized functionality for retrieving memories based on various criteria", + "description": "RESTful API service for programmatic access to memory functions", + "importance": 9.0, + "key_functions": [ + "Route handling", + "Request validation", + "Response formatting" + ], + "name": "HTTP API Service" + }, + { + "code_paths": [ + "cortex-mem-mcp/src/lib.rs", + "cortex-mem-mcp/src/main.rs" + ], + "description": "Memory Control Protocol interface for AI agent integration", + "importance": 8.5, + "key_functions": [ + "Protocol handling", + "Tool routing", + "Agent communication" + ], + "name": "MCP Protocol Interface" + }, + { + "code_paths": [ + "cortex-mem-insights/src/server/index.ts", + "cortex-mem-insights/src/routes/" + ], + "description": "Web-based interface for monitoring and managing memory system", + "importance": 8.0, + "key_functions": [ + "Data visualization", + "System monitoring", + "User interaction" + ], + "name": "Web Dashboard" + } + ] + }, + { + "code_paths": [ + "cortex-mem-core/src/memory/optimizer.rs", + "cortex-mem-core/src/memory/optimization_*" + ], + "complexity": 8.5, + "description": "Responsible for maintaining memory quality and efficiency through various optimization strategies including deduplication, relevance filtering, and quality improvement.", + "domain_type": "Core Business Domain", + "importance": 9.0, + "name": "Memory Optimization Domain", + "sub_modules": [ + { + "code_paths": [ + "cortex-mem-core/src/memory/optimizer.rs", + "cortex-mem-core/src/memory/optimization_analyzer.rs" + ], + "description": "Core optimization logic that analyzes and improves memory collections", + "importance": 9.0, + "key_functions": [ + "Issue detection", + "Plan generation", + "Execution tracking" + ], + "name": "Optimization Engine" + }, + { + "code_paths": [ + "cortex-mem-core/src/types/optimization.rs", + "cortex-mem-core/src/memory/optimization_plan.rs" + ], + "description": "Implementation of various optimization approaches (full, incremental, deduplication, etc.)", + "importance": 8.5, + "key_functions": [ + "Strategy selection", + "Filter application", + "Action planning" + ], + "name": "Optimization Strategies" + }, + { + "code_paths": [ + "cortex-mem-insights/src/lib/stores/optimization.ts", + "cortex-mem-insights/src/routes/optimization/+page.svelte" + ], + "description": "User interface for managing optimization workflows", + "importance": 8.0, + "key_functions": [ + "Job initiation", + "Progress monitoring", + "Results review" + ], + "name": "Optimization UI" + } + ] + }, + { + "code_paths": [ + "cortex-mem-core/src/llm/" + ], + "complexity": 8.5, + "description": "Manages interaction with Large Language Models for extracting insights from content and making intelligent decisions about memory operations.", + "domain_type": "Core Business Domain", + "importance": 9.0, + "name": "LLM Integration Domain", + "sub_modules": [ + { + "code_paths": [ + "cortex-mem-core/src/llm/client.rs" + ], + "description": "Client for communicating with LLM services", "importance": 9.0, "key_functions": [ - "Get memory by ID", - "List memories with pagination", - "Filter by metadata", - "Batch retrieval" + "Text completion", + "Embedding generation", + "Structured extraction" + ], + "name": "LLM Client" + }, + { + "code_paths": [ + "cortex-mem-core/src/llm/extractor_types.rs", + "cortex-mem-core/src/memory/extractor.rs" + ], + "description": "Extracts structured information from unstructured content", + "importance": 8.5, + "key_functions": [ + "Fact extraction", + "Entity recognition", + "Keyword identification" + ], + "name": "Information Extraction" + }, + { + "code_paths": [ + "cortex-mem-core/src/memory/updater.rs", + "cortex-mem-core/src/memory/prompts.rs" + ], + "description": "Uses LLM analysis to make decisions about memory operations", + "importance": 8.5, + "key_functions": [ + "Content analysis", + "Similarity assessment", + "Operation recommendation" + ], + "name": "Memory Intelligence" + } + ] + }, + { + "code_paths": [ + "cortex-mem-config/src/lib.rs", + "config.toml" + ], + "complexity": 6.5, + "description": "Centralized configuration system that manages settings for all components across the application.", + "domain_type": "Infrastructure Domain", + "importance": 8.0, + "name": "Configuration Management Domain", + "sub_modules": [ + { + "code_paths": [ + "cortex-mem-config/src/lib.rs" + ], + "description": "Defines the schema and structure of configuration data", + "importance": 8.0, + "key_functions": [ + "Type definition", + "Default values", + "Validation" + ], + "name": "Config Structure" + }, + { + "code_paths": [ + "cortex-mem-config/src/lib.rs", + "cortex-mem-core/src/init/mod.rs" + ], + "description": "Loads configuration from files and environment", + "importance": 7.5, + "key_functions": [ + "File parsing", + "Fallback resolution", + "Error handling" + ], + "name": "Config Loading" + }, + { + "code_paths": [ + "examples/lomoco-evaluation/src/cortex_mem/config_utils.py" + ], + "description": "Validates configuration integrity and completeness", + "importance": 7.0, + "key_functions": [ + "Schema checking", + "Required field verification", + "Value validation" + ], + "name": "Config Validation" + } + ] + }, + { + "code_paths": [ + "cortex-mem-core/src/vector_store/" + ], + "complexity": 7.5, + "description": "Handles persistence of memories using vector databases and ensures reliable storage operations.", + "domain_type": "Infrastructure Domain", + "importance": 8.5, + "name": "Storage Integration Domain", + "sub_modules": [ + { + "code_paths": [ + "cortex-mem-core/src/vector_store/qdrant.rs" + ], + "description": "Manages vector-based storage and retrieval", + "importance": 8.5, + "key_functions": [ + "Embedding storage", + "Similarity search", + "Index management" + ], + "name": "Vector Store" + }, + { + "code_paths": [ + "cortex-mem-core/src/vector_store/qdrant.rs" + ], + "description": "Specific implementation for Qdrant vector database", + "importance": 8.0, + "key_functions": [ + "Connection management", + "Collection operations", + "Query execution" ], - "name": "Memory Retrieval" + "name": "Qdrant Integration" } ] } ], "domain_relations": [ { - "description": "Memory Management Domain depends on Memory Storage Domain to persist and retrieve memories from the vector database. The manager components call into vector store implementations to perform actual storage operations.", + "description": "Memory Management relies on Storage Integration for persistent storage of memory vectors and metadata in Qdrant database", "from_domain": "Memory Management Domain", - "relation_type": "Service Dependency", - "strength": 9.5, - "to_domain": "Memory Storage Domain" + "relation_type": "Data Dependency", + "strength": 9.0, + "to_domain": "Storage Integration Domain" }, { - "description": "Optimization processes require access to managed memories through the Memory Management Domain's APIs to analyze and modify existing memory entries.", - "from_domain": "Optimization Domain", + "description": "Memory operations use LLM Integration for content analysis, embedding generation, and intelligent decision-making during memory updates", + "from_domain": "Memory Management Domain", "relation_type": "Service Call", "strength": 8.5, + "to_domain": "LLM Integration Domain" + }, + { + "description": "Optimization processes depend on Memory Management for retrieving memories to analyze and applying changes after optimization", + "from_domain": "Memory Optimization Domain", + "relation_type": "Service Call", + "strength": 9.0, "to_domain": "Memory Management Domain" }, { - "description": "The insights dashboard needs to retrieve memory data from the Memory Management Domain to display in visualizations and analytics views.", - "from_domain": "Insights & Analytics Domain", - "relation_type": "Data Dependency", - "strength": 8.0, + "description": "All access interfaces (CLI, HTTP API, MCP, Web) depend on Memory Management as the primary business logic layer for executing memory operations", + "from_domain": "Access Interface Domain", + "relation_type": "Service Call", + "strength": 9.5, "to_domain": "Memory Management Domain" + }, + { + "description": "LLM client configuration (API keys, endpoints) is provided by the centralized configuration system", + "from_domain": "LLM Integration Domain", + "relation_type": "Configuration Dependency", + "strength": 8.0, + "to_domain": "Configuration Management Domain" + }, + { + "description": "Vector store connection parameters and settings are configured through the centralized configuration system", + "from_domain": "Storage Integration Domain", + "relation_type": "Configuration Dependency", + "strength": 8.5, + "to_domain": "Configuration Management Domain" + }, + { + "description": "Optimization strategies and thresholds can be configured through the configuration system", + "from_domain": "Memory Optimization Domain", + "relation_type": "Configuration Dependency", + "strength": 7.5, + "to_domain": "Configuration Management Domain" + }, + { + "description": "Interface-specific settings like server ports, logging levels, and feature flags are managed through configuration", + "from_domain": "Access Interface Domain", + "relation_type": "Configuration Dependency", + "strength": 8.0, + "to_domain": "Configuration Management Domain" } ] } @@ -329,20 +667,20 @@ Contains static analysis results of the codebase and business process analysis. ```json { "main_workflow": { - "description": "The core workflow for creating, retrieving, updating, and deleting memories in the system. This process begins with a user or application initiating a memory operation through either the HTTP API endpoint or CLI command. The system then processes the request by generating embeddings for the memory content using an LLM client, storing the memory with its embeddings in the Qdrant vector database, and returning a success response with the memory ID to the client.", - "flowchart_mermaid": "graph TD\n A[User/Application] --> B{Initiate Memory Operation}\n B --> C[HTTP API / CLI Command]\n C --> D[Process Request]\n D --> E[Generate Embeddings via LLM Client]\n E --> F[Store in Qdrant Vector Database]\n F --> G[Return Success Response with Memory ID]\n G --> A", - "name": "Memory Management Process" + "description": "The core workflow of the Cortex-Mem system revolves around managing AI agent memories through a comprehensive lifecycle of creation, storage, retrieval, and optimization. This workflow begins when a user or agent initiates memory creation via CLI, API, or MCP interface, triggering content analysis and classification. The system then generates embeddings using an LLM service and extracts structured information like facts, entities, and keywords before storing the memory with its metadata in a Qdrant vector database. For retrieval, users can perform semantic searches by query or use metadata filters, where the system generates an embedding for the search term and finds similar memories based on vector similarity. The workflow also includes periodic optimization processes that analyze memory quality, detect duplicates, and suggest merging or deletion to maintain efficiency.", + "flowchart_mermaid": "graph TD\n A[User/Agent Request] --> B{Operation Type}\n B -->|Create| C[Content Analysis & Classification]\n B -->|Search| D[Generate Query Embedding]\n B -->|Optimize| E[Analyze Memory Collection]\n \n C --> F[Extract Facts, Entities, Keywords]\n F --> G[Generate Memory Embedding]\n G --> H[Store in Qdrant DB]\n \n D --> I[Vector Similarity Search]\n I --> J[Apply Metadata Filters]\n J --> K[Return Ranked Results]\n \n E --> L[Detect Duplicates & Issues]\n L --> M[Generate Optimization Plan]\n M --> N[Execute Merge/Delete Operations]\n \n H --> P((Persistent Storage))\n K --> Q[Display Results]\n N --> R[Update Memory State]\n \n style A fill:#4CAF50,stroke:#388E3C\n style Q fill:#2196F3,stroke:#1976D2\n style P fill:#FF9800,stroke:#F57C00", + "name": "Memory Management Workflow" }, "other_important_workflows": [ { - "description": "A critical workflow for analyzing and improving the quality of stored memories through deduplication, relevance tuning, and quality enhancement. This process starts when a user or system initiates an optimization request with optional filters and strategy parameters. The system analyzes existing memories to detect issues like duplicates and low-quality entries, executes an optimization plan using LLMs to improve memory quality, and generates a comprehensive report of the optimization results.", - "flowchart_mermaid": "graph TD\n A[User/System] --> B{Initiate Optimization}\n B --> C[Send Optimization Request with Filters]\n C --> D[Analyze Memories for Issues]\n D --> E[Detect Duplicates & Quality Problems]\n E --> F[Execute Optimization Plan via LLM]\n F --> G[Generate Optimization Report]\n G --> A", - "name": "Memory Optimization Process" + "description": "This workflow handles the startup and configuration of the Cortex-Mem system across all interfaces. It begins with loading configuration from TOML files, which may include settings for the vector database (Qdrant), LLM services, HTTP server, and logging. The system then initializes tracing for comprehensive logging and creates a MemoryManager instance by auto-detecting the appropriate vector store and LLM client based on the configuration. During initialization, the system can automatically determine embedding dimensions either from existing LLM clients or through dynamic testing. Once initialized, the system exposes its functionality through multiple entry points: a CLI for direct interaction, an HTTP API service for programmatic access, an MCP server for AI agent integration, and a web dashboard for monitoring. Each interface shares the same underlying memory management system but provides tailored experiences for different user types.", + "flowchart_mermaid": "graph TD\n A[Load Configuration] --> B[Initialize Tracing & Logging]\n B --> C{Auto-detect Components}\n C --> D[Detect Vector Store]\n C --> E[Detect LLM Client]\n C --> F[Determine Embedding Dimension]\n \n D --> G[Create MemoryManager]\n E --> G\n F --> G\n \n G --> H[Expose Interfaces]\n H --> I[CLI Interface]\n H --> J[HTTP API Service]\n H --> K[MCP Server]\n H --> L[Web Dashboard]\n \n style A fill:#4CAF50,stroke:#388E3C\n style I fill:#2196F3,stroke:#1976D2\n style J fill:#2196F3,stroke:#1976D2\n style K fill:#2196F3,stroke:#1976D2\n style L fill:#2196F3,stroke:#1976D2", + "name": "System Initialization Workflow" }, { - "description": "The primary workflow for finding relevant memories based on semantic queries or metadata filters. This process begins when a user or application sends a search request containing query text and/or metadata filters. The system generates an embedding for the query text using an LLM client, performs a semantic search in the Qdrant vector database using the query embedding, applies additional metadata filters, and returns a ranked list of matching memories to the client.", - "flowchart_mermaid": "graph TD\n A[User/Application] --> B{Initiate Search}\n B --> C[Send Search Request with Query]\n C --> D[Generate Query Embedding via LLM]\n D --> E[Semantic Search in Qdrant DB]\n E --> F[Apply Metadata Filters]\n F --> G[Return Ranked Matching Memories]\n G --> A", - "name": "Memory Search Process" + "description": "The optimization workflow enables users to improve memory quality and efficiency through systematic analysis and improvement processes. Users initiate this workflow via CLI commands or the web dashboard, specifying optimization strategies such as full, incremental, deduplication, or quality-based optimization. The system first analyzes the memory collection to identify issues like duplicate entries, low-quality memories, or irrelevant content. Using LLM-powered analysis, it assesses similarity between memories based on embeddings and content, generating recommendations for merging related memories or deleting redundant ones. Before execution, the system provides a preview mode that shows the proposed changes and requires user confirmation, ensuring safety. When executed, the optimization process applies the plan by updating, merging, or deleting memories through the MemoryManager, while maintaining detailed logs of all actions taken. The workflow concludes with reporting statistics and results back to the user, including metrics on space reclaimed, duplicates removed, and overall quality improvements.", + "flowchart_mermaid": "graph TD\n A[User Initiates Optimization] --> B[Select Strategy & Filters]\n B --> C[Analyze Memory Collection]\n C --> D[Identify Duplicates & Issues]\n D --> E[Use LLM for Similarity Assessment]\n E --> F[Generate Optimization Plan]\n F --> G[Preview Changes & Confirm]\n G --> H{Execute?}\n H -->|Yes| I[Apply Changes via MemoryManager]\n H -->|No| J[Cancel]\n \n I --> K[Merge Related Memories]\n I --> L[Delete Redundant Entries]\n I --> M[Update Memory Metadata]\n \n K --> N[Log Actions & Metrics]\n L --> N\n M --> N\n \n N --> O[Report Results & Statistics]\n O --> P[Display Optimization Summary]\n \n style A fill:#4CAF50,stroke:#388E3C\n style P fill:#2196F3,stroke:#1976D2", + "name": "Optimization Execution Workflow" } ] } @@ -356,7 +694,7 @@ Code analysis results from preprocessing phase, including definitions of functio { "code_dossier": { "code_purpose": "entry", - "description": "Project execution entry point for the cortex-mem-cli application. Parses CLI arguments, initializes tracing and configuration, creates the memory manager, and dispatches commands.", + "description": "Project execution entry point for Cortex Memory CLI. Parses CLI arguments, initializes the memory system, and routes commands to appropriate handlers.", "file_path": "cortex-mem-cli/src/main.rs", "functions": [ "main", @@ -373,17 +711,17 @@ Code analysis results from preprocessing phase, including definitions of functio "OptimizeCommandRunner" ], "name": "main.rs", - "source_summary": "use clap::{Parser, Subcommand};\nuse cortex_mem_core::{\n config::Config,\n initialize_memory_system,\n memory::MemoryManager,\n};\nuse std::path::PathBuf;\nuse std::sync::Arc;\nuse tokio;\nuse tracing::info;\nuse tracing_subscriber;\n\nmod commands;\n\nuse commands::{\n OptimizeCommand, \n OptimizationStatusCommand, \n OptimizationConfigCommand, \n OptimizeCommandRunner,\n};\nuse commands::add::AddCommand;\nuse commands::delete::DeleteCommand;\nuse commands::list::ListCommand;\nuse commands::search::SearchCommand;\n\n#[derive(Parser)]\n#[command(name = \"cortex-mem-cli\")]\n#[command(about = \"Rust Agent Memory System CLI\")]\npub struct Cli {\n #[command(subcommand)]\n pub command: Commands,\n\n /// Path to the configuration file\n #[arg(short, long, default_value = \"config.toml\")]\n pub config: PathBuf,\n}\n\n#[derive(Subcommand)]\npub enum Commands {\n /// Add a new memory\n Add {\n /// Content to store as memory\n #[arg(short, long)]\n content: String,\n /// User ID for the memory\n #[arg(short, long)]\n user_id: Option,\n /// Agent ID for the memory\n #[arg(short, long)]\n agent_id: Option,\n /// Memory type (conversational, procedural, factual)\n #[arg(short = 't', long, default_value = \"conversational\")]\n memory_type: String,\n },\n /// Search for memories\n Search {\n /// Search query (optional - if not provided, will use only metadata filters)\n #[arg(short, long)]\n query: Option,\n /// User ID filter\n #[arg(short, long)]\n user_id: Option,\n /// Agent ID filter\n #[arg(short, long)]\n agent_id: Option,\n /// Topics filter (comma-separated)\n #[arg(long, value_delimiter = ',')]\n topics: Option>,\n /// Keywords filter (comma-separated)\n #[arg(long, value_delimiter = ',')]\n keywords: Option>,\n /// Maximum number of results\n #[arg(short, long, default_value = \"10\")]\n limit: usize,\n },\n /// List memories\n List {\n /// User ID filter\n #[arg(short, long)]\n user_id: Option,\n /// Agent ID filter\n #[arg(short, long)]\n agent_id: Option,\n /// Memory type filter\n #[arg(short = 't', long)]\n memory_type: Option,\n /// Topics filter (comma-separated)\n #[arg(long, value_delimiter = ',')]\n topics: Option>,\n /// Keywords filter (comma-separated)\n #[arg(long, value_delimiter = ',')]\n keywords: Option>,\n /// Maximum number of results\n #[arg(short, long, default_value = \"20\")]\n limit: usize,\n },\n /// Delete a memory by ID\n Delete {\n /// Memory ID to delete\n id: String,\n },\n /// Optimize memory database\n Optimize {\n #[command(flatten)]\n cmd: OptimizeCommand,\n },\n /// Show optimization status\n OptimizeStatus {\n #[command(flatten)]\n cmd: OptimizationStatusCommand,\n },\n /// Manage optimization configuration\n OptimizeConfig {\n #[command(flatten)]\n cmd: OptimizationConfigCommand,\n },\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n // Initialize tracing\n tracing_subscriber::fmt::init();\n\n let cli = Cli::parse();\n\n // Load configuration from file\n let config = Config::load(&cli.config)?;\n\n // Create memory manager\n let memory_manager = create_memory_manager(&config).await?;\n\n // Execute command\n match cli.command {\n Commands::Add {\n content,\n user_id,\n agent_id,\n memory_type,\n } => {\n let cmd = AddCommand::new(memory_manager);\n cmd.execute(content, user_id, agent_id, memory_type).await?;\n }\n Commands::Search {\n query,\n user_id,\n agent_id,\n topics,\n keywords,\n limit,\n } => {\n let cmd = SearchCommand::new(memory_manager);\n cmd.execute(query, user_id, agent_id, topics, keywords, limit).await?;\n }\n Commands::List {\n user_id,\n agent_id,\n memory_type,\n topics,\n keywords,\n limit,\n } => {\n let cmd = ListCommand::new(memory_manager);\n cmd.execute(user_id, agent_id, memory_type, topics, keywords, limit).await?;\n }\n Commands::Delete { id } => {\n let cmd = DeleteCommand::new(memory_manager);\n cmd.execute(id).await?;\n }\n Commands::Optimize { cmd } => {\n let runner = OptimizeCommandRunner::new(Arc::new(memory_manager), config);\n runner.run_optimize(&cmd).await?;\n }\n Commands::OptimizeStatus { cmd } => {\n let runner = OptimizeCommandRunner::new(Arc::new(memory_manager), config);\n runner.run_status(&cmd).await?;\n }\n Commands::OptimizeConfig { cmd } => {\n let runner = OptimizeCommandRunner::new(Arc::new(memory_manager), config);\n runner.run_config(&cmd).await?;\n }\n }\n\n Ok(())\n}\n\nasync fn create_memory_manager(\n config: &Config,\n) -> Result> {\n // Use the new initialization system with auto-detection\n let (vector_store, llm_client) = initialize_memory_system(config).await?;\n\n // Create memory manager\n let memory_manager = MemoryManager::new(vector_store, llm_client, config.memory.clone());\n\n info!(\"Memory manager initialized successfully with auto-detected embedding dimensions\");\n Ok(memory_manager)\n}\n" + "source_summary": "use clap::{Parser, Subcommand};\nuse cortex_mem_core::{\n config::Config,\n initialize_memory_system,\n memory::MemoryManager,\n};\nuse std::path::PathBuf;\nuse std::sync::Arc;\nuse tokio;\nuse tracing::info;\nuse tracing_subscriber;\n\nmod commands;\n\nuse commands::{\n OptimizeCommand,\n OptimizationStatusCommand,\n OptimizationConfigCommand,\n OptimizeCommandRunner,\n};\nuse commands::add::AddCommand;\nuse commands::delete::DeleteCommand;\nuse commands::list::ListCommand;\nuse commands::search::SearchCommand;\n\n#[derive(Parser)]\n#[command(name = \"cortex-mem-cli\")]\n#[command(about = \"Cortex Memory CLI for Agent Memory Layer\")]\n#[command(author = \"Sopaco\")]\n#[command(version)]\npub struct Cli {\n #[command(subcommand)]\n pub command: Commands,\n\n /// Path to the configuration file\n #[arg(short, long, default_value = \"config.toml\")]\n pub config: PathBuf,\n}\n\n#[derive(Subcommand)]\npub enum Commands {\n /// Add a new memory\n Add {\n /// Content to store as memory\n #[arg(short, long)]\n content: String,\n /// User ID for the memory\n #[arg(short, long)]\n user_id: Option,\n /// Agent ID for the memory\n #[arg(short, long)]\n agent_id: Option,\n /// Memory type (conversational, procedural, factual)\n #[arg(short = 't', long, default_value = \"conversational\")]\n memory_type: String,\n },\n /// Search for memories\n Search {\n /// Search query (optional - if not provided, will use only metadata filters)\n #[arg(short, long)]\n query: Option,\n /// User ID filter\n #[arg(short, long)]\n user_id: Option,\n /// Agent ID filter\n #[arg(short, long)]\n agent_id: Option,\n /// Topics filter (comma-separated)\n #[arg(long, value_delimiter = ',')]\n topics: Option>,\n /// Keywords filter (comma-separated)\n #[arg(long, value_delimiter = ',')]\n keywords: Option>,\n /// Maximum number of results\n #[arg(short, long, default_value = \"10\")]\n limit: usize,\n },\n /// List memories\n List {\n /// User ID filter\n #[arg(short, long)]\n user_id: Option,\n /// Agent ID filter\n #[arg(short, long)]\n agent_id: Option,\n /// Memory type filter\n #[arg(short = 't', long)]\n memory_type: Option,\n /// Topics filter (comma-separated)\n #[arg(long, value_delimiter = ',')]\n topics: Option>,\n /// Keywords filter (comma-separated)\n #[arg(long, value_delimiter = ',')]\n keywords: Option>,\n /// Maximum number of results\n #[arg(short, long, default_value = \"20\")]\n limit: usize,\n },\n /// Delete a memory by ID\n Delete {\n /// Memory ID to delete\n id: String,\n },\n /// Optimize memory database\n Optimize {\n #[command(flatten)]\n cmd: OptimizeCommand,\n },\n /// Show optimization status\n OptimizeStatus {\n #[command(flatten)]\n cmd: OptimizationStatusCommand,\n },\n /// Manage optimization configuration\n OptimizeConfig {\n #[command(flatten)]\n cmd: OptimizationConfigCommand,\n },\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n // Initialize tracing\n tracing_subscriber::fmt::init();\n\n let cli = Cli::parse();\n\n // Load configuration from file\n let config = Config::load(&cli.config)?;\n\n // Create memory manager\n let memory_manager = create_memory_manager(&config).await?;\n\n // Execute command\n match cli.command {\n Commands::Add {\n content,\n user_id,\n agent_id,\n memory_type,\n } => {\n let cmd = AddCommand::new(memory_manager);\n cmd.execute(content, user_id, agent_id, memory_type).await?;\n }\n Commands::Search {\n query,\n user_id,\n agent_id,\n topics,\n keywords,\n limit,\n } => {\n let cmd = SearchCommand::new(memory_manager);\n cmd.execute(query, user_id, agent_id, topics, keywords, limit).await?;\n }\n Commands::List {\n user_id,\n agent_id,\n memory_type,\n topics,\n keywords,\n limit,\n } => {\n let cmd = ListCommand::new(memory_manager);\n cmd.execute(user_id, agent_id, memory_type, topics, keywords, limit).await?;\n }\n Commands::Delete { id } => {\n let cmd = DeleteCommand::new(memory_manager);\n cmd.execute(id).await?;\n }\n Commands::Optimize { cmd } => {\n let runner = OptimizeCommandRunner::new(Arc::new(memory_manager), config);\n runner.run_optimize(&cmd).await?;\n }\n Commands::OptimizeStatus { cmd } => {\n let runner = OptimizeCommandRunner::new(Arc::new(memory_manager), config);\n runner.run_status(&cmd).await?;\n }\n Commands::OptimizeConfig { cmd } => {\n let runner = OptimizeCommandRunner::new(Arc::new(memory_manager), config);\n runner.run_config(&cmd).await?;\n }\n }\n\n Ok(())\n}\n\nasync fn create_memory_manager(\n config: &Config,\n) -> Result> {\n // Use the new initialization system with auto-detection\n let (vector_store, llm_client) = initialize_memory_system(config).await?;\n\n // Create memory manager\n let memory_manager = MemoryManager::new(vector_store, llm_client, config.memory.clone());\n\n info!(\"Memory manager initialized successfully with auto-detected embedding dimensions\");\n Ok(memory_manager)\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 6.0, - "lines_of_code": 197, + "cyclomatic_complexity": 7.0, + "lines_of_code": 199, "number_of_classes": 2, "number_of_functions": 2 }, "dependencies": [ { - "dependency_type": "use", + "dependency_type": "library", "is_external": true, "line_number": 1, "name": "clap", @@ -391,101 +729,109 @@ Code analysis results from preprocessing phase, including definitions of functio "version": null }, { - "dependency_type": "use", - "is_external": true, + "dependency_type": "library", + "is_external": false, "line_number": 2, "name": "cortex_mem_core", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "std", "is_external": false, - "line_number": 4, + "line_number": 3, "name": "std::path::PathBuf", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "std", "is_external": false, - "line_number": 5, + "line_number": 4, "name": "std::sync::Arc", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "library", "is_external": true, - "line_number": 6, + "line_number": 5, "name": "tokio", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "library", "is_external": true, - "line_number": 7, + "line_number": 6, "name": "tracing", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "library", "is_external": true, - "line_number": 8, + "line_number": 7, "name": "tracing_subscriber", "path": null, "version": null }, { - "dependency_type": "mod", + "dependency_type": "module", "is_external": false, - "line_number": 10, + "line_number": 9, "name": "commands", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "commands::add::AddCommand", + "path": null, + "version": null + }, + { + "dependency_type": "module", "is_external": false, "line_number": 12, - "name": "commands::OptimizeCommand", + "name": "commands::delete::DeleteCommand", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "module", "is_external": false, - "line_number": 15, - "name": "commands::OptimizeCommandRunner", + "line_number": 13, + "name": "commands::list::ListCommand", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "module", "is_external": false, - "line_number": 16, - "name": "commands::add::AddCommand", + "line_number": 14, + "name": "commands::search::SearchCommand", "path": null, "version": null } ], - "detailed_description": "This component serves as the entry point for the cortex-mem-cli application, implementing a command-line interface for interacting with a memory management system. It uses clap for argument parsing, defining a comprehensive set of subcommands for adding, searching, listing, and deleting memories, as well as optimization-related operations. The main function initializes tracing with tracing_subscriber, parses command-line arguments, loads configuration from a file, creates a MemoryManager instance via the core library's initialization system, and then dispatches to the appropriate command handler based on the user's input. The create_memory_manager function orchestrates the initialization of the memory system by leveraging the initialize_memory_system function from the core library, which auto-detects vector store and LLM client configurations. The component follows a clean separation of concerns by delegating command implementation to separate modules (add, delete, list, search, optimize) while maintaining the command dispatching logic centrally. It uses async/await for all operations, indicating that the underlying memory operations are non-blocking and likely involve I/O operations such as database access or network calls to LLM services.", + "detailed_description": "This component serves as the entry point for the Cortex Memory CLI application. It uses the Clap library to define and parse command-line arguments and subcommands for managing agent memories. The CLI supports operations including adding, searching, listing, and deleting memories, as well as optimization-related functions (optimize, status, config). Upon startup, it initializes tracing for logging, loads configuration from a TOML file, and creates a MemoryManager instance using an auto-detected vector store and LLM client. The main function routes each command to its respective handler, which are implemented in separate modules. The create_memory_manager function abstracts the initialization logic, promoting clean separation between setup and execution. All command executions are asynchronous, leveraging Tokio for runtime management.", "interfaces": [ { - "description": "Main CLI argument structure that contains the command to execute and global options like config file path", + "description": "Top-level CLI argument parser containing global options and subcommands", "interface_type": "struct", "name": "Cli", "parameters": [ { - "description": "The subcommand to execute", + "description": "Subcommand to execute", "is_optional": false, "name": "command", "param_type": "Commands" }, { - "description": "Path to the configuration file, defaults to config.toml", + "description": "Path to configuration file, defaulting to config.toml", "is_optional": false, "name": "config", "param_type": "PathBuf" @@ -495,7 +841,7 @@ Code analysis results from preprocessing phase, including definitions of functio "visibility": "pub" }, { - "description": "Enumeration of all available subcommands in the CLI: Add, Search, List, Delete, Optimize, OptimizeStatus, OptimizeConfig", + "description": "Enumeration of all available CLI subcommands with their specific arguments", "interface_type": "enum", "name": "Commands", "parameters": [], @@ -503,7 +849,7 @@ Code analysis results from preprocessing phase, including definitions of functio "visibility": "pub" }, { - "description": "Entry point of the application that initializes the system and dispatches commands", + "description": "Entry point of the application that orchestrates initialization and command execution", "interface_type": "function", "name": "main", "parameters": [], @@ -512,17 +858,17 @@ Code analysis results from preprocessing phase, including definitions of functio } ], "responsibilities": [ - "Parse command-line arguments and subcommands using clap", - "Initialize application logging and tracing infrastructure", - "Load configuration from external files and pass to system components", - "Create and manage the lifecycle of the MemoryManager instance", - "Dispatch parsed commands to appropriate command handlers" + "Parse and validate command-line arguments using Clap", + "Initialize application logging and configuration subsystems", + "Bootstrap the memory management system with auto-detected components", + "Route CLI commands to their respective handler implementations", + "Manage lifecycle of asynchronous operations through Tokio runtime" ] }, { "code_dossier": { "code_purpose": "entry", - "description": "Entry point for the Cortex Memo MCP (Memory Control Protocol) server. Parses command-line arguments, initializes logging, constructs the MemoryMcpService with configuration, and serves it over stdio transport.", + "description": "Project execution entry point for Cortex Memory MCP Server, responsible for initializing configuration, logging, and starting the MCP service via stdio transport.", "file_path": "cortex-mem-mcp/src/main.rs", "functions": [ "main" @@ -533,11 +879,11 @@ Code analysis results from preprocessing phase, including definitions of functio "main" ], "name": "main.rs", - "source_summary": "use anyhow::anyhow;\nuse clap::Parser;\nuse cortex_mem_mcp::MemoryMcpService;\nuse rmcp::{transport::stdio, ServiceExt};\nuse std::path::PathBuf;\nuse tracing::{error, info};\n\n#[derive(Parser)]\n#[command(name = \"cortex-mem-mcp\")]\n#[command(about = \"MCP server for Cortex Memo memory management system\")]\nstruct Cli {\n /// Path to the configuration file\n #[arg(short, long, default_value = \"config.toml\")]\n config: PathBuf,\n\n /// Agent identifier for memory operations\n #[arg(long)]\n agent: Option,\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let cli = Cli::parse();\n\n // Initialize logging\n tracing_subscriber::fmt()\n .with_max_level(tracing::Level::INFO)\n .init();\n\n info!(\"Starting Cortex Memo MCP Server\");\n info!(\"Using configuration file: {:?}\", cli.config);\n\n // Create the service\n let service = MemoryMcpService::with_config_path_and_agent(cli.config, cli.agent)\n .await\n .map_err(|e| anyhow!(\"Failed to initialize memory management service: {}\", e))?;\n\n // Serve the MCP service\n let running_service = service\n .serve(stdio())\n .await\n .map_err(|e| anyhow!(\"Failed to start MCP server: {}\", e))?;\n\n info!(\"MCP server initialized successfully\");\n\n // Wait for the server to finish\n match running_service.waiting().await {\n Ok(reason) => info!(\"Server shutdown: {:?}\", reason),\n Err(e) => error!(\"Server error: {:?}\", e),\n }\n\n Ok(())\n}\n" + "source_summary": "use anyhow::anyhow;\nuse clap::Parser;\nuse cortex_mem_mcp::MemoryMcpService;\nuse rmcp::{transport::stdio, ServiceExt};\nuse std::path::PathBuf;\nuse tracing::{error, info};\n\n#[derive(Parser)]\n#[command(name = \"cortex-mem-mcp\")]\n#[command(about = \"MCP server of Cortex Memory to enhance agent's memory layer\")]\n#[command(author = \"Sopaco\")]\n#[command(version)]\nstruct Cli {\n /// Path to the configuration file\n #[arg(short, long, default_value = \"config.toml\")]\n config: PathBuf,\n\n /// Agent identifier for memory operations\n #[arg(long)]\n agent: Option,\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let cli = Cli::parse();\n\n // Initialize logging\n tracing_subscriber::fmt()\n .with_max_level(tracing::Level::INFO)\n .init();\n\n info!(\"Starting Cortex Memo MCP Server\");\n info!(\"Using configuration file: {:?}\", cli.config);\n\n // Create the service\n let service = MemoryMcpService::with_config_path_and_agent(cli.config, cli.agent)\n .await\n .map_err(|e| anyhow!(\"Failed to initialize memory management service: {}\", e))?;\n\n // Serve the MCP service\n let running_service = service\n .serve(stdio())\n .await\n .map_err(|e| anyhow!(\"Failed to start MCP server: {}\", e))?;\n\n info!(\"MCP server initialized successfully\");\n\n // Wait for the server to finish\n match running_service.waiting().await {\n Ok(reason) => info!(\"Server shutdown: {:?}\", reason),\n Err(e) => error!(\"Server error: {:?}\", e),\n }\n\n Ok(())\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 5.0, - "lines_of_code": 53, + "cyclomatic_complexity": 4.0, + "lines_of_code": 55, "number_of_classes": 1, "number_of_functions": 1 }, @@ -567,19 +913,19 @@ Code analysis results from preprocessing phase, including definitions of functio "version": null }, { - "dependency_type": "protocol framework", + "dependency_type": "mcp framework", "is_external": true, "line_number": 4, "name": "rmcp", - "path": "rmcp::transport::stdio", + "path": "rmcp::{transport::stdio, ServiceExt}", "version": null }, { "dependency_type": "standard library", "is_external": false, "line_number": 5, - "name": "std", - "path": "std::path::PathBuf", + "name": "std::path::PathBuf", + "path": null, "version": null }, { @@ -589,28 +935,33 @@ Code analysis results from preprocessing phase, including definitions of functio "name": "tracing", "path": null, "version": null - }, - { - "dependency_type": "async runtime", - "is_external": true, - "line_number": 19, - "name": "tokio", - "path": null, - "version": null } ], - "detailed_description": "The main.rs file serves as the entry point for the Cortex Memo MCP server application. It utilizes the `clap` library for parsing command-line arguments, defining options for a configuration file path and an optional agent identifier. The `tracing` and `tracing_subscriber` crates are used to set up structured logging with an INFO level default. The core logic involves parsing the CLI arguments, initializing the logging system, creating an instance of `MemoryMcpService` by providing the configuration path and agent ID, and then serving this service using the `rmcp` framework's stdio transport. The server runs asynchronously using the `tokio` runtime, and the main function waits for the server to terminate, logging the reason for shutdown, whether it was a clean stop or an error.", + "detailed_description": "The main.rs file serves as the entry point for the Cortex Memory MCP (Memory Control Protocol) server. It uses Clap for command-line argument parsing, allowing users to specify a configuration file path and an optional agent identifier. The component initializes tracing-based logging with INFO level, constructs a MemoryMcpService instance using the provided configuration and agent ID, and then serves this service over stdio transport using the rmcp framework. Once the server is running, it awaits termination, logging shutdown reasons or errors accordingly. This module acts as a bootstrap for the entire memory enhancement service for AI agents, orchestrating setup and execution flow without containing business logic itself.", "interfaces": [ { - "description": "Command-line interface argument parser defined using clap. It configures the binary name and description, and specifies two arguments: a config file path (with a default) and an optional agent identifier.", + "description": "Command-line interface argument parser defining configuration file path and optional agent identifier", "interface_type": "struct", "name": "Cli", - "parameters": [], + "parameters": [ + { + "description": "Path to the TOML configuration file, defaults to 'config.toml'", + "is_optional": false, + "name": "config", + "param_type": "PathBuf" + }, + { + "description": "Optional agent identifier used for scoping memory operations", + "is_optional": true, + "name": "agent", + "param_type": "Option" + } + ], "return_type": null, "visibility": "private" }, { - "description": "The asynchronous entry point of the application. It orchestrates the startup sequence: argument parsing, logging setup, service creation, server execution, and shutdown handling.", + "description": "Asynchronous entry point that parses CLI args, initializes logging, creates and runs the MCP service", "interface_type": "function", "name": "main", "parameters": [], @@ -619,11 +970,11 @@ Code analysis results from preprocessing phase, including definitions of functio } ], "responsibilities": [ - "Parse command-line arguments for configuration and agent settings.", - "Initialize the application-wide structured logging system.", - "Construct the core MemoryMcpService with provided configuration.", - "Serve the MCP service over stdio and manage its lifecycle.", - "Handle startup and runtime errors with appropriate logging." + "Parse command-line arguments for configuration and agent identification", + "Initialize structured logging with appropriate verbosity", + "Bootstrap and configure the MemoryMcpService with provided settings", + "Start and run the MCP server using stdio as transport mechanism", + "Handle server lifecycle events including graceful shutdown and error reporting" ] }, { @@ -712,105 +1063,133 @@ Code analysis results from preprocessing phase, including definitions of functio { "code_dossier": { "code_purpose": "entry", - "description": "Project execution entry point for demonstrating memory operations using shared MemoryOperations abstraction.", - "file_path": "examples/memory-tools-refactor/src/main.rs", + "description": "Internationalization (i18n) module responsible for managing language settings, translations, and formatting across the application.", + "file_path": "cortex-mem-insights/src/lib/i18n/index.ts", "functions": [ - "main" + "setLanguage", + "getCurrentLanguage", + "t", + "format.importance", + "format.quality", + "format.percentage", + "format.date", + "format.relativeTime" ], "importance_score": 1.0, "interfaces": [ - "main" + "Language", + "TranslationKey" ], - "name": "main.rs", - "source_summary": "use cortex_mem_config::Config;\nuse cortex_mem_core::{\n init::initialize_memory_system,\n memory::MemoryManager,\n};\nuse cortex_mem_tools::{MemoryOperations, MemoryOperationPayload};\nuse serde_json::json;\nuse std::sync::Arc;\nuse tracing::{info, Level};\nuse tracing_subscriber;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n // Initialize logging\n tracing_subscriber::fmt()\n .with_max_level(Level::INFO)\n .init();\n\n info!(\"Starting memory tools refactor example\");\n\n // Load configuration\n let config = Config::load(\"config.toml\")?;\n info!(\"Loaded configuration\");\n\n // Initialize memory system\n let (vector_store, llm_client) = initialize_memory_system(&config).await?;\n info!(\"Initialized memory system\");\n\n // Create memory manager\n let memory_manager = Arc::new(MemoryManager::new(\n vector_store,\n llm_client,\n config.memory.clone(),\n ));\n info!(\"Created memory manager\");\n\n // Create shared operations\n let operations = MemoryOperations::new(\n memory_manager.clone(),\n Some(\"default_user\".to_string()),\n Some(\"default_agent\".to_string()),\n 10,\n );\n info!(\"Created shared operations\");\n\n // Test 1: Store a memory using the shared operations\n info!(\"Test 1: Storing a memory\");\n let mut store_payload = MemoryOperationPayload::default();\n store_payload.content = Some(\"This is a test memory for the shared operations demo\".to_string());\n store_payload.memory_type = Some(\"conversational\".to_string());\n store_payload.topics = Some(vec![\"testing\".to_string(), \"demo\".to_string()]);\n\n let store_result = operations.store_memory(store_payload).await?;\n info!(\"Store result: {}\", store_result.success);\n\n // Extract memory_id for use in later tests\n let memory_id = if let Some(data) = store_result.data {\n data.get(\"memory_id\").and_then(|v| v.as_str()).unwrap()\n } else {\n \"\"\n };\n\n // Test 2: Query memories using the shared operations\n info!(\"Test 2: Querying memories\");\n let mut query_payload = MemoryOperationPayload::default();\n query_payload.query = Some(\"test memory\".to_string());\n query_payload.limit = Some(5);\n\n let query_result = operations.query_memory(query_payload).await?;\n info!(\"Query result: {} (found {} memories)\",\n query_result.success,\n query_result.data\n .as_ref()\n .and_then(|d| d.get(\"count\"))\n .and_then(|c| c.as_u64())\n .unwrap_or(0));\n\n // Test 3: List memories using the shared operations\n info!(\"Test 3: Listing memories\");\n let mut list_payload = MemoryOperationPayload::default();\n list_payload.limit = Some(10);\n\n let list_result = operations.list_memories(list_payload).await?;\n info!(\"List result: {} (found {} memories)\",\n list_result.success,\n list_result.data\n .as_ref()\n .and_then(|d| d.get(\"count\"))\n .and_then(|c| c.as_u64())\n .unwrap_or(0));\n\n // Test 4: Get a specific memory using the shared operations\n if !memory_id.is_empty() {\n info!(\"Test 4: Getting specific memory\");\n let mut get_payload = MemoryOperationPayload::default();\n get_payload.memory_id = Some(memory_id.to_string());\n\n let get_result = operations.get_memory(get_payload).await?;\n info!(\"Get result: {}\", get_result.success);\n }\n\n info!(\"Memory tools refactor example completed successfully\");\n Ok(())\n}\n" + "name": "index.ts", + "source_summary": "import { writable, derived } from 'svelte/store';\nimport en from './locales/en.json';\nimport zh from './locales/zh.json';\nimport ja from './locales/ja.json';\n\nexport type Language = 'en' | 'zh' | 'ja';\nexport type TranslationKey = string;\n\nconst translations = {\n en,\n zh,\n ja\n};\n\n// 从localStorage获取保存的语言设置,默认为英文\nconst storedLanguage = typeof window !== 'undefined' ? localStorage.getItem('cortex-mem-language') as Language : null;\nconst defaultLanguage: Language = storedLanguage && ['en', 'zh', 'ja'].includes(storedLanguage) ? storedLanguage : 'en';\n\n// 创建语言store\nexport const language = writable(defaultLanguage);\n\n// 创建翻译store\nexport const t = derived(language, ($language) => {\n const currentTranslations = translations[$language];\n \n // 创建翻译函数\n const translate = (key: TranslationKey, params?: Record): string => {\n // 支持嵌套key,如 'common.appName'\n const keys = key.split('.');\n let value: any = currentTranslations;\n \n for (const k of keys) {\n if (value && typeof value === 'object' && k in value) {\n value = value[k];\n } else {\n // 如果找不到翻译,回退到英文\n let fallbackValue: any = translations.en;\n for (const fallbackKey of keys) {\n if (fallbackValue && typeof fallbackValue === 'object' && fallbackKey in fallbackValue) {\n fallbackValue = fallbackValue[fallbackKey];\n } else {\n return key; // 如果英文也没有,返回key本身\n }\n }\n value = fallbackValue;\n break;\n }\n }\n \n // 如果找到了字符串值,处理参数替换\n if (typeof value === 'string' && params) {\n return Object.entries(params).reduce((str, [paramKey, paramValue]) => {\n return str.replace(new RegExp(`\\{${paramKey}\\}`, 'g'), String(paramValue));\n }, value);\n }\n \n return typeof value === 'string' ? value : key;\n };\n \n return translate;\n});\n\n// 切换语言函数\nexport function setLanguage(newLanguage: Language): void {\n language.set(newLanguage);\n if (typeof window !== 'undefined') {\n localStorage.setItem('cortex-mem-language', newLanguage);\n }\n}\n\n// 获取当前语言\nexport function getCurrentLanguage(): Language {\n let currentLang: Language = 'en';\n language.subscribe((lang) => {\n currentLang = lang;\n })();\n return currentLang;\n}\n\n// 获取语言选项\nexport const languageOptions = [\n { value: 'en', label: 'English' },\n { value: 'zh', label: '中文' },\n { value: 'ja', label: '日本語' }\n];\n\n// 格式化函数:用于格式化数字、日期等\nexport const format = {\n // 格式化重要性分数(保留两位小数)\n importance: (value: number): string => {\n return value.toFixed(2);\n },\n \n // 格式化质量分数(保留两位小数)\n quality: (value: number): string => {\n return value.toFixed(2);\n },\n \n // 格式化百分比(用于进度条等)\n percentage: (value: number): string => {\n return `${(value * 100).toFixed(1)}%`;\n },\n \n // 格式化日期\n date: (dateString: string, locale?: string): string => {\n try {\n const date = new Date(dateString);\n const currentLocale = locale || getCurrentLanguage();\n const localeMap = {\n en: 'en-US',\n zh: 'zh-CN',\n ja: 'ja-JP'\n };\n return date.toLocaleString(localeMap[currentLocale] || 'en-US', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit'\n });\n } catch {\n return dateString;\n }\n },\n \n // 格式化相对时间\n relativeTime: (dateString: string): string => {\n try {\n const date = new Date(dateString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n \n if (diffMins < 1) return 'just now';\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n \n return format.date(dateString);\n } catch {\n return dateString;\n }\n }\n};" }, "complexity_metrics": { - "cyclomatic_complexity": 5.0, - "lines_of_code": 104, + "cyclomatic_complexity": 11.0, + "lines_of_code": 147, "number_of_classes": 0, - "number_of_functions": 1 + "number_of_functions": 8 }, "dependencies": [ { - "dependency_type": "use", - "is_external": false, + "dependency_type": "import", + "is_external": true, "line_number": 1, - "name": "cortex_mem_config", - "path": "cortex_mem_config::Config", + "name": "svelte/store", + "path": "svelte/store", "version": null }, { - "dependency_type": "use", + "dependency_type": "import", "is_external": false, "line_number": 2, - "name": "cortex_mem_core", - "path": "cortex_mem_core::{init::initialize_memory_system, memory::MemoryManager}", + "name": "./locales/en.json", + "path": "./locales/en.json", "version": null }, { - "dependency_type": "use", + "dependency_type": "import", "is_external": false, - "line_number": 4, - "name": "cortex_mem_tools", - "path": "cortex_mem_tools::{MemoryOperations, MemoryOperationPayload}", + "line_number": 3, + "name": "./locales/zh.json", + "path": "./locales/zh.json", "version": null }, { - "dependency_type": "use", - "is_external": true, - "line_number": 5, - "name": "serde_json", - "path": "serde_json::json", + "dependency_type": "import", + "is_external": false, + "line_number": 4, + "name": "./locales/ja.json", + "path": "./locales/ja.json", "version": null - }, + } + ], + "detailed_description": "This component serves as the core internationalization (i18n) module for the application. It manages multi-language support by loading locale-specific translation files (en, zh, ja), persisting user language preferences via localStorage, and providing reactive translation and formatting utilities. The `t` function is a derived store that reacts to language changes and supports nested translation keys with parameter interpolation. A fallback mechanism ensures English is used when translations are missing. The `format` object provides reusable formatting methods for numbers, percentages, dates, and relative time, localized based on current language. Language state is managed using Svelte's writable and derived stores for reactivity. The module also exposes language options for UI selection and allows imperative language switching via `setLanguage`.", + "interfaces": [ { - "dependency_type": "use", - "is_external": true, - "line_number": 7, - "name": "tokio", - "path": "tokio::main", - "version": null + "description": "Represents the supported language codes in the application.", + "interface_type": "type", + "name": "Language", + "parameters": [], + "return_type": "'en' | 'zh' | 'ja'", + "visibility": "public" }, { - "dependency_type": "use", - "is_external": true, - "line_number": 6, - "name": "tracing", - "path": "tracing::{info, Level}", - "version": null + "description": "Type alias for translation string keys, supporting dot notation for nesting.", + "interface_type": "type", + "name": "TranslationKey", + "parameters": [], + "return_type": "string", + "visibility": "public" }, { - "dependency_type": "use", - "is_external": true, - "line_number": 7, - "name": "tracing_subscriber", - "path": "tracing_subscriber", - "version": null - } - ], - "detailed_description": "This component serves as the entry point for a demonstration application showcasing the refactored memory tools system. It initializes the logging system using tracing_subscriber, loads configuration from a TOML file, sets up the core memory system via initialize_memory_system which returns a vector store and LLM client, and creates a MemoryManager wrapped in an Arc for shared ownership. It then instantiates a MemoryOperations struct providing a unified interface for memory manipulation with default user, agent, and batch size. The main logic consists of four sequential tests: storing a new memory with specific content, topics, and type; querying memories using a text search; listing all stored memories with a limit; and retrieving a specific memory by its ID obtained from the store result. Each operation logs its progress and outcome, concluding with a success message. The use of async/await indicates non-blocking operations, likely involving I/O such as database access or API calls.", - "interfaces": [ + "description": "Derived store that returns a translation function based on current language", + "interface_type": "store", + "name": "t", + "parameters": [ + { + "description": "Dot-separated path to the translation string", + "is_optional": false, + "name": "key", + "param_type": "TranslationKey" + }, + { + "description": "Optional parameters for interpolation in the translated string", + "is_optional": true, + "name": "params", + "param_type": "Record" + } + ], + "return_type": "string", + "visibility": "public" + }, { - "description": "Asynchronous entry point that orchestrates the memory operations demo, returning Ok on success or a boxed error.", + "description": "Updates the current language and persists it to localStorage", "interface_type": "function", - "name": "main", - "parameters": [], - "return_type": "Result<(), Box>", + "name": "setLanguage", + "parameters": [ + { + "description": "The language code to switch to", + "is_optional": false, + "name": "newLanguage", + "param_type": "Language" + } + ], + "return_type": "void", "visibility": "public" } ], "responsibilities": [ - "Initialize the application runtime environment including logging and configuration", - "Orchestrate the setup of core memory system components (vector store, LLM client, memory manager)", - "Instantiate and manage the shared MemoryOperations interface for memory manipulation", - "Execute end-to-end demonstration of memory operations (store, query, list, get)", - "Handle errors gracefully and provide detailed execution logging" + "Manage application-wide language state using reactive stores", + "Provide translation lookup with fallback to English and support for nested keys and parameter interpolation", + "Persist and retrieve user language preference via localStorage", + "Expose a set of formatting utilities for numbers, dates, and relative time with localization", + "Provide language selection options for UI components" ] }, { "code_dossier": { "code_purpose": "entry", - "description": "Main application entry point for a memory-enabled agent system with TUI interface and background memory persistence.", + "description": "Main application entry point for Cortex Memory TARS, a terminal-based interactive AI assistant with memory capabilities. Handles initialization, event loop, UI rendering and shutdown sequence.", "file_path": "examples/cortex-mem-tars/src/main.rs", "functions": [ "main", @@ -825,16 +1204,15 @@ Code analysis results from preprocessing phase, including definitions of functio "Cli", "AppMessage", "App", - "MemoryManager", - "OpenAILLMClient", - "QdrantVectorStore" + "draw_ui", + "handle_key_event" ], "name": "main.rs", - "source_summary": "use clap::Parser;\nuse crossterm::{\n event, execute,\n terminal::{EnterAlternateScreen, enable_raw_mode},\n};\nuse cortex_mem_config::Config;\nuse cortex_mem_core::init_logging;\nuse cortex_mem_rig::{\n llm::OpenAILLMClient, memory::manager::MemoryManager, vector_store::qdrant::QdrantVectorStore,\n};\nuse ratatui::{Terminal, backend::CrosstermBackend};\nuse std::{io, path::PathBuf, sync::Arc};\nuse tokio::sync::mpsc;\nuse tokio::time::Duration;\n\nmod agent;\nmod app;\nmod events;\nmod log_monitor;\nmod terminal;\nmod ui;\n\nuse agent::{\n agent_reply_with_memory_retrieval_streaming, create_memory_agent, extract_user_basic_info,\n store_conversations_batch,\n};\nuse app::{App, AppMessage, redirect_log_to_ui, set_global_log_sender};\nuse events::{handle_key_event, process_user_input};\nuse log_monitor::start_log_monitoring_task;\nuse terminal::cleanup_terminal_final;\nuse ui::draw_ui;\n\n#[derive(Parser)]\n#[command(name = \"multi-round-interactive\")]\n#[command(about = \"Multi-round interactive conversation with a memory-enabled agent\")]\nstruct Cli {\n /// Path to the configuration file\n #[arg(short, long, default_value = \"config.toml\")]\n config: PathBuf,\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n // 加载基本配置以获取日志设置\n let cli = Cli::parse();\n let config = Config::load(&cli.config)?;\n\n // 初始化日志系统\n init_logging(&config.logging)?;\n\n // 设置终端\n enable_raw_mode()?;\n let mut stdout = io::stdout();\n execute!(\n stdout,\n EnterAlternateScreen,\n crossterm::event::EnableMouseCapture\n )?;\n let backend = CrosstermBackend::new(stdout);\n let mut terminal = Terminal::new(backend)?;\n\n let result = run_application(&mut terminal).await;\n\n // 最终清理 - 使用最彻底的方法\n cleanup_terminal_final(&mut terminal);\n\n result\n}\n\n/// 主应用逻辑\nasync fn run_application(\n terminal: &mut Terminal>,\n) -> Result<(), Box> {\n // 创建消息通道\n let (msg_tx, mut msg_rx) = mpsc::unbounded_channel::();\n\n // 使用我们的自定义日志系统,禁用tracing\n // tracing_subscriber::fmt::init();\n\n // 设置全局日志发送器以便我们的日志系统正常工作\n set_global_log_sender(msg_tx.clone());\n\n // 初始化组件\n // 配置加载已经在main函数中完成,这里只获取文件路径\n let cli = Cli::parse();\n let config = Config::load(&cli.config)?;\n\n let llm_client = OpenAILLMClient::new(&config.llm, &config.embedding)?;\n let vector_store = QdrantVectorStore::new(&config.qdrant)\n .await\n .expect(\"无法连接到Qdrant\");\n\n let memory_config = config.memory.clone();\n let memory_manager = Arc::new(MemoryManager::new(\n Box::new(vector_store),\n Box::new(llm_client.clone()),\n memory_config,\n ));\n\n // 创建带记忆的Agent\n let memory_tool_config = cortex_mem_rig::tool::MemoryToolConfig {\n default_user_id: Some(\"demo_user\".to_string()),\n ..Default::default()\n };\n\n let agent = create_memory_agent(memory_manager.clone(), memory_tool_config, &config).await?;\n\n // 初始化用户信息\n let user_id = \"demo_user\";\n let user_info = extract_user_basic_info(&config, memory_manager.clone(), user_id).await?;\n\n // 创建应用状态\n let mut app = App::new(msg_tx);\n\n if let Some(info) = user_info {\n app.user_info = Some(info.clone());\n app.log_info(\"已加载用户基本信息\");\n } else {\n app.log_info(\"未找到用户基本信息\");\n }\n\n app.log_info(\"初始化完成,开始对话...\");\n\n // 主事件循环\n loop {\n // 更新消息(包括在quit过程中收到的所有消息)\n while let Ok(msg) = msg_rx.try_recv() {\n match msg {\n AppMessage::Log(log_msg) => {\n app.add_log(log_msg);\n }\n AppMessage::Conversation { user, assistant } => {\n app.add_conversation(user, assistant);\n }\n AppMessage::StreamingChunk { user, chunk } => {\n // 如果是新的用户输入,开始新的流式回复\n if app.current_streaming_response.is_none() || \n app.current_streaming_response.as_ref().map(|(u, _)| u != &user).unwrap_or(false) {\n app.start_streaming_response(user);\n }\n app.add_streaming_chunk(chunk);\n }\n AppMessage::StreamingComplete { user: _, full_response: _ } => {\n app.complete_streaming_response();\n }\n AppMessage::MemoryIterationCompleted => {\n app.memory_iteration_completed = true;\n app.should_quit = true;\n }\n }\n }\n\n // 绘制UI\n terminal.draw(|f| draw_ui(f, &mut app))?;\n\n // 处理事件\n if event::poll(std::time::Duration::from_millis(100))? {\n if let Some(input) = handle_key_event(event::read()?, &mut app) {\n // 先检查是否是quit命令\n let is_quit = process_user_input(input.clone(), &mut app);\n\n // 如果是quit命令,先添加到对话历史\n if is_quit {\n app.add_conversation(input.clone(), \"正在执行退出命令...\".to_string());\n }\n\n if is_quit {\n // 立即退出到terminal,后台执行记忆化任务\n let conversations_vec: Vec<(String, String)> =\n app.conversations.iter().map(|(user, assistant, _)| (user.clone(), assistant.clone())).collect();\n handle_quit_async(\n terminal,\n &mut app,\n &conversations_vec,\n &memory_manager,\n user_id,\n )\n .await?;\n\n // 退出主循环\n break;\n } else {\n // 记录用户输入\n redirect_log_to_ui(\"INFO\", &format!(\"接收用户输入: {}\", input));\n\n // 处理用户输入\n let agent_clone = agent.clone();\n let memory_manager_clone = memory_manager.clone();\n let config_clone = config.clone();\n let user_info_clone = app.user_info.clone();\n let user_id_clone = user_id.to_string();\n let msg_tx_clone = app.message_sender.clone();\n\n // 获取当前对话历史的引用(转换为slice)\n let current_conversations: Vec<(String, String)> =\n app.conversations.iter().map(|(user, assistant, _)| (user.clone(), assistant.clone())).collect();\n\n // 记录开始处理\n redirect_log_to_ui(\"INFO\", \"开始处理用户请求...\");\n\n tokio::spawn(async move {\n // 创建流式通道\n let (stream_tx, mut stream_rx) = mpsc::unbounded_channel::();\n \n // 启动流式处理任务\n let agent_clone2 = agent_clone.clone();\n let memory_manager_clone2 = memory_manager_clone.clone();\n let config_clone2 = config_clone.clone();\n let user_info_clone2 = user_info_clone.clone();\n let user_id_clone2 = user_id_clone.clone();\n let input_clone = input.clone();\n let current_conversations_clone = current_conversations.clone();\n \n let generation_task = tokio::spawn(async move {\n agent_reply_with_memory_retrieval_streaming(\n &agent_clone2,\n memory_manager_clone2,\n &input_clone,\n &user_id_clone2,\n user_info_clone2.as_deref(),\n ¤t_conversations_clone,\n stream_tx,\n )\n .await\n });\n\n // 处理流式内容\n while let Some(chunk) = stream_rx.recv().await {\n if let Some(sender) = &msg_tx_clone {\n let _ = sender.send(AppMessage::StreamingChunk {\n user: input.clone(),\n chunk,\n });\n }\n }\n\n // 等待生成任务完成\n match generation_task.await {\n Ok(Ok(full_response)) => {\n // 发送完成消息\n if let Some(sender) = &msg_tx_clone {\n let _ = sender.send(AppMessage::StreamingComplete {\n user: input.clone(),\n full_response: full_response.clone(),\n });\n redirect_log_to_ui(\"INFO\", &format!(\"生成回复完成: {}\", full_response));\n }\n }\n Ok(Err(e)) => {\n let error_msg = format!(\"抱歉,我遇到了一些技术问题: {}\", e);\n redirect_log_to_ui(\"ERROR\", &error_msg);\n // 完成流式回复(即使出错也要清理状态)\n if let Some(sender) = &msg_tx_clone {\n let _ = sender.send(AppMessage::StreamingComplete {\n user: input.clone(),\n full_response: error_msg,\n });\n }\n }\n Err(e) => {\n let error_msg = format!(\"任务执行失败: {}\", e);\n redirect_log_to_ui(\"ERROR\", &error_msg);\n // 完成流式回复(即使出错也要清理状态)\n if let Some(sender) = &msg_tx_clone {\n let _ = sender.send(AppMessage::StreamingComplete {\n user: input.clone(),\n full_response: error_msg,\n });\n }\n }\n }\n });\n }\n }\n }\n\n // 检查是否有新的对话结果\n app.is_processing = false;\n\n // 只有在没有在shutting down状态或者记忆化已完成时才能退出\n if app.should_quit && app.memory_iteration_completed {\n break;\n }\n\n // **在quit过程中处理剩余的日志消息但不退出**\n if app.is_shutting_down && !app.memory_iteration_completed {\n // **立即处理所有待处理的日志消息**\n while let Ok(msg) = msg_rx.try_recv() {\n match msg {\n AppMessage::Log(log_msg) => {\n app.add_log(log_msg);\n }\n AppMessage::Conversation { user, assistant } => {\n app.add_conversation(user, assistant);\n }\n AppMessage::StreamingChunk { user, chunk } => {\n // 如果是新的用户输入,开始新的流式回复\n if app.current_streaming_response.is_none() || \n app.current_streaming_response.as_ref().map(|(u, _)| u != &user).unwrap_or(false) {\n app.start_streaming_response(user);\n }\n app.add_streaming_chunk(chunk);\n }\n AppMessage::StreamingComplete { user: _, full_response: _ } => {\n app.complete_streaming_response();\n }\n AppMessage::MemoryIterationCompleted => {\n app.memory_iteration_completed = true;\n app.should_quit = true;\n break;\n }\n }\n }\n\n // 在shutting down期间立即刷新UI显示最新日志\n if let Err(e) = terminal.draw(|f| draw_ui(f, &mut app)) {\n eprintln!(\"UI绘制错误: {}\", e);\n }\n\n // 在shutting down期间添加短暂延迟,让用户能看到日志更新\n tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;\n }\n }\n\n println!(\"Cortex TARS powering down. Goodbye!\");\n Ok(())\n}\n\n/// 异步处理退出逻辑,立即退出TUI到terminal\nasync fn handle_quit_async(\n _terminal: &mut Terminal>,\n app: &mut App,\n conversations: &Vec<(String, String)>,\n memory_manager: &Arc,\n user_id: &str,\n) -> Result<(), Box> {\n use crossterm::cursor::{MoveTo, Show};\n use crossterm::style::{\n Attribute, Color, ResetColor, SetAttribute, SetBackgroundColor, SetForegroundColor,\n };\n use crossterm::{\n event::DisableMouseCapture,\n execute,\n terminal::{Clear, ClearType, LeaveAlternateScreen},\n };\n use std::io::{Write, stdout};\n\n // 记录退出命令到UI\n redirect_log_to_ui(\"INFO\", \"🚀 用户输入退出命令 /quit,开始后台记忆化...\");\n\n // 先获取所有日志内容\n let all_logs: Vec = app.logs.iter().cloned().collect();\n\n // 彻底清理terminal状态\n let mut stdout = stdout();\n\n // 执行完整的terminal重置序列\n execute!(&mut stdout, ResetColor)?;\n execute!(&mut stdout, Clear(ClearType::All))?;\n execute!(&mut stdout, MoveTo(0, 0))?;\n execute!(&mut stdout, Show)?;\n execute!(&mut stdout, LeaveAlternateScreen)?;\n execute!(&mut stdout, DisableMouseCapture)?;\n execute!(&mut stdout, SetAttribute(Attribute::Reset))?;\n execute!(&mut stdout, SetForegroundColor(Color::Reset))?;\n execute!(&mut stdout, SetBackgroundColor(Color::Reset))?;\n\n // 禁用原始模式\n let _ = crossterm::terminal::disable_raw_mode();\n\n // 刷新输出确保清理完成\n stdout.flush()?;\n\n // 输出分隔线\n println!(\"\\n╔══════════════════════════════════════════════════════════════════════════════╗\");\n println!(\"║ 🧠 Cortex Memory - 退出流程 ║\");\n println!(\"╚══════════════════════════════════════════════════════════════════════════════╝\");\n\n // 显示会话摘要\n println!(\"📋 会话摘要:\");\n println!(\" • 对话轮次: {} 轮\", conversations.len());\n println!(\" • 用户ID: {}\", user_id);\n\n // 显示最近的日志(如果有)\n if !all_logs.is_empty() {\n println!(\"\\n📜 最近的操作日志:\");\n let recent_logs = if all_logs.len() > 10 {\n &all_logs[all_logs.len() - 10..]\n } else {\n &all_logs[..]\n };\n\n println!(\" {}\", \"─\".repeat(70));\n for (i, log) in recent_logs.iter().enumerate() {\n let beautified_content = beautify_log_content(log);\n\n // 添加日志条目编号\n if i > 0 {\n println!(\" {}\", \"─\".repeat(70));\n }\n\n // 显示美化后的内容,支持多行显示\n let lines: Vec<&str> = beautified_content.split('\\n').collect();\n for (line_i, line) in lines.iter().enumerate() {\n if line_i == 0 {\n // 第一行显示编号和完整内容\n let colored_line = get_log_level_color(log, line);\n println!(\" {}\", colored_line);\n } else {\n // 后续行添加缩进\n println!(\" │ {}\", line);\n }\n }\n }\n if all_logs.len() > 10 {\n println!(\" {}\", \"─\".repeat(70));\n println!(\" ... (显示最近10条,共{}条)\", all_logs.len());\n }\n }\n\n println!(\"\\n🧠 开始执行记忆化存储...\");\n\n // 准备对话数据(过滤quit命令)\n let mut valid_conversations = Vec::new();\n for (user_msg, assistant_msg) in conversations {\n let user_msg_trimmed = user_msg.trim().to_lowercase();\n if user_msg_trimmed == \"quit\"\n || user_msg_trimmed == \"exit\"\n || user_msg_trimmed == \"/quit\"\n || user_msg_trimmed == \"/exit\"\n {\n continue;\n }\n valid_conversations.push((user_msg.clone(), assistant_msg.clone()));\n }\n\n if valid_conversations.is_empty() {\n println!(\"⚠️ 没有需要存储的内容\");\n println!(\n \"\\n╔══════════════════════════════════════════════════════════════════════════════╗\"\n );\n println!(\n \"║ ✅ 退出流程完成 ║\"\n );\n println!(\n \"╚══════════════════════════════════════════════════════════════════════════════╝\"\n );\n println!(\"👋 感谢使用Cortex Memory!\");\n return Ok(());\n }\n\n // 只有在有内容需要存储时才启动日志监听任务\n let log_dir = \"logs\".to_string();\n let log_monitoring_handle = tokio::spawn(async move {\n if let Err(e) = start_log_monitoring_task(log_dir).await {\n eprintln!(\"日志监听任务失败: {}\", e);\n }\n });\n\n println!(\n \"📝 正在保存 {} 条对话记录到记忆库...\",\n valid_conversations.len()\n );\n println!(\"🚀 开始存储对话到记忆系统...\");\n\n // 执行批量记忆化\n match store_conversations_batch(memory_manager.clone(), &valid_conversations, user_id).await {\n Ok(_) => {\n println!(\"✨ 记忆化完成!\");\n println!(\"✅ 所有对话已成功存储到记忆系统\");\n println!(\"🔍 存储详情:\");\n println!(\" • 对话轮次: {} 轮\", valid_conversations.len());\n println!(\" • 用户消息: {} 条\", valid_conversations.len());\n println!(\" • 助手消息: {} 条\", valid_conversations.len());\n }\n Err(e) => {\n println!(\"❌ 记忆存储失败: {}\", e);\n println!(\"⚠️ 虽然记忆化失败,但仍正常退出\");\n }\n }\n\n // 停止日志监听任务\n log_monitoring_handle.abort();\n\n tokio::time::sleep(Duration::from_secs(3)).await;\n\n println!(\"\\n╔══════════════════════════════════════════════════════════════════════════════╗\");\n println!(\"║ 🎉 退出流程完成 ║\");\n println!(\"╚══════════════════════════════════════════════════════════════════════════════╝\");\n println!(\"👋 感谢使用Cortex Memory!\");\n\n Ok(())\n}\n\n/// 美化日志内容显示\nfn beautify_log_content(log_line: &str) -> String {\n // 过滤掉时间戳前缀,保持简洁\n let content = if let Some(content_start) = log_line.find(\"] \") {\n &log_line[content_start + 2..]\n } else {\n log_line\n };\n\n // 判断是否为JSON内容\n let trimmed_content = content.trim();\n let is_json = trimmed_content.starts_with('{') && trimmed_content.ends_with('}');\n\n if is_json {\n // 尝试美化JSON,保留完整内容\n match prettify_json(trimmed_content) {\n Ok(formatted_json) => {\n // 如果格式化成功,返回完整的带缩进的JSON\n formatted_json\n }\n Err(_) => {\n // 如果JSON格式化失败,返回原始内容\n content.to_string()\n }\n }\n } else {\n // 非JSON内容,保持原样\n content.to_string()\n }\n}\n\n/// 美化JSON内容\nfn prettify_json(json_str: &str) -> Result> {\n use serde_json::Value;\n\n let value: Value = serde_json::from_str(json_str)?;\n Ok(serde_json::to_string_pretty(&value)?)\n}\n\n/// 根据日志级别返回带颜色的文本\nfn get_log_level_color(log_line: &str, text: &str) -> String {\n let log_level = if let Some(level_start) = log_line.find(\"[\") {\n if let Some(level_end) = log_line[level_start..].find(\"]\") {\n &log_line[level_start + 1..level_start + level_end]\n } else {\n \"UNKNOWN\"\n }\n } else {\n \"UNKNOWN\"\n };\n\n // ANSI颜色代码\n let (color_code, reset_code) = match log_level.to_uppercase().as_str() {\n \"ERROR\" => (\"\\x1b[91m\", \"\\x1b[0m\"), // 亮红色\n \"WARN\" | \"WARNING\" => (\"\\x1b[93m\", \"\\x1b[0m\"), // 亮黄色\n \"INFO\" => (\"\\x1b[36m\", \"\\x1b[0m\"), // 亮青色\n \"DEBUG\" => (\"\\x1b[94m\", \"\\x1b[0m\"), // 亮蓝色\n \"TRACE\" => (\"\\x1b[95m\", \"\\x1b[0m\"), // 亮紫色\n _ => (\"\\x1b[0m\", \"\\x1b[0m\"), // 白色\n };\n\n format!(\"{}{}{}\", color_code, text, reset_code)\n}\n" + "source_summary": "use clap::Parser;\nuse crossterm::{\n event, execute,\n terminal::{EnterAlternateScreen, enable_raw_mode},\n};\nuse cortex_mem_config::Config;\nuse cortex_mem_core::init_logging;\nuse cortex_mem_rig::{\n llm::OpenAILLMClient, memory::manager::MemoryManager, vector_store::qdrant::QdrantVectorStore,\n};\nuse ratatui::{Terminal, backend::CrosstermBackend};\nuse std::{io, path::PathBuf, sync::Arc};\nuse tokio::sync::mpsc;\nuse tokio::time::Duration;\n\nmod agent;\nmod app;\nmod events;\nmod log_monitor;\nmod terminal;\nmod ui;\n\nuse agent::{\n agent_reply_with_memory_retrieval_streaming, create_memory_agent, extract_user_basic_info,\n store_conversations_batch,\n};\nuse app::{App, AppMessage, redirect_log_to_ui, set_global_log_sender};\nuse events::{handle_key_event, process_user_input};\nuse log_monitor::start_log_monitoring_task;\nuse terminal::cleanup_terminal_final;\nuse ui::draw_ui;\n\n#[derive(Parser)]\n#[command(name = \"Cortex Memory Tars\")]\n#[command(about = \"A Multi-round interactive conversation with a memory-enabled agent\")]\n#[command(author = \"Sopaco\")]\n#[command(version)]\nstruct Cli {\n /// Path to the configuration file\n #[arg(short, long, default_value = \"config.toml\")]\n config: PathBuf,\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n // 加载基本配置以获取日志设置\n let cli = Cli::parse();\n let config = Config::load(&cli.config)?;\n\n // 初始化日志系统\n init_logging(&config.logging)?;\n\n // 设置终端\n enable_raw_mode()?;\n let mut stdout = io::stdout();\n execute!(\n stdout,\n EnterAlternateScreen,\n crossterm::event::EnableMouseCapture\n )?;\n let backend = CrosstermBackend::new(stdout);\n let mut terminal = Terminal::new(backend)?;\n\n let result = run_application(&mut terminal).await;\n\n // 最终清理 - 使用最彻底的方法\n cleanup_terminal_final(&mut terminal);\n\n result\n}\n\n/// 主应用逻辑\nasync fn run_application(\n terminal: &mut Terminal>,\n) -> Result<(), Box> {\n // 创建消息通道\n let (msg_tx, mut msg_rx) = mpsc::unbounded_channel::();\n\n // 使用我们的自定义日志系统,禁用tracing\n // tracing_subscriber::fmt::init();\n\n // 设置全局日志发送器以便我们的日志系统正常工作\n set_global_log_sender(msg_tx.clone());\n\n // 初始化组件\n // 配置加载已经在main函数中完成,这里只获取文件路径\n let cli = Cli::parse();\n let config = Config::load(&cli.config)?;\n\n let llm_client = OpenAILLMClient::new(&config.llm, &config.embedding)?;\n let vector_store = QdrantVectorStore::new(&config.qdrant)\n .await\n .expect(\"无法连接到Qdrant\");\n\n let memory_config = config.memory.clone();\n let memory_manager = Arc::new(MemoryManager::new(\n Box::new(vector_store),\n Box::new(llm_client.clone()),\n memory_config,\n ));\n\n // 创建带记忆的Agent\n let memory_tool_config = cortex_mem_rig::tool::MemoryToolConfig {\n default_user_id: Some(\"demo_user\".to_string()),\n ..Default::default()\n };\n\n let agent = create_memory_agent(memory_manager.clone(), memory_tool_config, &config).await?;\n\n // 初始化用户信息\n let user_id = \"demo_user\";\n let user_info = extract_user_basic_info(&config, memory_manager.clone(), user_id).await?;\n\n // 创建应用状态\n let mut app = App::new(msg_tx);\n\n if let Some(info) = user_info {\n app.user_info = Some(info.clone());\n app.log_info(\"已加载用户基本信息\");\n } else {\n app.log_info(\"未找到用户基本信息\");\n }\n\n app.log_info(\"初始化完成,开始对话...\");\n\n // 主事件循环\n loop {\n // 更新消息(包括在quit过程中收到的所有消息)\n while let Ok(msg) = msg_rx.try_recv() {\n match msg {\n AppMessage::Log(log_msg) => {\n app.add_log(log_msg);\n }\n AppMessage::Conversation { user, assistant } => {\n app.add_conversation(user, assistant);\n }\n AppMessage::StreamingChunk { user, chunk } => {\n // 如果是新的用户输入,开始新的流式回复\n if app.current_streaming_response.is_none() ||\n app.current_streaming_response.as_ref().map(|(u, _)| u != &user).unwrap_or(false) {\n app.start_streaming_response(user);\n }\n app.add_streaming_chunk(chunk);\n }\n AppMessage::StreamingComplete { user: _, full_response: _ } => {\n app.complete_streaming_response();\n }\n AppMessage::MemoryIterationCompleted => {\n app.memory_iteration_completed = true;\n app.should_quit = true;\n }\n }\n }\n\n // 绘制UI\n terminal.draw(|f| draw_ui(f, &mut app))?;\n\n // 处理事件\n if event::poll(std::time::Duration::from_millis(100))? {\n if let Some(input) = handle_key_event(event::read()?, &mut app) {\n // 先检查是否是quit命令\n let is_quit = process_user_input(input.clone(), &mut app);\n\n // 如果是quit命令,先添加到对话历史\n if is_quit {\n app.add_conversation(input.clone(), \"正在执行退出命令...\".to_string());\n }\n\n if is_quit {\n // 立即退出到terminal,后台执行记忆化任务\n let conversations_vec: Vec<(String, String)> =\n app.conversations.iter().map(|(user, assistant, _)| (user.clone(), assistant.clone())).collect();\n handle_quit_async(\n terminal,\n &mut app,\n &conversations_vec,\n &memory_manager,\n user_id,\n )\n .await?;\n\n // 退出主循环\n break;\n } else {\n // 记录用户输入\n redirect_log_to_ui(\"INFO\", &format!(\"接收用户输入: {}\", input));\n\n // 处理用户输入\n let agent_clone = agent.clone();\n let memory_manager_clone = memory_manager.clone();\n let config_clone = config.clone();\n let user_info_clone = app.user_info.clone();\n let user_id_clone = user_id.to_string();\n let msg_tx_clone = app.message_sender.clone();\n\n // 获取当前对话历史的引用(转换为slice)\n let current_conversations: Vec<(String, String)> =\n app.conversations.iter().map(|(user, assistant, _)| (user.clone(), assistant.clone())).collect();\n\n // 记录开始处理\n redirect_log_to_ui(\"INFO\", \"开始处理用户请求...\");\n\n tokio::spawn(async move {\n // 创建流式通道\n let (stream_tx, mut stream_rx) = mpsc::unbounded_channel::();\n\n // 启动流式处理任务\n let agent_clone2 = agent_clone.clone();\n let memory_manager_clone2 = memory_manager_clone.clone();\n let config_clone2 = config_clone.clone();\n let user_info_clone2 = user_info_clone.clone();\n let user_id_clone2 = user_id_clone.clone();\n let input_clone = input.clone();\n let current_conversations_clone = current_conversations.clone();\n\n let generation_task = tokio::spawn(async move {\n agent_reply_with_memory_retrieval_streaming(\n &agent_clone2,\n memory_manager_clone2,\n &input_clone,\n &user_id_clone2,\n user_info_clone2.as_deref(),\n ¤t_conversations_clone,\n stream_tx,\n )\n .await\n });\n\n // 处理流式内容\n while let Some(chunk) = stream_rx.recv().await {\n if let Some(sender) = &msg_tx_clone {\n let _ = sender.send(AppMessage::StreamingChunk {\n user: input.clone(),\n chunk,\n });\n }\n }\n\n // 等待生成任务完成\n match generation_task.await {\n Ok(Ok(full_response)) => {\n // 发送完成消息\n if let Some(sender) = &msg_tx_clone {\n let _ = sender.send(AppMessage::StreamingComplete {\n user: input.clone(),\n full_response: full_response.clone(),\n });\n redirect_log_to_ui(\"INFO\", &format!(\"生成回复完成: {}\", full_response));\n }\n }\n Ok(Err(e)) => {\n let error_msg = format!(\"抱歉,我遇到了一些技术问题: {}\", e);\n redirect_log_to_ui(\"ERROR\", &error_msg);\n // 完成流式回复(即使出错也要清理状态)\n if let Some(sender) = &msg_tx_clone {\n let _ = sender.send(AppMessage::StreamingComplete {\n user: input.clone(),\n full_response: error_msg,\n });\n }\n }\n Err(e) => {\n let error_msg = format!(\"任务执行失败: {}\", e);\n redirect_log_to_ui(\"ERROR\", &error_msg);\n // 完成流式回复(即使出错也要清理状态)\n if let Some(sender) = &msg_tx_clone {\n let _ = sender.send(AppMessage::StreamingComplete {\n user: input.clone(),\n full_response: error_msg,\n });\n }\n }\n }\n });\n }\n }\n }\n\n // 检查是否有新的对话结果\n app.is_processing = false;\n\n // 只有在没有在shutting down状态或者记忆化已完成时才能退出\n if app.should_quit && app.memory_iteration_completed {\n break;\n }\n\n // **在quit过程中处理剩余的日志消息但不退出**\n if app.is_shutting_down && !app.memory_iteration_completed {\n // **立即处理所有待处理的日志消息**\n while let Ok(msg) = msg_rx.try_recv() {\n match msg {\n AppMessage::Log(log_msg) => {\n app.add_log(log_msg);\n }\n AppMessage::Conversation { user, assistant } => {\n app.add_conversation(user, assistant);\n }\n AppMessage::StreamingChunk { user, chunk } => {\n // 如果是新的用户输入,开始新的流式回复\n if app.current_streaming_response.is_none() ||\n app.current_streaming_response.as_ref().map(|(u, _)| u != &user).unwrap_or(false) {\n app.start_streaming_response(user);\n }\n app.add_streaming_chunk(chunk);\n }\n AppMessage::StreamingComplete { user: _, full_response: _ } => {\n app.complete_streaming_response();\n }\n AppMessage::MemoryIterationCompleted => {\n app.memory_iteration_completed = true;\n app.should_quit = true;\n break;\n }\n }\n }\n\n // 在shutting down期间立即刷新UI显示最新日志\n if let Err(e) = terminal.draw(|f| draw_ui(f, &mut app)) {\n eprintln!(\"UI绘制错误: {}\", e);\n }\n\n // 在shutting down期间添加短暂延迟,让用户能看到日志更新\n tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;\n }\n }\n\n println!(\"Cortex TARS powering down. Goodbye!\");\n Ok(())\n}\n\n/// 异步处理退出逻辑,立即退出TUI到terminal\nasync fn handle_quit_async(\n _terminal: &mut Terminal>,\n app: &mut App,\n conversations: &Vec<(String, String)>,\n memory_manager: &Arc,\n user_id: &str,\n) -> Result<(), Box> {\n use crossterm::cursor::{MoveTo, Show};\n use crossterm::style::{\n Attribute, Color, ResetColor, SetAttribute, SetBackgroundColor, SetForegroundColor,\n };\n use crossterm::{\n event::DisableMouseCapture,\n execute,\n terminal::{Clear, ClearType, LeaveAlternateScreen},\n };\n use std::io::{Write, stdout};\n\n // 记录退出命令到UI\n redirect_log_to_ui(\"INFO\", \"🚀 用户输入退出命令 /quit,开始后台记忆化...\");\n\n // 先获取所有日志内容\n let all_logs: Vec = app.logs.iter().cloned().collect();\n\n // 彻底清理terminal状态\n let mut stdout = stdout();\n\n // 执行完整的terminal重置序列\n execute!(&mut stdout, ResetColor)?;\n execute!(&mut stdout, Clear(ClearType::All))?;\n execute!(&mut stdout, MoveTo(0, 0))?;\n execute!(&mut stdout, Show)?;\n execute!(&mut stdout, LeaveAlternateScreen)?;\n execute!(&mut stdout, DisableMouseCapture)?;\n execute!(&mut stdout, SetAttribute(Attribute::Reset))?;\n execute!(&mut stdout, SetForegroundColor(Color::Reset))?;\n execute!(&mut stdout, SetBackgroundColor(Color::Reset))?;\n\n // 禁用原始模式\n let _ = crossterm::terminal::disable_raw_mode();\n\n // 刷新输出确保清理完成\n stdout.flush()?;\n\n // 输出分隔线\n println!(\"\\n╔══════════════════════════════════════════════════════════════════════════════╗\");\n println!(\"║ 🧠 Cortex Memory - 退出流程 ║\");\n println!(\"╚══════════════════════════════════════════════════════════════════════════════╝\");\n\n // 显示会话摘要\n println!(\"📋 会话摘要:\");\n println!(\" • 对话轮次: {} 轮\", conversations.len());\n println!(\" • 用户ID: {}\", user_id);\n\n // 显示最近的日志(如果有)\n if !all_logs.is_empty() {\n println!(\"\\n📜 最近的操作日志:\");\n let recent_logs = if all_logs.len() > 10 {\n &all_logs[all_logs.len() - 10..]\n } else {\n &all_logs[..]\n };\n\n println!(\" {}\", \"─\".repeat(70));\n for (i, log) in recent_logs.iter().enumerate() {\n let beautified_content = beautify_log_content(log);\n\n // 添加日志条目编号\n if i > 0 {\n println!(\" {}\", \"─\".repeat(70));\n }\n\n // 显示美化后的内容,支持多行显示\n let lines: Vec<&str> = beautified_content.split('\\n').collect();\n for (line_i, line) in lines.iter().enumerate() {\n if line_i == 0 {\n // 第一行显示编号和完整内容\n let colored_line = get_log_level_color(log, line);\n println!(\" {}\", colored_line);\n } else {\n // 后续行添加缩进\n println!(\" │ {}\", line);\n }\n }\n }\n if all_logs.len() > 10 {\n println!(\" {}\", \"─\".repeat(70));\n println!(\" ... (显示最近10条,共{}条)\", all_logs.len());\n }\n }\n\n println!(\"\\n🧠 开始执行记忆化存储...\");\n\n // 准备对话数据(过滤quit命令)\n let mut valid_conversations = Vec::new();\n for (user_msg, assistant_msg) in conversations {\n let user_msg_trimmed = user_msg.trim().to_lowercase();\n if user_msg_trimmed == \"quit\"\n || user_msg_trimmed == \"exit\"\n || user_msg_trimmed == \"/quit\"\n || user_msg_trimmed == \"/exit\"\n {\n continue;\n }\n valid_conversations.push((user_msg.clone(), assistant_msg.clone()));\n }\n\n if valid_conversations.is_empty() {\n println!(\"⚠️ 没有需要存储的内容\");\n println!(\n \"\\n╔══════════════════════════════════════════════════════════════════════════════╗\"\n );\n println!(\n \"║ ✅ 退出流程完成 ║\"\n );\n println!(\n \"╚══════════════════════════════════════════════════════════════════════════════╝\"\n );\n println!(\"👋 感谢使用Cortex Memory!\");\n return Ok(());\n }\n\n // 只有在有内容需要存储时才启动日志监听任务\n let log_dir = \"logs\".to_string();\n let log_monitoring_handle = tokio::spawn(async move {\n if let Err(e) = start_log_monitoring_task(log_dir).await {\n eprintln!(\"日志监听任务失败: {}\", e);\n }\n });\n\n println!(\n \"📝 正在保存 {} 条对话记录到记忆库...\",\n valid_conversations.len()\n );\n println!(\"🚀 开始存储对话到记忆系统...\");\n\n // 执行批量记忆化\n match store_conversations_batch(memory_manager.clone(), &valid_conversations, user_id).await {\n Ok(_) => {\n println!(\"✨ 记忆化完成!\");\n println!(\"✅ 所有对话已成功存储到记忆系统\");\n println!(\"🔍 存储详情:\");\n println!(\" • 对话轮次: {} 轮\", valid_conversations.len());\n println!(\" • 用户消息: {} 条\", valid_conversations.len());\n println!(\" • 助手消息: {} 条\", valid_conversations.len());\n }\n Err(e) => {\n println!(\"❌ 记忆存储失败: {}\", e);\n println!(\"⚠️ 虽然记忆化失败,但仍正常退出\");\n }\n }\n\n // 停止日志监听任务\n log_monitoring_handle.abort();\n\n tokio::time::sleep(Duration::from_secs(3)).await;\n\n println!(\"\\n╔══════════════════════════════════════════════════════════════════════════════╗\");\n println!(\"║ 🎉 退出流程完成 ║\");\n println!(\"╚══════════════════════════════════════════════════════════════════════════════╝\");\n println!(\"👋 感谢使用Cortex Memory!\");\n\n Ok(())\n}\n\n/// 美化日志内容显示\nfn beautify_log_content(log_line: &str) -> String {\n // 过滤掉时间戳前缀,保持简洁\n let content = if let Some(content_start) = log_line.find(\"] \") {\n &log_line[content_start + 2..]\n } else {\n log_line\n };\n\n // 判断是否为JSON内容\n let trimmed_content = content.trim();\n let is_json = trimmed_content.starts_with('{') && trimmed_content.ends_with('}');\n\n if is_json {\n // 尝试美化JSON,保留完整内容\n match prettify_json(trimmed_content) {\n Ok(formatted_json) => {\n // 如果格式化成功,返回完整的带缩进的JSON\n formatted_json\n }\n Err(_) => {\n // 如果JSON格式化失败,返回原始内容\n content.to_string()\n }\n }\n } else {\n // 非JSON内容,保持原样\n content.to_string()\n }\n}\n\n/// 美化JSON内容\nfn prettify_json(json_str: &str) -> Result> {\n use serde_json::Value;\n\n let value: Value = serde_json::from_str(json_str)?;\n Ok(serde_json::to_string_pretty(&value)?)\n}\n\n/// 根据日志级别返回带颜色的文本\nfn get_log_level_color(log_line: &str, text: &str) -> String {\n let log_level = if let Some(level_start) = log_line.find(\"[\") {\n if let Some(level_end) = log_line[level_start..].find(\"]\") {\n &log_line[level_start + 1..level_start + level_end]\n } else {\n \"UNKNOWN\"\n }\n } else {\n \"UNKNOWN\"\n };\n\n // ANSI颜色代码\n let (color_code, reset_code) = match log_level.to_uppercase().as_str() {\n \"ERROR\" => (\"\\x1b[91m\", \"\\x1b[0m\"), // 亮红色\n \"WARN\" | \"WARNING\" => (\"\\x1b[93m\", \"\\x1b[0m\"), // 亮黄色\n \"INFO\" => (\"\\x1b[36m\", \"\\x1b[0m\"), // 亮青色\n \"DEBUG\" => (\"\\x1b[94m\", \"\\x1b[0m\"), // 亮蓝色\n \"TRACE\" => (\"\\x1b[95m\", \"\\x1b[0m\"), // 亮紫色\n _ => (\"\\x1b[0m\", \"\\x1b[0m\"), // 白色\n };\n\n format!(\"{}{}{}\", color_code, text, reset_code)\n}\n" }, "complexity_metrics": { "cyclomatic_complexity": 39.0, - "lines_of_code": 557, + "lines_of_code": 559, "number_of_classes": 1, "number_of_functions": 6 }, @@ -887,6 +1265,14 @@ Code analysis results from preprocessing phase, including definitions of functio "path": null, "version": null }, + { + "dependency_type": "std", + "is_external": true, + "line_number": 9, + "name": "std", + "path": null, + "version": null + }, { "dependency_type": "crate", "is_external": true, @@ -898,7 +1284,7 @@ Code analysis results from preprocessing phase, including definitions of functio { "dependency_type": "module", "is_external": false, - "line_number": 16, + "line_number": 20, "name": "agent", "path": "./examples/cortex-mem-tars/src/agent.rs", "version": null @@ -906,7 +1292,7 @@ Code analysis results from preprocessing phase, including definitions of functio { "dependency_type": "module", "is_external": false, - "line_number": 17, + "line_number": 21, "name": "app", "path": "./examples/cortex-mem-tars/src/app.rs", "version": null @@ -914,7 +1300,7 @@ Code analysis results from preprocessing phase, including definitions of functio { "dependency_type": "module", "is_external": false, - "line_number": 18, + "line_number": 22, "name": "events", "path": "./examples/cortex-mem-tars/src/events.rs", "version": null @@ -922,7 +1308,7 @@ Code analysis results from preprocessing phase, including definitions of functio { "dependency_type": "module", "is_external": false, - "line_number": 19, + "line_number": 23, "name": "log_monitor", "path": "./examples/cortex-mem-tars/src/log_monitor.rs", "version": null @@ -930,7 +1316,7 @@ Code analysis results from preprocessing phase, including definitions of functio { "dependency_type": "module", "is_external": false, - "line_number": 20, + "line_number": 24, "name": "terminal", "path": "./examples/cortex-mem-tars/src/terminal.rs", "version": null @@ -938,31 +1324,24 @@ Code analysis results from preprocessing phase, including definitions of functio { "dependency_type": "module", "is_external": false, - "line_number": 21, + "line_number": 25, "name": "ui", "path": "./examples/cortex-mem-tars/src/ui.rs", "version": null } ], - "detailed_description": "This component serves as the main entry point for a sophisticated AI agent system with persistent memory capabilities. It implements a terminal-based user interface using ratatui and crossterm, enabling interactive conversations with an AI agent that can remember and retrieve past interactions. The system follows a clean architecture pattern with clear separation of concerns, where the main function orchestrates the initialization of all components, including configuration loading, logging setup, terminal configuration, and core system components like the LLM client and vector store. The application features a central event loop that handles user input, renders the UI, and manages asynchronous tasks through message passing via mpsc channels. A key architectural feature is the graceful shutdown process that allows the application to exit the TUI interface immediately while continuing to process memory persistence tasks in the background. The agent system uses a memory manager with Qdrant as a vector database to store and retrieve conversational context, enabling the AI to maintain continuity across sessions. The code demonstrates sophisticated error handling, resource cleanup, and user experience considerations like colored log output and session summaries during shutdown.", + "detailed_description": "This is the main application entry point for Cortex Memory TARS, a terminal-based interactive AI assistant with persistent memory capabilities. The component orchestrates the entire application lifecycle from initialization to shutdown. It integrates multiple subsystems including terminal UI (using ratatui and crossterm), logging, event handling, memory management, and AI agent interaction. The core functionality revolves around providing a continuous conversation interface where users can interact with an AI agent that maintains context across interactions through a memory system. The application features a sophisticated shutdown sequence that continues processing memory storage operations after the UI has been terminated, ensuring data persistence even during exit. Key technical aspects include async/await concurrency model, message passing between components via channels, and integration with external services like OpenAI and Qdrant vector database.", "interfaces": [ { "description": "Command line interface configuration with configuration file path parameter", "interface_type": "struct", "name": "Cli", - "parameters": [ - { - "description": "Path to configuration file, defaults to config.toml", - "is_optional": false, - "name": "config", - "param_type": "PathBuf" - } - ], + "parameters": [], "return_type": null, "visibility": "public" }, { - "description": "Application entry point that initializes components and starts the main event loop", + "description": "Entry point of the application, initializes configuration, logging and terminal, then runs the main application loop", "interface_type": "function", "name": "main", "parameters": [], @@ -970,69 +1349,69 @@ Code analysis results from preprocessing phase, including definitions of functio "visibility": "public" }, { - "description": "Main application loop that handles user input, UI rendering, and message processing", + "description": "Main application logic loop that handles events, updates state, and renders UI", "interface_type": "function", "name": "run_application", "parameters": [ { - "description": "Reference to the terminal for UI rendering", + "description": "Reference to the terminal instance for UI rendering", "is_optional": false, "name": "terminal", - "param_type": "Terminal>" + "param_type": "&mut Terminal>" } ], "return_type": "Result<(), Box>", "visibility": "private" }, { - "description": "Handles application shutdown with background memory persistence tasks", + "description": "Handles asynchronous shutdown sequence, including memory persistence operations", "interface_type": "function", "name": "handle_quit_async", "parameters": [ { - "description": "Terminal reference for cleanup", + "description": "Terminal reference (unused in function)", "is_optional": false, "name": "_terminal", - "param_type": "Terminal>" + "param_type": "&mut Terminal>" }, { - "description": "Application state", + "description": "Mutable reference to application state", "is_optional": false, "name": "app", - "param_type": "App" + "param_type": "&mut App" }, { - "description": "Conversation history to persist", + "description": "Reference to conversation history", "is_optional": false, "name": "conversations", - "param_type": "Vec<(String, String)>" + "param_type": "&Vec<(String, String)>" }, { - "description": "Memory management system", + "description": "Reference to shared memory manager", "is_optional": false, "name": "memory_manager", - "param_type": "Arc" + "param_type": "&Arc" }, { - "description": "User identifier for memory operations", + "description": "User identifier string", "is_optional": false, "name": "user_id", - "param_type": "str" + "param_type": "&str" } ], "return_type": "Result<(), Box>", "visibility": "private" }, { - "description": "Formats log content for display, with JSON pretty-printing support", + "description": "Processes log content for display, extracting meaningful content and formatting JSON", "interface_type": "function", "name": "beautify_log_content", "parameters": [ { - "description": "Raw log line to format", + "description": "Log line to beautify", "is_optional": false, "name": "log_line", - "param_type": "str" + "param_type": "&str" } ], "return_type": "String", @@ -1040,359 +1419,208 @@ Code analysis results from preprocessing phase, including definitions of functio } ], "responsibilities": [ - "Orchestrate application lifecycle including initialization and graceful shutdown", - "Manage TUI interface with ratatui and handle user input events", - "Coordinate message passing between UI, agent, and logging systems", - "Implement background memory persistence during application exit", - "Handle configuration loading and system component initialization" + "Initialize application components and dependencies", + "Manage application lifecycle from startup to shutdown", + "Orchestrate the main event loop for user interaction", + "Handle terminal UI rendering and input processing", + "Coordinate memory persistence operations during shutdown" ] }, { "code_dossier": { "code_purpose": "entry", - "description": "Cortex-Mem 核心能力评估框架的执行入口,支持多种评估模式和数据集管理", - "file_path": "examples/cortex-mem-evaluation/src/main.rs", + "description": null, + "file_path": "cortex-mem-service/src/main.rs", "functions": [ - "main" + "main", + "create_memory_manager" ], "importance_score": 1.0, "interfaces": [ + "AppState", "Cli", - "Commands", - "main" + "OptimizationJobState" ], "name": "main.rs", - "source_summary": "use anyhow::Result;\nuse clap::{Parser, Subcommand};\nuse std::path::PathBuf;\nuse tracing::info;\n\nmod evaluator;\nmod dataset;\nmod runner;\nmod report;\nmod memory;\n\nuse crate::runner::ExperimentRunner;\n\n/// Cortex-Mem 核心能力评估框架\n#[derive(Parser)]\n#[command(name = \"cortex-mem-evaluation\")]\n#[command(about = \"评估 Cortex-Mem 核心能力的框架\", long_about = None)]\nstruct Cli {\n #[command(subcommand)]\n command: Commands,\n}\n\n#[derive(Subcommand)]\nenum Commands {\n /// 运行完整评估\n Run {\n /// 配置文件路径\n #[arg(short, long, default_value = \"config/evaluation_config.toml\")]\n config: PathBuf,\n \n /// 输出目录\n #[arg(short, long, default_value = \"results\")]\n output_dir: PathBuf,\n },\n \n /// 仅运行召回率评估\n Recall {\n /// 配置文件路径\n #[arg(short, long, default_value = \"config/evaluation_config.toml\")]\n config: PathBuf,\n \n /// 输出目录\n #[arg(short, long, default_value = \"results\")]\n output_dir: PathBuf,\n },\n \n /// 仅运行有效性评估\n Effectiveness {\n /// 配置文件路径\n #[arg(short, long, default_value = \"config/evaluation_config.toml\")]\n config: PathBuf,\n \n /// 输出目录\n #[arg(short, long, default_value = \"results\")]\n output_dir: PathBuf,\n },\n \n /// 仅运行性能评估\n Performance {\n /// 配置文件路径\n #[arg(short, long, default_value = \"config/evaluation_config.toml\")]\n config: PathBuf,\n \n /// 输出目录\n #[arg(short, long, default_value = \"results\")]\n output_dir: PathBuf,\n },\n \n /// 生成测试数据集\n GenerateDataset {\n /// 数据集类型:recall, effectiveness, all\n #[arg(short, long, default_value = \"all\")]\n dataset_type: String,\n \n /// 输出目录\n #[arg(short, long, default_value = \"data\")]\n output_dir: PathBuf,\n \n /// 数据集大小\n #[arg(short, long, default_value = \"100\")]\n size: usize,\n \n /// 是否使用实验室数据\n #[arg(long, default_value = \"true\")]\n use_lab_data: bool,\n },\n \n /// 验证测试数据集\n ValidateDataset {\n /// 数据集路径\n #[arg(short, long)]\n dataset_path: PathBuf,\n \n /// 数据集类型:recall, effectiveness\n #[arg(short, long)]\n dataset_type: String,\n },\n}\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n // 初始化日志\n tracing_subscriber::fmt()\n .with_max_level(tracing::Level::INFO)\n .with_target(false)\n .init();\n \n let cli = Cli::parse();\n \n match cli.command {\n Commands::Run { config, output_dir } => {\n info!(\"开始运行完整评估...\");\n let runner = ExperimentRunner::new(config, output_dir)?;\n runner.run_full_evaluation().await?;\n info!(\"评估完成!\");\n }\n \n Commands::Recall { config, output_dir } => {\n info!(\"开始运行召回率评估...\");\n let runner = ExperimentRunner::new(config, output_dir)?;\n runner.run_recall_evaluation().await?;\n info!(\"召回率评估完成!\");\n }\n \n Commands::Effectiveness { config, output_dir } => {\n info!(\"开始运行有效性评估...\");\n let runner = ExperimentRunner::new(config, output_dir)?;\n runner.run_effectiveness_evaluation().await?;\n info!(\"有效性评估完成!\");\n }\n \n Commands::Performance { config, output_dir } => {\n info!(\"开始运行性能评估...\");\n let runner = ExperimentRunner::new(config, output_dir)?;\n runner.run_full_evaluation().await?;\n info!(\"性能评估完成!\");\n }\n \n Commands::GenerateDataset { dataset_type, output_dir, size, use_lab_data } => {\n info!(\"开始生成测试数据集...\");\n crate::dataset::generate_test_dataset(&dataset_type, &output_dir, size, use_lab_data).await?;\n info!(\"测试数据集生成完成!\");\n }\n \n Commands::ValidateDataset { dataset_path, dataset_type } => {\n info!(\"开始验证测试数据集...\");\n crate::dataset::validate_dataset(&dataset_path, &dataset_type).await?;\n info!(\"测试数据集验证完成!\");\n }\n }\n \n Ok(())\n}" + "source_summary": "use axum::{\n Router,\n routing::{get, post},\n};\nuse clap::Parser;\nuse cortex_mem_core::{\n config::Config, llm::create_llm_client, memory::MemoryManager,\n vector_store::qdrant::QdrantVectorStore,\n};\nuse std::{path::PathBuf, sync::Arc};\nuse tokio::net::TcpListener;\nuse tower::ServiceBuilder;\nuse tower_http::cors::CorsLayer;\nuse tracing::info;\nuse tracing_subscriber;\n\nmod handlers;\nmod models;\nmod optimization_handlers;\n\nuse handlers::{\n\n batch_delete_memories, batch_update_memories, create_memory, delete_memory, get_memory, health_check, list_memories, search_memories, update_memory, get_llm_status, llm_health_check,\n\n};\nuse optimization_handlers::{\n analyze_optimization, cancel_optimization, cleanup_history, get_optimization_history,\n get_optimization_statistics, get_optimization_status, start_optimization,\n OptimizationJobState,\n};\n\n/// Application state shared across handlers\n#[derive(Clone)]\npub struct AppState {\n pub memory_manager: Arc,\n pub optimization_jobs: Arc>>,\n}\n\n#[derive(Parser)]\n#[command(name = \"cortex-mem-service\")]\n#[command(about = \"Cortex Memory HTTP Service\")]\n#[command(author = \"Sopaco\")]\n#[command(version)]\nstruct Cli {\n /// Path to the configuration file\n #[arg(short, long, default_value = \"config.toml\")]\n config: PathBuf,\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n // Initialize tracing\n tracing_subscriber::fmt::init();\n\n let cli = Cli::parse();\n\n // Load configuration\n let config = Config::load(&cli.config)?;\n\n // Create memory manager\n let memory_manager = create_memory_manager(&config).await?;\n\n // Create application state\n let app_state = AppState {\n memory_manager: Arc::new(memory_manager),\n optimization_jobs: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())),\n };\n\n // Build the application router\n let app = Router::new()\n .route(\"/health\", get(health_check))\n .route(\"/memories\", post(create_memory).get(list_memories))\n .route(\"/memories/search\", post(search_memories))\n .route(\n \"/memories/{id}\",\n get(get_memory).put(update_memory).delete(delete_memory),\n )\n .route(\"/memories/batch/delete\", post(batch_delete_memories))\n .route(\"/memories/batch/update\", post(batch_update_memories))\n // Optimization routes\n .route(\"/optimization\", post(start_optimization))\n .route(\"/optimization/{job_id}\", get(get_optimization_status))\n .route(\"/optimization/{job_id}/cancel\", post(cancel_optimization))\n .route(\"/optimization/history\", get(get_optimization_history))\n .route(\"/optimization/analyze\", post(analyze_optimization))\n .route(\"/optimization/statistics\", get(get_optimization_statistics))\n .route(\"/optimization/cleanup\", post(cleanup_history))\n // LLM service status routes\n .route(\"/llm/status\", get(get_llm_status))\n .route(\"/llm/health-check\", get(llm_health_check))\n .layer(\n ServiceBuilder::new()\n .layer(CorsLayer::permissive())\n .into_inner(),\n )\n .with_state(app_state);\n\n // Start the server\n let addr = format!(\"{}:{}\", config.server.host, config.server.port);\n\n info!(\"Starting cortex-mem-service on {}\", addr);\n\n let listener = TcpListener::bind(&addr).await?;\n axum::serve(listener, app).await?;\n\n Ok(())\n}\n\nasync fn create_memory_manager(\n config: &Config,\n) -> Result> {\n // Create vector store\n let vector_store = QdrantVectorStore::new(&config.qdrant).await?;\n\n // Create LLM client\n let llm_client = create_llm_client(&config.llm, &config.embedding)?;\n\n // Create memory manager\n let memory_manager =\n MemoryManager::new(Box::new(vector_store), llm_client, config.memory.clone());\n\n info!(\"Memory manager initialized successfully\");\n Ok(memory_manager)\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 2.0, - "lines_of_code": 153, - "number_of_classes": 2, - "number_of_functions": 1 + "cyclomatic_complexity": 1.0, + "lines_of_code": 124, + "number_of_classes": 3, + "number_of_functions": 2 }, "dependencies": [ { - "dependency_type": "error_handling", + "dependency_type": "package", "is_external": true, - "line_number": 1, - "name": "anyhow", + "line_number": null, + "name": "axum", "path": null, "version": null }, { - "dependency_type": "cli_parsing", + "dependency_type": "package", "is_external": true, - "line_number": 2, + "line_number": null, "name": "clap", "path": null, "version": null }, { - "dependency_type": "standard_library", + "dependency_type": "module", "is_external": false, - "line_number": 3, - "name": "std::path::PathBuf", - "path": null, + "line_number": null, + "name": "cortex_mem_core", + "path": "cortex_mem_core", "version": null }, { - "dependency_type": "logging", + "dependency_type": "package", "is_external": true, - "line_number": 4, - "name": "tracing", + "line_number": null, + "name": "std", "path": null, "version": null }, { - "dependency_type": "async_runtime", + "dependency_type": "package", "is_external": true, - "line_number": 153, + "line_number": null, "name": "tokio", "path": null, "version": null }, { - "dependency_type": "internal_module", - "is_external": false, - "line_number": 8, - "name": "evaluator", - "path": "./evaluator", + "dependency_type": "package", + "is_external": true, + "line_number": null, + "name": "tower", + "path": null, "version": null }, { - "dependency_type": "internal_module", - "is_external": false, - "line_number": 9, - "name": "dataset", - "path": "./dataset", + "dependency_type": "package", + "is_external": true, + "line_number": null, + "name": "tower_http", + "path": null, "version": null }, { - "dependency_type": "internal_module", - "is_external": false, - "line_number": 10, - "name": "runner", - "path": "./runner", + "dependency_type": "package", + "is_external": true, + "line_number": null, + "name": "tracing", + "path": null, "version": null }, { - "dependency_type": "internal_module", + "dependency_type": "package", + "is_external": true, + "line_number": null, + "name": "tracing_subscriber", + "path": null, + "version": null + }, + { + "dependency_type": "module", "is_external": false, - "line_number": 11, - "name": "report", - "path": "./report", + "line_number": null, + "name": "handlers", + "path": "./cortex-mem-service/src/handlers.rs", "version": null }, { - "dependency_type": "internal_module", + "dependency_type": "module", "is_external": false, - "line_number": 12, - "name": "memory", - "path": "./memory", + "line_number": null, + "name": "models", + "path": "./cortex-mem-service/src/models.rs", "version": null }, { - "dependency_type": "internal_struct", + "dependency_type": "module", "is_external": false, - "line_number": 14, - "name": "ExperimentRunner", - "path": "./runner", + "line_number": null, + "name": "optimization_handlers", + "path": "./cortex-mem-service/src/optimization_handlers.rs", "version": null } ], - "detailed_description": "该组件是Cortex-Mem核心能力评估框架的主执行入口,基于Rust语言开发,使用Clap库构建命令行接口。组件支持多种评估模式:完整评估、召回率评估、有效性评估、性能评估,以及测试数据集的生成与验证功能。通过命令行参数配置评估的输入(配置文件路径)和输出(结果目录),利用tokio异步运行时执行各项评估任务。主函数解析命令行参数后,根据不同的子命令调用对应的处理逻辑,主要依赖ExperimentRunner执行具体的评估流程,或调用dataset模块进行数据集管理。组件作为整个评估系统的统一入口,协调各子系统的工作。", + "detailed_description": "Main entry point for the Cortex Memory HTTP Service, responsible for initializing the server, parsing command-line arguments, loading configuration, creating the memory manager, and setting up the HTTP routes. It uses Axum for routing and handler management, integrates with Qdrant for vector storage, and provides a RESTful API for memory operations including CRUD, search, batch operations, optimization, and health checks. The application state is shared across handlers via Arc-wrapped components for thread-safe access.", "interfaces": [ { - "description": "命令行接口主结构,定义程序名称和描述", + "description": "Shared application state containing the memory manager and optimization job tracking", "interface_type": "struct", - "name": "Cli", + "name": "AppState", "parameters": [], "return_type": null, "visibility": "public" }, { - "description": "定义所有支持的子命令及其参数", - "interface_type": "enum", - "name": "Commands", + "description": "Command-line interface arguments parser using Clap", + "interface_type": "struct", + "name": "Cli", "parameters": [], "return_type": null, "visibility": "public" }, { - "description": "程序主入口函数,初始化环境并根据命令执行相应逻辑", - "interface_type": "function", - "name": "main", + "description": "State tracking structure for memory optimization jobs", + "interface_type": "struct", + "name": "OptimizationJobState", "parameters": [], - "return_type": "Result<()>", + "return_type": null, "visibility": "public" } ], "responsibilities": [ - "解析命令行参数并路由到对应的评估或数据集操作", - "初始化日志系统以支持运行时信息记录", - "协调调用评估执行器(ExperimentRunner)完成各类评估任务", - "管理评估流程的生命周期和状态输出", - "提供数据集生成和验证的命令行接口" + "Initialize and configure the HTTP server with Axum", + "Parse command-line arguments and load configuration", + "Create and configure the memory manager with vector store and LLM client", + "Set up all API routes for memory operations and system health checks", + "Manage shared application state across request handlers" ] }, { "code_dossier": { - "code_purpose": "entry", - "description": "Project execution entry point for the Cortex Memory Service, responsible for initializing the application, setting up the HTTP server with Axum, parsing command-line arguments, loading configuration, creating the memory manager, and routing requests to appropriate handlers.", - "file_path": "cortex-mem-service/src/main.rs", + "code_purpose": "config", + "description": "Main configuration module defining various service configurations including Qdrant, LLM, server, embedding, memory, and logging settings.", + "file_path": "cortex-mem-config/src/lib.rs", "functions": [ - "main", - "create_memory_manager" + "Config::load" ], - "importance_score": 1.0, + "importance_score": 0.9, "interfaces": [ - "AppState", - "Cli" + "Config", + "QdrantConfig", + "LLMConfig", + "ServerConfig", + "EmbeddingConfig", + "MemoryConfig", + "LoggingConfig" ], - "name": "main.rs", - "source_summary": "use axum::{\n Router,\n routing::{get, post},\n};\nuse clap::Parser;\nuse cortex_mem_core::{\n config::Config, llm::create_llm_client, memory::MemoryManager,\n vector_store::qdrant::QdrantVectorStore,\n};\nuse std::{path::PathBuf, sync::Arc};\nuse tokio::net::TcpListener;\nuse tower::ServiceBuilder;\nuse tower_http::cors::CorsLayer;\nuse tracing::info;\nuse tracing_subscriber;\n\nmod handlers;\nmod models;\nmod optimization_handlers;\n\nuse handlers::{\n\n batch_delete_memories, batch_update_memories, create_memory, delete_memory, get_memory, health_check, list_memories, search_memories, update_memory, get_llm_status, llm_health_check,\n\n};\nuse optimization_handlers::{\n analyze_optimization, cancel_optimization, cleanup_history, get_optimization_history,\n get_optimization_statistics, get_optimization_status, start_optimization,\n OptimizationJobState,\n};\n\n/// Application state shared across handlers\n#[derive(Clone)]\npub struct AppState {\n pub memory_manager: Arc,\n pub optimization_jobs: Arc>>,\n}\n\n#[derive(Parser)]\n#[command(name = \"cortex-mem-service\")]\n#[command(about = \"Rust Agent Memory System HTTP Service\")]\nstruct Cli {\n /// Path to the configuration file\n #[arg(short, long, default_value = \"config.toml\")]\n config: PathBuf,\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n // Initialize tracing\n tracing_subscriber::fmt::init();\n\n let cli = Cli::parse();\n\n // Load configuration\n let config = Config::load(&cli.config)?;\n\n // Create memory manager\n let memory_manager = create_memory_manager(&config).await?;\n\n // Create application state\n let app_state = AppState {\n memory_manager: Arc::new(memory_manager),\n optimization_jobs: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())),\n };\n\n // Build the application router\n let app = Router::new()\n .route(\"/health\", get(health_check))\n .route(\"/memories\", post(create_memory).get(list_memories))\n .route(\"/memories/search\", post(search_memories))\n .route(\n \"/memories/{id}\",\n get(get_memory).put(update_memory).delete(delete_memory),\n )\n .route(\"/memories/batch/delete\", post(batch_delete_memories))\n .route(\"/memories/batch/update\", post(batch_update_memories))\n // Optimization routes\n .route(\"/optimization\", post(start_optimization))\n .route(\"/optimization/{job_id}\", get(get_optimization_status))\n .route(\"/optimization/{job_id}/cancel\", post(cancel_optimization))\n .route(\"/optimization/history\", get(get_optimization_history))\n .route(\"/optimization/analyze\", post(analyze_optimization))\n .route(\"/optimization/statistics\", get(get_optimization_statistics))\n .route(\"/optimization/cleanup\", post(cleanup_history))\n // LLM service status routes\n .route(\"/llm/status\", get(get_llm_status))\n .route(\"/llm/health-check\", get(llm_health_check))\n .layer(\n ServiceBuilder::new()\n .layer(CorsLayer::permissive())\n .into_inner(),\n )\n .with_state(app_state);\n\n // Start the server\n let addr = format!(\"{}:{}\", config.server.host, config.server.port);\n\n info!(\"Starting cortex-mem-service on {}\", addr);\n\n let listener = TcpListener::bind(&addr).await?;\n axum::serve(listener, app).await?;\n\n Ok(())\n}\n\nasync fn create_memory_manager(\n config: &Config,\n) -> Result> {\n // Create vector store\n let vector_store = QdrantVectorStore::new(&config.qdrant).await?;\n\n // Create LLM client\n let llm_client = create_llm_client(&config.llm, &config.embedding)?;\n\n // Create memory manager\n let memory_manager =\n MemoryManager::new(Box::new(vector_store), llm_client, config.memory.clone());\n\n info!(\"Memory manager initialized successfully\");\n Ok(memory_manager)\n}\n" + "name": "lib.rs", + "source_summary": "use anyhow::Result;\nuse serde::{Deserialize, Serialize};\nuse std::path::Path;\n\n/// Main configuration structure\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct Config {\n pub qdrant: QdrantConfig,\n pub llm: LLMConfig,\n pub server: ServerConfig,\n pub embedding: EmbeddingConfig,\n pub memory: MemoryConfig,\n pub logging: LoggingConfig,\n}\n\n/// Qdrant vector database configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct QdrantConfig {\n pub url: String,\n pub collection_name: String,\n pub embedding_dim: Option,\n pub timeout_secs: u64,\n}\n\n/// LLM configuration for rig framework\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LLMConfig {\n pub api_base_url: String,\n pub api_key: String,\n pub model_efficient: String,\n pub temperature: f32,\n pub max_tokens: u32,\n}\n\n/// HTTP server configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ServerConfig {\n pub host: String,\n pub port: u16,\n pub cors_origins: Vec,\n}\n\n/// Embedding service configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EmbeddingConfig {\n pub api_base_url: String,\n pub model_name: String,\n pub api_key: String,\n pub batch_size: usize,\n pub timeout_secs: u64,\n}\n\n/// Memory manager configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct MemoryConfig {\n pub max_memories: usize,\n pub similarity_threshold: f32,\n pub max_search_results: usize,\n pub memory_ttl_hours: Option,\n pub auto_summary_threshold: usize,\n pub auto_enhance: bool,\n pub deduplicate: bool,\n pub merge_threshold: f32,\n pub search_similarity_threshold: Option,\n}\n\n/// Logging configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LoggingConfig {\n pub enabled: bool,\n pub log_directory: String,\n pub level: String,\n}\n\nimpl Config {\n /// Load configuration from a TOML file\n pub fn load>(path: P) -> Result {\n let content = std::fs::read_to_string(path)?;\n let config: Config = toml::from_str(&content)?;\n Ok(config)\n }\n}\n\nimpl Default for MemoryConfig {\n fn default() -> Self {\n MemoryConfig {\n max_memories: 10000,\n similarity_threshold: 0.65,\n max_search_results: 50,\n memory_ttl_hours: None,\n auto_summary_threshold: 32768,\n auto_enhance: true,\n deduplicate: true,\n merge_threshold: 0.75,\n search_similarity_threshold: Some(0.70),\n }\n }\n}\n\nimpl Default for LoggingConfig {\n fn default() -> Self {\n LoggingConfig {\n enabled: false,\n log_directory: \"logs\".to_string(),\n level: \"info\".to_string(),\n }\n }\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 122, - "number_of_classes": 2, - "number_of_functions": 2 + "cyclomatic_complexity": 4.0, + "lines_of_code": 108, + "number_of_classes": 7, + "number_of_functions": 3 }, "dependencies": [ { - "dependency_type": "framework", + "dependency_type": "error_handling", "is_external": true, "line_number": 1, - "name": "axum", + "name": "anyhow", "path": null, "version": null }, { - "dependency_type": "cli", - "is_external": true, - "line_number": 6, - "name": "clap", - "path": null, - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": 7, - "name": "cortex_mem_core", - "path": "./cortex-mem-core", - "version": null - }, - { - "dependency_type": "standard_library", - "is_external": false, - "line_number": 10, - "name": "std", - "path": null, - "version": null - }, - { - "dependency_type": "runtime", - "is_external": true, - "line_number": 11, - "name": "tokio", - "path": null, - "version": null - }, - { - "dependency_type": "middleware", - "is_external": true, - "line_number": 12, - "name": "tower", - "path": null, - "version": null - }, - { - "dependency_type": "middleware", - "is_external": true, - "line_number": 13, - "name": "tower_http", - "path": null, - "version": null - }, - { - "dependency_type": "logging", - "is_external": true, - "line_number": 14, - "name": "tracing", - "path": null, - "version": null - }, - { - "dependency_type": "logging", - "is_external": true, - "line_number": 15, - "name": "tracing_subscriber", - "path": null, - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": 21, - "name": "handlers", - "path": "./cortex-mem-service/src/handlers.rs", - "version": null - } - ], - "detailed_description": "This file serves as the main entry point for the Cortex Memory Service, a Rust-based HTTP service for managing agent memories. It uses Axum for routing HTTP requests, Tokio for async runtime, and Clap for command-line argument parsing. The application initializes a tracing subscriber for logging, parses command-line arguments to get the config file path (defaulting to 'config.toml'), loads the configuration, creates a MemoryManager instance (via create_memory_manager helper), and sets up shared application state (AppState) containing the memory manager and optimization job tracking. The router is built with numerous endpoints for CRUD operations on memories (create, read, update, delete, search, list), batch operations, health checks, and optimization tasks (start, status, cancel, cleanup). It also includes routes for checking LLM service status. The server binds to a TCP listener based on the config and starts serving requests. The create_memory_manager function is responsible for initializing the Qdrant vector store, creating an LLM client, and constructing the MemoryManager, which is the core service handling all memory operations.", - "interfaces": [ - { - "description": "Shared application state passed to all request handlers. Contains the MemoryManager for data operations and a thread-safe map to track optimization job states.", - "interface_type": "struct", - "name": "AppState", - "parameters": [ - { - "description": "Thread-safe reference to the core memory management service", - "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" - }, - { - "description": "Thread-safe storage for tracking the state of ongoing memory optimization jobs", - "is_optional": false, - "name": "optimization_jobs", - "param_type": "Arc>>" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Command-line interface configuration parsed using Clap. Defines the arguments the application accepts when started.", - "interface_type": "struct", - "name": "Cli", - "parameters": [ - { - "description": "Path to the configuration file, defaults to 'config.toml'", - "is_optional": false, - "name": "config", - "param_type": "PathBuf" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "The primary entry point of the application. Initializes the runtime, parses CLI args, loads config, sets up services, builds the router, and starts the HTTP server.", - "interface_type": "function", - "name": "main", - "parameters": [], - "return_type": "Result<(), Box>", - "visibility": "public" - } - ], - "responsibilities": [ - "Initialize the application runtime and logging infrastructure", - "Parse command-line arguments and load application configuration", - "Construct and initialize core service components (MemoryManager, LLM client, vector store)", - "Define and configure the HTTP API router with all endpoints", - "Manage shared application state and server lifecycle" - ] - }, - { - "code_dossier": { - "code_purpose": "config", - "description": "Main configuration module defining various service configurations including Qdrant, LLM, server, embedding, memory, and logging settings.", - "file_path": "cortex-mem-config/src/lib.rs", - "functions": [ - "Config::load" - ], - "importance_score": 0.9, - "interfaces": [ - "Config", - "QdrantConfig", - "LLMConfig", - "ServerConfig", - "EmbeddingConfig", - "MemoryConfig", - "LoggingConfig" - ], - "name": "lib.rs", - "source_summary": "use anyhow::Result;\nuse serde::{Deserialize, Serialize};\nuse std::path::Path;\n\n/// Main configuration structure\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct Config {\n pub qdrant: QdrantConfig,\n pub llm: LLMConfig,\n pub server: ServerConfig,\n pub embedding: EmbeddingConfig,\n pub memory: MemoryConfig,\n pub logging: LoggingConfig,\n}\n\n/// Qdrant vector database configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct QdrantConfig {\n pub url: String,\n pub collection_name: String,\n pub embedding_dim: Option,\n pub timeout_secs: u64,\n}\n\n/// LLM configuration for rig framework\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LLMConfig {\n pub api_base_url: String,\n pub api_key: String,\n pub model_efficient: String,\n pub temperature: f32,\n pub max_tokens: u32,\n}\n\n/// HTTP server configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ServerConfig {\n pub host: String,\n pub port: u16,\n pub cors_origins: Vec,\n}\n\n/// Embedding service configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EmbeddingConfig {\n pub api_base_url: String,\n pub model_name: String,\n pub api_key: String,\n pub batch_size: usize,\n pub timeout_secs: u64,\n}\n\n/// Memory manager configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct MemoryConfig {\n pub max_memories: usize,\n pub similarity_threshold: f32,\n pub max_search_results: usize,\n pub memory_ttl_hours: Option,\n pub auto_summary_threshold: usize,\n pub auto_enhance: bool,\n pub deduplicate: bool,\n pub merge_threshold: f32,\n pub search_similarity_threshold: Option,\n}\n\n/// Logging configuration\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LoggingConfig {\n pub enabled: bool,\n pub log_directory: String,\n pub level: String,\n}\n\nimpl Config {\n /// Load configuration from a TOML file\n pub fn load>(path: P) -> Result {\n let content = std::fs::read_to_string(path)?;\n let config: Config = toml::from_str(&content)?;\n Ok(config)\n }\n}\n\nimpl Default for MemoryConfig {\n fn default() -> Self {\n MemoryConfig {\n max_memories: 10000,\n similarity_threshold: 0.65,\n max_search_results: 50,\n memory_ttl_hours: None,\n auto_summary_threshold: 32768,\n auto_enhance: true,\n deduplicate: true,\n merge_threshold: 0.75,\n search_similarity_threshold: Some(0.70),\n }\n }\n}\n\nimpl Default for LoggingConfig {\n fn default() -> Self {\n LoggingConfig {\n enabled: false,\n log_directory: \"logs\".to_string(),\n level: \"info\".to_string(),\n }\n }\n}\n" - }, - "complexity_metrics": { - "cyclomatic_complexity": 4.0, - "lines_of_code": 108, - "number_of_classes": 7, - "number_of_functions": 3 - }, - "dependencies": [ - { - "dependency_type": "error_handling", - "is_external": true, - "line_number": 1, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "serialization", + "dependency_type": "serialization", "is_external": true, "line_number": 2, "name": "serde", @@ -1508,633 +1736,492 @@ Code analysis results from preprocessing phase, including definitions of functio }, { "code_dossier": { - "code_purpose": "command", - "description": "Command-line interface command for adding memory entries to the system, supporting both simple content storage and structured conversation input.", - "file_path": "cortex-mem-cli/src/commands/add.rs", + "code_purpose": "util", + "description": "Configuration utilities for reading, validating, and extracting values from a TOML config file used in Cortex Mem evaluation. Ensures required sections and fields are present, particularly for LLM, embedding, Qdrant, and memory configurations.", + "file_path": "examples/lomoco-evaluation/src/cortex_mem/config_utils.py", "functions": [ - "new", - "execute", - "parse_conversation_content" + "validate_config", + "get_config_value", + "check_openai_config" ], - "importance_score": 0.8, + "importance_score": 0.9, "interfaces": [ - "AddCommand::new", - "AddCommand::execute", - "parse_conversation_content" + "validate_config", + "get_config_value", + "check_openai_config" ], - "name": "add.rs", - "source_summary": "use cortex_mem_core::{\n memory::MemoryManager,\n types::{MemoryMetadata, MemoryType, Message},\n};\nuse tracing::{error, info};\n\npub struct AddCommand {\n memory_manager: MemoryManager,\n}\n\nimpl AddCommand {\n pub fn new(memory_manager: MemoryManager) -> Self {\n Self { memory_manager }\n }\n\n pub async fn execute(\n &self,\n content: String,\n user_id: Option,\n agent_id: Option,\n memory_type: String,\n ) -> Result<(), Box> {\n let memory_type = MemoryType::parse(&memory_type);\n\n let mut metadata = MemoryMetadata::new(memory_type.to_owned());\n\n if let Some(ref user_id) = user_id {\n metadata = metadata.with_user_id(user_id.to_owned());\n }\n\n if let Some(ref agent_id) = agent_id {\n metadata = metadata.with_agent_id(agent_id.to_owned());\n }\n\n // Check if this should be handled as a conversation (for procedural memory or advanced fact extraction)\n let is_conversation = memory_type == MemoryType::Procedural\n || content.contains('\\n')\n || content.contains(\"Assistant:\")\n || content.contains(\"User:\");\n\n if is_conversation {\n // Handle as conversation for advanced processing\n let messages = if content.contains('\\n')\n || content.contains(\"User:\")\n || content.contains(\"Assistant:\")\n {\n // Parse conversation format\n parse_conversation_content(&content, &user_id, &agent_id)\n } else {\n // Single user message\n vec![Message {\n role: \"user\".to_string(),\n content: content.clone(),\n name: user_id.clone(),\n }]\n };\n\n match self.memory_manager.add_memory(&messages, metadata).await {\n Ok(results) => {\n info!(\"Memory added successfully with {} actions\", results.len());\n println!(\"✅ Memory added successfully!\");\n println!(\"Memory Type: {:?}\", memory_type);\n println!(\"Actions Performed: {}\", results.len());\n\n for (i, result) in results.iter().enumerate() {\n println!(\n \" {}. {:?} - {}\",\n i + 1,\n result.event,\n result.memory.chars().take(100).collect::()\n );\n if result.memory.len() > 100 {\n println!(\" (truncated)\");\n }\n }\n }\n Err(e) => {\n error!(\"Failed to add memory: {}\", e);\n println!(\"❌ Failed to add memory: {}\", e);\n return Err(e.into());\n }\n }\n } else {\n // Handle as simple content storage\n match self.memory_manager.store(content.clone(), metadata).await {\n Ok(memory_id) => {\n info!(\"Memory stored successfully with ID: {}\", memory_id);\n println!(\"✅ Memory added successfully!\");\n println!(\"ID: {}\", memory_id);\n println!(\"Content: {}\", content.chars().take(100).collect::());\n if content.len() > 100 {\n println!(\"(truncated)\");\n }\n }\n Err(e) => {\n error!(\"Failed to store memory: {}\", e);\n println!(\"❌ Failed to add memory: {}\", e);\n return Err(e.into());\n }\n }\n }\n\n Ok(())\n }\n}\n\n/// Parse conversation content from CLI input\nfn parse_conversation_content(\n content: &str,\n user_id: &Option,\n agent_id: &Option,\n) -> Vec {\n let mut messages = Vec::new();\n let lines: Vec<&str> = content.lines().collect();\n\n for line in lines {\n let trimmed = line.trim();\n if trimmed.is_empty() {\n continue;\n }\n\n if trimmed.starts_with(\"User:\") || trimmed.starts_with(\"user:\") {\n let user_content = trimmed[5..].trim();\n messages.push(Message {\n role: \"user\".to_string(),\n content: user_content.to_string(),\n name: user_id.clone(),\n });\n } else if trimmed.starts_with(\"Assistant:\")\n || trimmed.starts_with(\"assistant:\")\n || trimmed.starts_with(\"AI:\")\n {\n let assistant_content = trimmed[10..].trim();\n messages.push(Message {\n role: \"assistant\".to_string(),\n content: assistant_content.to_string(),\n name: agent_id.clone(),\n });\n } else {\n // If no role prefix, treat as user message\n messages.push(Message {\n role: \"user\".to_string(),\n content: trimmed.to_string(),\n name: user_id.clone(),\n });\n }\n }\n\n // If no messages were parsed, treat entire content as user message\n if messages.is_empty() {\n messages.push(Message {\n role: \"user\".to_string(),\n content: content.to_string(),\n name: user_id.clone(),\n });\n }\n\n messages\n}\n" + "name": "config_utils.py", + "source_summary": "\"\"\"\nConfiguration utilities for Cortex Mem evaluation.\nHandles config.toml file reading and validation.\n\"\"\"\n\nimport os\nfrom pathlib import Path\n\n\ndef validate_config(config_path: str) -> bool:\n \"\"\"Validate that config file exists and has required settings.\"\"\"\n if not os.path.exists(config_path):\n print(f\"Config file not found: {config_path}\")\n return False\n \n try:\n with open(config_path, 'r') as f:\n content = f.read()\n \n # Check for required sections\n required_sections = [\"llm\", \"embedding\", \"qdrant\", \"memory\"]\n missing_sections = []\n \n for section in required_sections:\n if f\"[{section}]\" not in content:\n missing_sections.append(section)\n \n if missing_sections:\n print(f\"Missing required sections in config: {missing_sections}\")\n return False\n \n # Check for required fields in each section\n import toml\n \n config_data = toml.load(config_path)\n \n # Check llm section\n if \"llm\" in config_data:\n llm = config_data[\"llm\"]\n required_llm_fields = [\"api_key\", \"api_base_url\", \"model_efficient\"]\n missing_llm = [field for field in required_llm_fields if field not in llm]\n if missing_llm:\n print(f\"Missing fields in [llm] section: {missing_llm}\")\n return False\n \n # Check embedding section\n if \"embedding\" in config_data:\n embedding = config_data[\"embedding\"]\n required_embedding_fields = [\"api_key\", \"api_base_url\", \"model_name\"]\n missing_embedding = [field for field in required_embedding_fields if field not in embedding]\n if missing_embedding:\n print(f\"Missing fields in [embedding] section: {missing_embedding}\")\n return False\n \n # Check qdrant section\n if \"qdrant\" in config_data:\n qdrant = config_data[\"qdrant\"]\n required_qdrant_fields = [\"url\", \"collection_name\"]\n missing_qdrant = [field for field in required_qdrant_fields if field not in qdrant]\n if missing_qdrant:\n print(f\"Missing fields in [qdrant] section: {missing_qdrant}\")\n return False\n \n return True\n \n except Exception as e:\n print(f\"Error validating config: {e}\")\n return False\n\n\ndef get_config_value(config_path: str, section: str, key: str, default=None):\n \"\"\"Get a specific value from config file.\"\"\"\n try:\n import toml\n config_data = toml.load(config_path)\n \n if section in config_data and key in config_data[section]:\n return config_data[section][key]\n return default\n except:\n return default\n\n\ndef check_openai_config(config_path: str) -> bool:\n \"\"\"Check if OpenAI configuration is properly set.\"\"\"\n try:\n import toml\n config_data = toml.load(config_path)\n \n # Check llm section\n if \"llm\" not in config_data:\n print(\"Missing [llm] section in config\")\n return False\n \n llm = config_data[\"llm\"]\n if \"api_key\" not in llm or not llm[\"api_key\"]:\n print(\"OpenAI API key not set in [llm] section\")\n return False\n \n if \"api_base_url\" not in llm or not llm[\"api_base_url\"]:\n print(\"OpenAI API base URL not set in [llm] section\")\n return False\n \n # Check embedding section\n if \"embedding\" not in config_data:\n print(\"Missing [embedding] section in config\")\n return False\n \n embedding = config_data[\"embedding\"]\n if \"api_key\" not in embedding or not embedding[\"api_key\"]:\n print(\"OpenAI API key not set in [embedding] section\")\n return False\n \n return True\n \n except Exception as e:\n print(f\"Error checking OpenAI config: {e}\")\n return False" }, "complexity_metrics": { - "cyclomatic_complexity": 18.0, - "lines_of_code": 159, - "number_of_classes": 1, + "cyclomatic_complexity": 27.0, + "lines_of_code": 118, + "number_of_classes": 0, "number_of_functions": 3 }, "dependencies": [ { - "dependency_type": "import", - "is_external": false, - "line_number": 1, - "name": "cortex_mem_core::memory::MemoryManager", - "path": "cortex-mem-core/src/memory/mod.rs", - "version": null - }, - { - "dependency_type": "import", + "dependency_type": "standard_library", "is_external": false, - "line_number": 2, - "name": "cortex_mem_core::types::MemoryMetadata", - "path": "cortex-mem-core/src/types/mod.rs", + "line_number": 6, + "name": "os", + "path": null, "version": null }, { - "dependency_type": "import", + "dependency_type": "standard_library", "is_external": false, - "line_number": 2, - "name": "cortex_mem_core::types::MemoryType", - "path": "cortex-mem-core/src/types/mod.rs", + "line_number": 7, + "name": "pathlib", + "path": null, "version": null }, { - "dependency_type": "import", - "is_external": false, - "line_number": 2, - "name": "cortex_mem_core::types::Message", - "path": "cortex-mem-core/src/types/mod.rs", + "dependency_type": "external_library", + "is_external": true, + "line_number": 28, + "name": "toml", + "path": null, "version": null }, { - "dependency_type": "import", + "dependency_type": "external_library", "is_external": true, - "line_number": 3, - "name": "tracing::error", + "line_number": 69, + "name": "toml", "path": null, "version": null }, { - "dependency_type": "import", + "dependency_type": "external_library", "is_external": true, - "line_number": 3, - "name": "tracing::info", + "line_number": 90, + "name": "toml", "path": null, "version": null } ], - "detailed_description": "This component implements a CLI command for adding memory entries to the Cortex-Mem system. It supports two primary modes: simple content storage and conversation-based input processing. When handling content, it first determines whether the input should be treated as a conversation (based on memory type or content structure), then parses accordingly. For conversation input, it recognizes 'User:' and 'Assistant:' prefixes to structure message roles. The component orchestrates memory addition through the MemoryManager, handling both direct storage and complex memory operations, with appropriate success/failure feedback to the user via console output and structured logging.", + "detailed_description": "This utility module provides functions to validate the structure and content of a configuration file (config.toml) used by the Cortex Mem evaluation system. The `validate_config` function checks for the existence of the file and verifies that it contains required sections (llm, embedding, qdrant, memory) and mandatory fields within those sections (e.g., api_key, api_base_url). It uses string parsing to detect sections and the `toml` library to validate nested fields. The `get_config_value` function safely retrieves a specific value from a given section and key, returning a default if not found or on error. The `check_openai_config` function is a specialized validator ensuring that the OpenAI-related configuration in the 'llm' and 'embedding' sections is complete and non-empty. All functions include error handling and print diagnostic messages to aid debugging.", "interfaces": [ { - "description": "Creates a new AddCommand instance with the provided memory manager", - "interface_type": "constructor", - "name": "AddCommand::new", + "description": "Returns True if the config file exists and contains all required sections and fields; otherwise, prints an error message and returns False.", + "interface_type": "function", + "name": "validate_config", "parameters": [ { - "description": "Dependency-injected memory manager for backend operations", + "description": "Path to the config.toml file", "is_optional": false, - "name": "memory_manager", - "param_type": "MemoryManager" + "name": "config_path", + "param_type": "str" } ], - "return_type": "AddCommand", + "return_type": "bool", "visibility": "public" }, { - "description": "Executes the memory addition operation with the provided parameters", - "interface_type": "method", - "name": "AddCommand::execute", + "description": "Returns the value of the specified key in the given section, or the default value if not found or on error.", + "interface_type": "function", + "name": "get_config_value", "parameters": [ { - "description": "The content to be stored in memory", + "description": null, "is_optional": false, - "name": "content", - "param_type": "String" + "name": "config_path", + "param_type": "str" }, { - "description": "Optional user identifier for memory attribution", - "is_optional": true, - "name": "user_id", - "param_type": "Option" + "description": null, + "is_optional": false, + "name": "section", + "param_type": "str" }, { - "description": "Optional agent identifier for memory attribution", - "is_optional": true, - "name": "agent_id", - "param_type": "Option" + "description": null, + "is_optional": false, + "name": "key", + "param_type": "str" }, { - "description": "Type of memory to be created (converted to MemoryType)", - "is_optional": false, - "name": "memory_type", - "param_type": "String" + "description": "Default value to return if the key is not found", + "is_optional": true, + "name": "default", + "param_type": "Any" } ], - "return_type": "Result<(), Box>", + "return_type": "Any", "visibility": "public" }, { - "description": "Parses conversation-formatted content into structured messages with appropriate roles", + "description": "Returns True if the OpenAI configuration (api_key, api_base_url) in the 'llm' and 'embedding' sections is present and non-empty; otherwise, prints an error message and returns False.", "interface_type": "function", - "name": "parse_conversation_content", + "name": "check_openai_config", "parameters": [ { - "description": "Raw conversation content to parse", - "is_optional": false, - "name": "content", - "param_type": "&str" - }, - { - "description": "Reference to optional user identifier", + "description": null, "is_optional": false, - "name": "user_id", - "param_type": "&Option" - }, - { - "description": "Reference to optional agent identifier", - "is_optional": false, - "name": "agent_id", - "param_type": "&Option" + "name": "config_path", + "param_type": "str" } ], - "return_type": "Vec", - "visibility": "private" + "return_type": "bool", + "visibility": "public" } ], "responsibilities": [ - "Parse and validate CLI input for memory addition", - "Determine appropriate memory handling strategy (simple storage vs conversation processing)", - "Orchestrate memory addition through the MemoryManager component", - "Provide user feedback through console output and structured logging", - "Handle both structured conversation input and simple content storage" + "Validate the existence and structural integrity of the config.toml file", + "Ensure all required configuration sections (llm, embedding, qdrant, memory) are present", + "Verify that critical fields within each configuration section are defined", + "Provide safe access to configuration values with default fallbacks", + "Specifically validate the completeness of OpenAI API configuration" ] }, { "code_dossier": { - "code_purpose": "command", - "description": "Command-line interface command to list memories based on filters such as user, agent, memory type, topics, and keywords. Integrates with MemoryManager for data retrieval and formats output for CLI presentation.", - "file_path": "cortex-mem-cli/src/commands/list.rs", + "code_purpose": "util", + "description": "Configuration utilities for LangMem evaluation. Handles config.toml file reading and validation.", + "file_path": "examples/lomoco-evaluation/src/langmem_eval/config_utils.py", "functions": [ - "new", - "execute" + "validate_config", + "get_config_value", + "check_openai_config", + "get_langmem_model_string" ], - "importance_score": 0.8, + "importance_score": 0.9, "interfaces": [ - "ListCommand::new", - "ListCommand::execute" + "validate_config", + "get_config_value", + "check_openai_config", + "get_langmem_model_string" ], - "name": "list.rs", - "source_summary": "use cortex_mem_core::{\n memory::MemoryManager,\n types::{Filters, MemoryType},\n};\nuse serde_json::Value;\nuse tracing::{error, info};\n\npub struct ListCommand {\n memory_manager: MemoryManager,\n}\n\nimpl ListCommand {\n pub fn new(memory_manager: MemoryManager) -> Self {\n Self { memory_manager }\n }\n\n pub async fn execute(\n &self,\n user_id: Option,\n agent_id: Option,\n memory_type: Option,\n topics: Option>,\n keywords: Option>,\n limit: usize,\n ) -> Result<(), Box> {\n let mut filters = Filters::new();\n\n if let Some(user_id) = user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(memory_type_str) = memory_type {\n filters.memory_type = Some(MemoryType::parse(&memory_type_str));\n }\n\n if let Some(topics) = topics {\n filters.topics = Some(topics);\n }\n\n if let Some(keywords) = keywords {\n filters.custom.insert(\n \"keywords\".to_string(),\n Value::Array(keywords.into_iter().map(Value::String).collect()),\n );\n }\n\n match self.memory_manager.list(&filters, Some(limit)).await {\n Ok(memories) => {\n if memories.is_empty() {\n println!(\"📝 No memories found with the specified filters\");\n } else {\n println!(\"📝 Found {} memories:\", memories.len());\n println!();\n\n for (i, memory) in memories.iter().enumerate() {\n println!(\"{}. ID: {}\", i + 1, memory.id);\n println!(\" Content: {}\", memory.content);\n println!(\" Type: {:?}\", memory.metadata.memory_type);\n println!(\n \" Created: {}\",\n memory.created_at.format(\"%Y-%m-%d %H:%M:%S\")\n );\n println!(\n \" Updated: {}\",\n memory.updated_at.format(\"%Y-%m-%d %H:%M:%S\")\n );\n\n if let Some(user_id) = &memory.metadata.user_id {\n println!(\" User: {}\", user_id);\n }\n\n if let Some(agent_id) = &memory.metadata.agent_id {\n println!(\" Agent: {}\", agent_id);\n }\n\n if let Some(role) = &memory.metadata.role {\n println!(\" Role: {}\", role);\n }\n\n // Display topics\n if !memory.metadata.topics.is_empty() {\n println!(\" Topics: {}\", memory.metadata.topics.join(\", \"));\n }\n\n // Display keywords from custom metadata\n if let Some(keywords) = memory.metadata.custom.get(\"keywords\") {\n if let Some(keywords_array) = keywords.as_array() {\n let keyword_strings: Vec = keywords_array\n .iter()\n .filter_map(|k| k.as_str())\n .map(|s| s.to_string())\n .collect();\n if !keyword_strings.is_empty() {\n println!(\" Keywords: {}\", keyword_strings.join(\", \"));\n }\n }\n }\n\n println!();\n }\n }\n\n info!(\"List completed: {} memories found\", memories.len());\n }\n Err(e) => {\n error!(\"Failed to list memories: {}\", e);\n println!(\"❌ List failed: {}\", e);\n return Err(e.into());\n }\n }\n\n Ok(())\n }\n}\n" + "name": "config_utils.py", + "source_summary": "\"\"\"\nConfiguration utilities for LangMem evaluation.\nHandles config.toml file reading and validation.\n\"\"\"\n\nimport os\nfrom pathlib import Path\n\n\ndef validate_config(config_path: str) -> bool:\n \"\"\"Validate that config file exists and has required settings.\"\"\"\n if not os.path.exists(config_path):\n print(f\"Config file not found: {config_path}\")\n return False\n \n try:\n with open(config_path, 'r') as f:\n content = f.read()\n \n # Check for required sections\n required_sections = [\"llm\"]\n missing_sections = []\n \n for section in required_sections:\n if f\"[{section}]\" not in content:\n missing_sections.append(section)\n \n if missing_sections:\n print(f\"Missing required sections in config: {missing_sections}\")\n return False\n \n # Check for required fields in each section\n import toml\n \n config_data = toml.load(config_path)\n \n # Check llm section\n if \"llm\" in config_data:\n llm = config_data[\"llm\"]\n required_llm_fields = [\"api_key\", \"api_base_url\", \"model_efficient\"]\n missing_llm = [field for field in required_llm_fields if field not in llm]\n if missing_llm:\n print(f\"Missing fields in [llm] section: {missing_llm}\")\n return False\n \n return True\n \n except Exception as e:\n print(f\"Error validating config: {e}\")\n return False\n\n\ndef get_config_value(config_path: str, section: str, key: str, default=None):\n \"\"\"Get a specific value from config file.\"\"\"\n try:\n import toml\n config_data = toml.load(config_path)\n \n if section in config_data and key in config_data[section]:\n return config_data[section][key]\n return default\n except:\n return default\n\n\ndef check_openai_config(config_path: str) -> bool:\n \"\"\"Check if OpenAI configuration is properly set.\"\"\"\n try:\n import toml\n config_data = toml.load(config_path)\n \n # Check llm section\n if \"llm\" not in config_data:\n print(\"Missing [llm] section in config\")\n return False\n \n llm = config_data[\"llm\"]\n if \"api_key\" not in llm or not llm[\"api_key\"]:\n print(\"OpenAI API key not set in [llm] section\")\n return False\n \n if \"api_base_url\" not in llm or not llm[\"api_base_url\"]:\n print(\"OpenAI API base URL not set in [llm] section\")\n return False\n \n return True\n \n except Exception as e:\n print(f\"Error checking OpenAI config: {e}\")\n return False\n\n\ndef get_langmem_model_string(config_path: str) -> str:\n \"\"\"\n Get the model string for LangMem from config.toml.\n LangMem expects model strings in format like \"openai:gpt-4o-2024-11-20\"\n or \"custom:base_url:model_name\"\n \"\"\"\n try:\n import toml\n config_data = toml.load(config_path)\n \n if \"llm\" in config_data:\n api_base_url = config_data[\"llm\"].get(\"api_base_url\", \"\")\n model_name = config_data[\"llm\"].get(\"model_efficient\", \"\")\n \n # For custom OpenAI-compatible APIs, use the format that LangMem can understand\n # We'll construct a custom model identifier\n if api_base_url and model_name:\n # Store the base URL and model name separately, we'll use them directly\n # when creating the LangMem client\n return f\"openai:{model_name}\"\n \n return \"openai:gpt-4o\"\n except Exception as e:\n print(f\"Error getting LangMem model string: {e}\")\n return \"openai:gpt-4o\"" }, "complexity_metrics": { - "cyclomatic_complexity": 16.0, - "lines_of_code": 118, - "number_of_classes": 1, - "number_of_functions": 2 + "cyclomatic_complexity": 20.0, + "lines_of_code": 117, + "number_of_classes": 0, + "number_of_functions": 4 }, "dependencies": [ { - "dependency_type": "struct", + "dependency_type": "standard_library", "is_external": false, - "line_number": 1, - "name": "cortex_mem_core::memory::MemoryManager", - "path": "cortex-mem-core/src/memory/mod.rs", + "line_number": null, + "name": "os", + "path": null, "version": null }, { - "dependency_type": "struct", + "dependency_type": "standard_library", "is_external": false, - "line_number": 2, - "name": "cortex_mem_core::types::Filters", - "path": "cortex-mem-core/src/types/mod.rs", + "line_number": null, + "name": "pathlib", + "path": null, "version": null }, { - "dependency_type": "enum", - "is_external": false, - "line_number": 2, - "name": "cortex_mem_core::types::MemoryType", - "path": "cortex-mem-core/src/types/mod.rs", + "dependency_type": "third_party", + "is_external": true, + "line_number": null, + "name": "toml", + "path": null, "version": null }, { - "dependency_type": "struct", - "is_external": true, - "line_number": 3, - "name": "serde_json::Value", + "dependency_type": "parameter", + "is_external": false, + "line_number": null, + "name": "config_path", "path": null, "version": null }, { - "dependency_type": "function", - "is_external": true, - "line_number": 4, - "name": "tracing::error", + "dependency_type": "parameter", + "is_external": false, + "line_number": null, + "name": "section", "path": null, "version": null }, { - "dependency_type": "function", - "is_external": true, - "line_number": 4, - "name": "tracing::info", + "dependency_type": "parameter", + "is_external": false, + "line_number": null, + "name": "key", "path": null, "version": null } ], - "detailed_description": "The ListCommand struct is responsible for executing the 'list' functionality in a CLI tool that interfaces with a memory management system. It accepts various filter parameters (user_id, agent_id, memory_type, topics, keywords, limit) and constructs a Filters object used to query the MemoryManager. The execute method handles asynchronous retrieval of memory entries, processes the results, and prints them in a human-readable format to stdout. It includes detailed formatting of metadata such as creation time, topics, and keywords. Error handling is performed via structured logging (tracing) and user-facing error messages. This component serves as a bridge between user input and the core memory storage layer, translating high-level commands into backend queries and presenting results clearly.", + "detailed_description": "This module provides utility functions for handling and validating TOML-based configuration files used in the LangMem evaluation system. It ensures the presence of required sections (e.g., [llm]) and fields (api_key, api_base_url, model_efficient), retrieves specific configuration values with fallback defaults, validates OpenAI-related settings, and formats model identifiers compatible with the LangMem framework. The core logic revolves around file existence checks, string parsing for section detection, structured TOML parsing, and conditional logic for field validation and model string construction.", "interfaces": [ { - "description": "Constructs a new instance of ListCommand with a given MemoryManager", - "interface_type": "constructor", - "name": "ListCommand::new", + "description": "Validates that the config file exists and contains required sections and fields.", + "interface_type": "function", + "name": "validate_config", "parameters": [ { - "description": "Injected dependency for accessing memory data", + "description": null, "is_optional": false, - "name": "memory_manager", - "param_type": "MemoryManager" + "name": "config_path", + "param_type": "str" } ], - "return_type": "ListCommand", + "return_type": "bool", "visibility": "public" }, { - "description": "Executes the list operation with provided filters and prints formatted results", - "interface_type": "method", - "name": "ListCommand::execute", + "description": "Retrieves a specific value from the config file or returns a default if not found.", + "interface_type": "function", + "name": "get_config_value", "parameters": [ { - "description": "Optional filter by user ID", - "is_optional": true, - "name": "user_id", - "param_type": "Option" + "description": null, + "is_optional": false, + "name": "config_path", + "param_type": "str" }, { - "description": "Optional filter by agent ID", - "is_optional": true, - "name": "agent_id", - "param_type": "Option" + "description": null, + "is_optional": false, + "name": "section", + "param_type": "str" }, { - "description": "Optional filter by memory type", - "is_optional": true, - "name": "memory_type", - "param_type": "Option" + "description": null, + "is_optional": false, + "name": "key", + "param_type": "str" }, { - "description": "Optional filter by topic list", + "description": null, "is_optional": true, - "name": "topics", - "param_type": "Option>" - }, + "name": "default", + "param_type": "Any" + } + ], + "return_type": "Any", + "visibility": "public" + }, + { + "description": "Checks whether OpenAI configuration (API key and base URL) is properly set in the config.", + "interface_type": "function", + "name": "check_openai_config", + "parameters": [ { - "description": "Optional filter by keyword list", - "is_optional": true, - "name": "keywords", - "param_type": "Option>" - }, + "description": null, + "is_optional": false, + "name": "config_path", + "param_type": "str" + } + ], + "return_type": "bool", + "visibility": "public" + }, + { + "description": "Constructs and returns a model string in the format expected by LangMem (e.g., openai:gpt-4o-2024-11-20).", + "interface_type": "function", + "name": "get_langmem_model_string", + "parameters": [ { - "description": "Maximum number of results to return", + "description": null, "is_optional": false, - "name": "limit", - "param_type": "usize" + "name": "config_path", + "param_type": "str" } ], - "return_type": "Result<(), Box>", + "return_type": "str", "visibility": "public" } ], "responsibilities": [ - "Parse and apply filter criteria (user, agent, type, topics, keywords) for memory listing", - "Interact with MemoryManager to retrieve filtered list of memories", - "Format and display memory entries in a readable CLI output format", - "Handle success and error cases with appropriate logging and user feedback", - "Manage optional parameters and construct complex filter conditions dynamically" + "Validate the existence and structure of the config file, ensuring required sections like [llm] are present", + "Verify that essential configuration fields (api_key, api_base_url, model_efficient) exist and are non-empty in the [llm] section", + "Retrieve specific configuration values by section and key with support for default fallbacks", + "Construct model identifier strings in the format required by the LangMem framework", + "Provide targeted validation for OpenAI-compatible API settings including API key and base URL" ] }, { "code_dossier": { "code_purpose": "command", - "description": "CLI命令组件,用于执行记忆系统的优化操作,包括预览、执行、状态查询和配置管理。", - "file_path": "cortex-mem-cli/src/commands/optimize.rs", + "description": "Command-line interface command for adding memory entries to the system, supporting both simple content storage and structured conversation input.", + "file_path": "cortex-mem-cli/src/commands/add.rs", "functions": [ - "run_optimize", - "run_preview", - "run_optimization", - "run_status", - "run_config", - "build_optimization_request", - "create_optimizer", - "get_memory_details", - "format_severity", - "truncate_content", - "prompt_for_confirmation" + "new", + "execute", + "parse_conversation_content" ], "importance_score": 0.8, "interfaces": [ - "OptimizeCommand", - "OptimizationStatusCommand", - "OptimizationConfigCommand", - "OptimizeCommandRunner" + "AddCommand::new", + "AddCommand::execute", + "parse_conversation_content" ], - "name": "optimize.rs", - "source_summary": "use clap::Parser;\nuse cortex_mem_core::{\n config::Config,\n memory::{DefaultMemoryOptimizer, MemoryManager},\n};\nuse std::sync::Arc;\n\n/// 优化命令\n#[derive(Parser)]\npub struct OptimizeCommand {\n /// 优化策略\n #[arg(long, default_value = \"full\")]\n pub strategy: String,\n\n /// 用户ID过滤\n #[arg(long)]\n pub user_id: Option,\n\n /// Agent ID过滤\n #[arg(long)]\n pub agent_id: Option,\n\n /// 记忆类型过滤\n #[arg(long)]\n pub memory_type: Option,\n\n /// 预览模式(不执行)\n #[arg(long)]\n pub preview: bool,\n\n /// 激进模式(更深层优化)\n #[arg(long)]\n pub aggressive: bool,\n\n /// 跳过确认\n #[arg(long)]\n pub no_confirm: bool,\n\n /// 超时时间(分钟)\n #[arg(long, default_value = \"30\")]\n pub timeout: u64,\n\n /// 显示详细内容(预览时显示记忆摘要)\n #[arg(long)]\n pub verbose: bool,\n\n /// 限制显示的问题数量(默认10)\n #[arg(long, default_value = \"10\")]\n pub limit: usize,\n}\n\n/// 优化状态命令\n#[derive(Parser)]\npub struct OptimizationStatusCommand {\n /// 显示详细指标\n #[arg(long)]\n pub detailed: bool,\n\n /// 显示历史记录\n #[arg(long)]\n pub history: bool,\n}\n\n/// 优化配置命令\n#[derive(Parser)]\npub struct OptimizationConfigCommand {\n /// 显示当前配置\n #[arg(long)]\n pub show: bool,\n\n /// 更新配置\n #[arg(long)]\n pub update: bool,\n\n /// 配置文件路径\n #[arg(conflicts_with = \"show\")]\n pub config_file: Option,\n}\n\n/// 优化命令执行器\npub struct OptimizeCommandRunner {\n memory_manager: Arc,\n config: Config,\n}\n\nimpl OptimizeCommandRunner {\n pub fn new(memory_manager: Arc, config: Config) -> Self {\n Self {\n memory_manager,\n config,\n }\n }\n\n pub async fn run_optimize(\n &self,\n cmd: &OptimizeCommand,\n ) -> Result<(), Box> {\n // 1. 构建优化请求\n let request = self.build_optimization_request(cmd)?;\n\n // 2. 创建优化器\n let optimizer = self.create_optimizer().await?;\n\n // 3. 执行优化\n if cmd.preview {\n self.run_preview(optimizer.as_ref(), &request).await?;\n } else {\n self.run_optimization(optimizer.as_ref(), &request, cmd.no_confirm)\n .await?;\n }\n\n Ok(())\n }\n\n async fn create_optimizer(\n &self,\n ) -> Result, Box> {\n // 使用默认的优化配置\n let optimization_config = cortex_mem_core::types::OptimizationConfig::default();\n\n let optimizer =\n DefaultMemoryOptimizer::new(self.memory_manager.clone(), optimization_config);\n\n Ok(Arc::new(optimizer))\n }\n\n async fn run_preview(\n &self,\n optimizer: &dyn cortex_mem_core::memory::MemoryOptimizer,\n request: &cortex_mem_core::types::OptimizationRequest,\n ) -> Result<(), Box> {\n println!(\"🔍 优化计划预览\");\n println!(\"策略: {:?}\", request.strategy);\n println!(\"过滤器: {:?}\", request.filters);\n println!();\n\n // 创建优化计划,添加错误处理\n let plan = match optimizer\n .create_optimization_plan(request.strategy.clone())\n .await\n {\n Ok(plan) => plan,\n Err(e) => {\n // 检查是否是API限制错误\n let error_str = e.to_string().to_lowercase();\n if error_str.contains(\"too many requests\") || error_str.contains(\"429\") {\n println!(\"⚠️ API请求频率限制,无法生成优化计划\");\n println!(\"💡 请稍后再试,或使用 --limit 参数减少查询数量\");\n return Ok(());\n } else {\n return Err(Box::new(e));\n }\n }\n };\n\n // 检查是否是详细模式\n let verbose = request\n .filters\n .custom_filters\n .get(\"verbose\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 显示问题统计\n println!(\"📊 问题统计:\");\n let issue_stats = plan.issue_statistics();\n println!(\" - 总问题数: {}\", issue_stats.total());\n println!(\n \" - 严重: {} 个, 高: {} 个, 中: {} 个, 低: {} 个\",\n issue_stats.critical_count,\n issue_stats.high_count,\n issue_stats.medium_count,\n issue_stats.low_count\n );\n\n if verbose {\n println!(\n \" - 重复: {} 个, 质量: {} 个, 相关性: {} 个, 分类: {} 个, 空间: {} 个\",\n issue_stats.duplicate_issues,\n issue_stats.quality_issues,\n issue_stats.relevance_issues,\n issue_stats.classification_issues,\n issue_stats.space_issues\n );\n }\n\n println!();\n println!(\"📋 检测到的问题:\");\n\n // 获取受影响的记忆详细信息(仅在详细模式下)\n // 添加错误处理,当遇到API限制时回退到非详细模式\n let memory_details = if verbose {\n match self.get_memory_details(&plan.issues).await {\n Ok(details) => Some(details),\n Err(e) => {\n // 检查是否是API限制错误\n let error_str = e.to_string().to_lowercase();\n if error_str.contains(\"too many requests\") || error_str.contains(\"429\") {\n println!(\"⚠️ API请求频率限制,回退到非详细模式\");\n None\n } else {\n return Err(e);\n }\n }\n }\n } else {\n None\n };\n\n // 如果原本请求详细信息但失败了,更新verbose标志\n let effective_verbose = verbose && memory_details.is_some();\n\n // 限制显示的问题数量\n let display_issues: Vec<_> = plan\n .issues\n .iter()\n .take(\n request\n .filters\n .custom_filters\n .get(\"limit\")\n .and_then(|v| v.as_u64())\n .unwrap_or(10) as usize,\n )\n .collect();\n\n for (i, issue) in display_issues.iter().enumerate() {\n println!(\n \" {}. [{}] {}\",\n i + 1,\n self.format_severity(issue.severity.clone()),\n issue.description\n );\n\n // 在详细模式下显示受影响的记忆信息\n if effective_verbose {\n if let Some(ref details) = memory_details {\n for memory_id in &issue.affected_memories {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📝 记忆ID: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n println!(\n \" 📖 内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 50)\n );\n println!(\n \" 🏷️ 类型: {:?}, 重要性: {:.2}, 创建: {}\",\n memory.metadata.memory_type,\n memory.metadata.importance_score,\n memory.created_at.format(\"%Y-%m-%d\")\n );\n if memory.metadata.user_id.is_some()\n || memory.metadata.agent_id.is_some()\n {\n println!(\n \" 👤 用户: {:?}, 代理: {:?}\",\n memory.metadata.user_id, memory.metadata.agent_id\n );\n }\n } else {\n println!(\n \" 📝 记忆ID: {}... (无法获取详细信息)\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n }\n }\n } else {\n // 详细模式回退到非详细模式\n println!(\n \" 📝 影响记忆: {} 个 (详细查看受API限制)\",\n issue.affected_memories.len()\n );\n }\n } else {\n // 非详细模式,只显示记忆ID数量\n println!(\" 📝 影响记忆: {} 个\", issue.affected_memories.len());\n }\n\n println!(\" 💡 建议: {}\", issue.recommendation);\n println!();\n }\n\n if plan.issues.len() > display_issues.len() {\n println!(\n \" ... 还有 {} 个问题未显示,使用 --limit 查看更多\",\n plan.issues.len() - display_issues.len()\n );\n }\n\n println!(\"🎯 建议的操作:\");\n\n // 获取操作统计\n let action_stats = plan.action_statistics();\n println!(\"📈 操作统计:\");\n println!(\" - 总操作数: {}\", action_stats.total());\n println!(\n \" - 合并: {} 个, 删除: {} 个, 更新: {} 个, 重分类: {} 个, 归档: {} 个\",\n action_stats.merge_count,\n action_stats.delete_count,\n action_stats.update_count,\n action_stats.reclassify_count,\n action_stats.archive_count\n );\n\n println!();\n let display_actions: Vec<_> = plan\n .actions\n .iter()\n .take(display_issues.len()) // 显示与问题相同数量的操作\n .collect();\n\n for (i, action) in display_actions.iter().enumerate() {\n println!(\" {}. {:?}\", i + 1, action);\n\n // 在详细模式下为每个操作添加解释\n if verbose {\n if let Some(ref details) = memory_details {\n match action {\n cortex_mem_core::types::OptimizationAction::Delete { memory_id } => {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📖 将删除内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 30)\n );\n }\n }\n cortex_mem_core::types::OptimizationAction::Merge { memories } => {\n println!(\" 🔗 将合并 {} 个记忆\", memories.len());\n if memories.len() > 0 && details.contains_key(&memories[0]) {\n println!(\n \" 📖 示例内容: \\\"{}\\\"\",\n self.truncate_content(&details[&memories[0]].content, 30)\n );\n }\n }\n cortex_mem_core::types::OptimizationAction::Update {\n memory_id,\n updates,\n } => {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📖 更新内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 30)\n );\n if let Some(new_type) = &updates.memory_type {\n println!(\n \" 🏷️ 类型将从 {:?} 更改为 {:?}\",\n memory.metadata.memory_type, new_type\n );\n }\n }\n }\n cortex_mem_core::types::OptimizationAction::Reclassify { memory_id } => {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📖 重新分类内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 30)\n );\n println!(\" 🏷️ 当前类型: {:?}\", memory.metadata.memory_type);\n }\n }\n cortex_mem_core::types::OptimizationAction::Archive { memory_id } => {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📖 归档内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 30)\n );\n println!(\n \" ⏰ 创建时间: {}\",\n memory.created_at.format(\"%Y-%m-%d %H:%M\")\n );\n }\n }\n }\n }\n } else {\n // 非详细模式,显示简单操作描述\n match action {\n cortex_mem_core::types::OptimizationAction::Delete { memory_id } => {\n println!(\n \" 🗑️ 删除记忆: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n }\n cortex_mem_core::types::OptimizationAction::Merge { memories } => {\n println!(\" 🔗 合并 {} 个记忆\", memories.len());\n }\n cortex_mem_core::types::OptimizationAction::Update { memory_id, updates } => {\n println!(\n \" ✏️ 更新记忆: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n if let Some(new_type) = &updates.memory_type {\n println!(\" 🏷️ 更新类型为 {:?}\", new_type);\n }\n }\n cortex_mem_core::types::OptimizationAction::Reclassify { memory_id } => {\n println!(\n \" 🔄 重新分类记忆: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n }\n cortex_mem_core::types::OptimizationAction::Archive { memory_id } => {\n println!(\n \" 📦 归档记忆: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n }\n }\n }\n println!();\n }\n\n // 显示未处理的操作数量\n if plan.actions.len() > display_actions.len() {\n println!(\n \" ... 还有 {} 个操作未显示\",\n plan.actions.len() - display_actions.len()\n );\n }\n\n println!(\n \"✨ 预计优化后可节省空间 {:.2} MB,提升质量 {:.1}%\",\n 0.1 * plan.issues.len() as f64, // 简单估算\n 5.0 * issue_stats.total() as f64\n ); // 简单估算\n\n Ok(())\n }\n\n async fn run_optimization(\n &self,\n optimizer: &dyn cortex_mem_core::memory::MemoryOptimizer,\n request: &cortex_mem_core::types::OptimizationRequest,\n no_confirm: bool,\n ) -> Result<(), Box> {\n if !no_confirm {\n println!(\"⚠️ 此操作将修改您的memory数据库\");\n let input = prompt_for_confirmation(\"是否继续? (y/N): \");\n if !input {\n println!(\"❌ 操作已取消\");\n return Ok(());\n }\n }\n\n println!(\"🚀 开始执行优化...\");\n\n let result = optimizer.optimize(request).await?;\n\n if result.success {\n println!(\"✅ 优化完成!\");\n println!(\"📊 优化统计:\");\n println!(\" - 执行时间: {:?}\", result.end_time - result.start_time);\n println!(\" - 发现问题: {} 个\", result.issues_found.len());\n println!(\" - 执行操作: {} 个\", result.actions_performed.len());\n\n if let Some(metrics) = result.metrics {\n println!(\" - 节省空间: {:.2} MB\", metrics.saved_space_mb);\n println!(\" - 改善质量: {:.2}%\", metrics.quality_improvement * 100.0);\n }\n } else {\n println!(\n \"❌ 优化失败: {}\",\n result\n .error_message\n .unwrap_or_else(|| \"未知错误\".to_string())\n );\n }\n\n Ok(())\n }\n\n pub async fn run_status(\n &self,\n cmd: &OptimizationStatusCommand,\n ) -> Result<(), Box> {\n println!(\"📈 优化状态\");\n\n if cmd.detailed {\n println!(\"详细指标功能开发中...\");\n }\n\n if cmd.history {\n println!(\"历史记录功能开发中...\");\n }\n\n Ok(())\n }\n\n pub async fn run_config(\n &self,\n cmd: &OptimizationConfigCommand,\n ) -> Result<(), Box> {\n if cmd.show {\n println!(\"优化配置:\");\n println!(\"当前配置功能开发中...\");\n } else if cmd.update {\n println!(\"更新配置功能开发中...\");\n }\n\n Ok(())\n }\n\n fn build_optimization_request(\n &self,\n cmd: &OptimizeCommand,\n ) -> Result> {\n let memory_type = cmd\n .memory_type\n .as_ref()\n .map(|s| cortex_mem_core::types::MemoryType::parse(s));\n\n let strategy = match cmd.strategy.to_lowercase().as_str() {\n \"full\" => cortex_mem_core::types::OptimizationStrategy::Full,\n \"incremental\" => cortex_mem_core::types::OptimizationStrategy::Incremental,\n \"batch\" => cortex_mem_core::types::OptimizationStrategy::Batch,\n \"deduplication\" => cortex_mem_core::types::OptimizationStrategy::Deduplication,\n \"relevance\" => cortex_mem_core::types::OptimizationStrategy::Relevance,\n \"quality\" => cortex_mem_core::types::OptimizationStrategy::Quality,\n \"space\" => cortex_mem_core::types::OptimizationStrategy::Space,\n _ => cortex_mem_core::types::OptimizationStrategy::Full,\n };\n\n let mut custom_filters = std::collections::HashMap::new();\n custom_filters.insert(\n \"limit\".to_string(),\n serde_json::Value::Number(serde_json::Number::from(cmd.limit)),\n );\n custom_filters.insert(\"verbose\".to_string(), serde_json::Value::Bool(cmd.verbose));\n\n let filters = cortex_mem_core::types::OptimizationFilters {\n user_id: cmd.user_id.clone(),\n agent_id: cmd.agent_id.clone(),\n memory_type,\n date_range: None,\n importance_range: None,\n custom_filters,\n };\n\n Ok(cortex_mem_core::types::OptimizationRequest {\n optimization_id: None,\n strategy,\n filters,\n aggressive: cmd.aggressive,\n dry_run: cmd.preview,\n timeout_minutes: Some(cmd.timeout),\n })\n }\n}\n\nfn prompt_for_confirmation(prompt: &str) -> bool {\n use std::io::{self, Write};\n\n print!(\"{}\", prompt);\n io::stdout().flush().unwrap();\n\n let mut input = String::new();\n io::stdin().read_line(&mut input).unwrap_or_default();\n\n input.trim().to_lowercase() == \"y\" || input.trim().to_lowercase() == \"yes\"\n}\n\nimpl OptimizeCommandRunner {\n /// 获取记忆详细信息\n async fn get_memory_details(\n &self,\n issues: &[cortex_mem_core::types::OptimizationIssue],\n ) -> Result<\n std::collections::HashMap,\n Box,\n > {\n let mut memory_details = std::collections::HashMap::new();\n\n // 收集所有需要获取的记忆ID\n let mut all_memory_ids = std::collections::HashSet::new();\n for issue in issues {\n for memory_id in &issue.affected_memories {\n all_memory_ids.insert(memory_id.clone());\n }\n }\n\n // 批量获取记忆详情\n for memory_id in all_memory_ids {\n match self.memory_manager.get(&memory_id).await {\n Ok(Some(memory)) => {\n // 记录记忆内容状态\n if memory.content.trim().is_empty() {\n tracing::warn!(\"记忆 {} 内容为空\", memory_id);\n } else {\n tracing::debug!(\"记忆 {} 内容长度: {}\", memory_id, memory.content.len());\n }\n memory_details.insert(memory_id, memory);\n }\n Ok(None) => {\n tracing::warn!(\"记忆 {} 不存在\", memory_id);\n }\n Err(e) => {\n tracing::warn!(\"无法获取记忆 {} 的详细信息: {}\", memory_id, e);\n }\n }\n }\n\n Ok(memory_details)\n }\n\n /// 格式化严重程度\n fn format_severity(&self, severity: cortex_mem_core::types::IssueSeverity) -> String {\n match severity {\n cortex_mem_core::types::IssueSeverity::Critical => \"🔴 严重\".to_string(),\n cortex_mem_core::types::IssueSeverity::High => \"🟠 高\".to_string(),\n cortex_mem_core::types::IssueSeverity::Medium => \"🟡 中\".to_string(),\n cortex_mem_core::types::IssueSeverity::Low => \"🟢 低\".to_string(),\n }\n }\n\n /// 截断内容(安全处理Unicode字符)\n fn truncate_content(&self, content: &str, max_length: usize) -> String {\n if content.len() <= max_length {\n content.to_string()\n } else {\n // 安全地找到字符边界\n let end = match content.char_indices().nth(max_length) {\n Some((idx, _)) => idx,\n None => content.len(),\n };\n format!(\"{}...\", &content[..end])\n }\n }\n}\n" + "name": "add.rs", + "source_summary": "use cortex_mem_core::{\n memory::MemoryManager,\n types::{MemoryMetadata, MemoryType, Message},\n};\nuse tracing::{error, info};\n\npub struct AddCommand {\n memory_manager: MemoryManager,\n}\n\nimpl AddCommand {\n pub fn new(memory_manager: MemoryManager) -> Self {\n Self { memory_manager }\n }\n\n pub async fn execute(\n &self,\n content: String,\n user_id: Option,\n agent_id: Option,\n memory_type: String,\n ) -> Result<(), Box> {\n let memory_type = MemoryType::parse(&memory_type);\n\n let mut metadata = MemoryMetadata::new(memory_type.to_owned());\n\n if let Some(ref user_id) = user_id {\n metadata = metadata.with_user_id(user_id.to_owned());\n }\n\n if let Some(ref agent_id) = agent_id {\n metadata = metadata.with_agent_id(agent_id.to_owned());\n }\n\n // Check if this should be handled as a conversation (for procedural memory or advanced fact extraction)\n let is_conversation = memory_type == MemoryType::Procedural\n || content.contains('\\n')\n || content.contains(\"Assistant:\")\n || content.contains(\"User:\");\n\n if is_conversation {\n // Handle as conversation for advanced processing\n let messages = if content.contains('\\n')\n || content.contains(\"User:\")\n || content.contains(\"Assistant:\")\n {\n // Parse conversation format\n parse_conversation_content(&content, &user_id, &agent_id)\n } else {\n // Single user message\n vec![Message {\n role: \"user\".to_string(),\n content: content.clone(),\n name: user_id.clone(),\n }]\n };\n\n match self.memory_manager.add_memory(&messages, metadata).await {\n Ok(results) => {\n info!(\"Memory added successfully with {} actions\", results.len());\n println!(\"✅ Memory added successfully!\");\n println!(\"Memory Type: {:?}\", memory_type);\n println!(\"Actions Performed: {}\", results.len());\n\n for (i, result) in results.iter().enumerate() {\n println!(\n \" {}. {:?} - {}\",\n i + 1,\n result.event,\n result.memory.chars().take(100).collect::()\n );\n if result.memory.len() > 100 {\n println!(\" (truncated)\");\n }\n }\n }\n Err(e) => {\n error!(\"Failed to add memory: {}\", e);\n println!(\"❌ Failed to add memory: {}\", e);\n return Err(e.into());\n }\n }\n } else {\n // Handle as simple content storage\n match self.memory_manager.store(content.clone(), metadata).await {\n Ok(memory_id) => {\n info!(\"Memory stored successfully with ID: {}\", memory_id);\n println!(\"✅ Memory added successfully!\");\n println!(\"ID: {}\", memory_id);\n println!(\"Content: {}\", content.chars().take(100).collect::());\n if content.len() > 100 {\n println!(\"(truncated)\");\n }\n }\n Err(e) => {\n error!(\"Failed to store memory: {}\", e);\n println!(\"❌ Failed to add memory: {}\", e);\n return Err(e.into());\n }\n }\n }\n\n Ok(())\n }\n}\n\n/// Parse conversation content from CLI input\nfn parse_conversation_content(\n content: &str,\n user_id: &Option,\n agent_id: &Option,\n) -> Vec {\n let mut messages = Vec::new();\n let lines: Vec<&str> = content.lines().collect();\n\n for line in lines {\n let trimmed = line.trim();\n if trimmed.is_empty() {\n continue;\n }\n\n if trimmed.starts_with(\"User:\") || trimmed.starts_with(\"user:\") {\n let user_content = trimmed[5..].trim();\n messages.push(Message {\n role: \"user\".to_string(),\n content: user_content.to_string(),\n name: user_id.clone(),\n });\n } else if trimmed.starts_with(\"Assistant:\")\n || trimmed.starts_with(\"assistant:\")\n || trimmed.starts_with(\"AI:\")\n {\n let assistant_content = trimmed[10..].trim();\n messages.push(Message {\n role: \"assistant\".to_string(),\n content: assistant_content.to_string(),\n name: agent_id.clone(),\n });\n } else {\n // If no role prefix, treat as user message\n messages.push(Message {\n role: \"user\".to_string(),\n content: trimmed.to_string(),\n name: user_id.clone(),\n });\n }\n }\n\n // If no messages were parsed, treat entire content as user message\n if messages.is_empty() {\n messages.push(Message {\n role: \"user\".to_string(),\n content: content.to_string(),\n name: user_id.clone(),\n });\n }\n\n messages\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 45.0, - "lines_of_code": 631, - "number_of_classes": 4, - "number_of_functions": 11 + "cyclomatic_complexity": 18.0, + "lines_of_code": 159, + "number_of_classes": 1, + "number_of_functions": 3 }, "dependencies": [ { - "dependency_type": "crate", - "is_external": true, + "dependency_type": "import", + "is_external": false, "line_number": 1, - "name": "clap", - "path": null, + "name": "cortex_mem_core::memory::MemoryManager", + "path": "cortex-mem-core/src/memory/mod.rs", "version": null }, { - "dependency_type": "crate", + "dependency_type": "import", + "is_external": false, + "line_number": 2, + "name": "cortex_mem_core::types::MemoryMetadata", + "path": "cortex-mem-core/src/types/mod.rs", + "version": null + }, + { + "dependency_type": "import", + "is_external": false, + "line_number": 2, + "name": "cortex_mem_core::types::MemoryType", + "path": "cortex-mem-core/src/types/mod.rs", + "version": null + }, + { + "dependency_type": "import", + "is_external": false, + "line_number": 2, + "name": "cortex_mem_core::types::Message", + "path": "cortex-mem-core/src/types/mod.rs", + "version": null + }, + { + "dependency_type": "import", "is_external": true, "line_number": 3, - "name": "cortex_mem_core", + "name": "tracing::error", "path": null, "version": null }, { - "dependency_type": "std", - "is_external": false, - "line_number": 6, - "name": "std", + "dependency_type": "import", + "is_external": true, + "line_number": 3, + "name": "tracing::info", "path": null, "version": null } ], - "detailed_description": "该组件是Cortex Memory CLI系统中的核心优化命令执行器,实现了基于CLAP的命令行接口。主要功能包括:1) 提供多种过滤选项(用户ID、Agent ID、记忆类型)和策略选择(全量、增量、去重等)的优化命令;2) 支持预览模式,安全地展示即将执行的优化计划和建议;3) 实现详细的交互式确认机制,防止误操作;4) 提供API频率限制的优雅降级处理,在请求受限时自动回退到基础模式;5) 具备丰富的可视化输出,使用emoji和格式化文本提升用户体验。组件通过MemoryManager与底层记忆系统交互,利用DefaultMemoryOptimizer执行具体的优化逻辑。代码结构清晰,遵循单一职责原则,将命令解析、请求构建、执行逻辑和UI展示分离。错误处理完善,对API限制等常见问题提供用户友好的反馈。整体设计体现了CLI工具的最佳实践,兼顾功能性、安全性和用户体验。", + "detailed_description": "This component implements a CLI command for adding memory entries to the Cortex-Mem system. It supports two primary modes: simple content storage and conversation-based input processing. When handling content, it first determines whether the input should be treated as a conversation (based on memory type or content structure), then parses accordingly. For conversation input, it recognizes 'User:' and 'Assistant:' prefixes to structure message roles. The component orchestrates memory addition through the MemoryManager, handling both direct storage and complex memory operations, with appropriate success/failure feedback to the user via console output and structured logging.", "interfaces": [ { - "description": "优化命令的CLI参数结构,使用CLAP派生实现命令行参数解析", - "interface_type": "struct", - "name": "OptimizeCommand", + "description": "Creates a new AddCommand instance with the provided memory manager", + "interface_type": "constructor", + "name": "AddCommand::new", "parameters": [ { - "description": "优化策略,支持full、incremental等多种模式", + "description": "Dependency-injected memory manager for backend operations", "is_optional": false, - "name": "strategy", - "param_type": "String" - }, - { - "description": "用户ID过滤条件", - "is_optional": true, - "name": "user_id", - "param_type": "Option" - }, - { - "description": "Agent ID过滤条件", - "is_optional": true, - "name": "agent_id", - "param_type": "Option" - }, - { - "description": "记忆类型过滤条件", - "is_optional": true, - "name": "memory_type", - "param_type": "Option" - }, - { - "description": "预览模式开关,true时仅展示计划不执行", - "is_optional": false, - "name": "preview", - "param_type": "bool" - }, - { - "description": "激进模式开关,启用更深层优化", - "is_optional": false, - "name": "aggressive", - "param_type": "bool" - }, - { - "description": "跳过确认提示开关", - "is_optional": false, - "name": "no_confirm", - "param_type": "bool" - }, - { - "description": "操作超时时间(分钟)", - "is_optional": false, - "name": "timeout", - "param_type": "u64" - }, - { - "description": "详细模式开关,展示记忆摘要", - "is_optional": false, - "name": "verbose", - "param_type": "bool" - }, - { - "description": "限制显示的问题数量", - "is_optional": false, - "name": "limit", - "param_type": "usize" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "优化状态查询命令的CLI参数结构", - "interface_type": "struct", - "name": "OptimizationStatusCommand", - "parameters": [ - { - "description": "显示详细指标", - "is_optional": false, - "name": "detailed", - "param_type": "bool" - }, - { - "description": "显示历史记录", - "is_optional": false, - "name": "history", - "param_type": "bool" + "name": "memory_manager", + "param_type": "MemoryManager" } ], - "return_type": null, + "return_type": "AddCommand", "visibility": "public" }, { - "description": "优化配置管理命令的CLI参数结构", - "interface_type": "struct", - "name": "OptimizationConfigCommand", + "description": "Executes the memory addition operation with the provided parameters", + "interface_type": "method", + "name": "AddCommand::execute", "parameters": [ { - "description": "显示当前配置", + "description": "The content to be stored in memory", "is_optional": false, - "name": "show", - "param_type": "bool" + "name": "content", + "param_type": "String" }, { - "description": "更新配置", - "is_optional": false, - "name": "update", - "param_type": "bool" + "description": "Optional user identifier for memory attribution", + "is_optional": true, + "name": "user_id", + "param_type": "Option" }, { - "description": "配置文件路径", + "description": "Optional agent identifier for memory attribution", "is_optional": true, - "name": "config_file", + "name": "agent_id", "param_type": "Option" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "优化命令执行器,协调优化流程的核心组件", - "interface_type": "struct", - "name": "OptimizeCommandRunner", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "执行优化命令的主入口方法", - "interface_type": "method", - "name": "run_optimize", - "parameters": [ - { - "description": "优化命令参数", - "is_optional": false, - "name": "cmd", - "param_type": "&OptimizeCommand" - } - ], - "return_type": "Result<(), Box>", - "visibility": "public" - }, - { - "description": "执行优化预览,展示优化计划", - "interface_type": "method", - "name": "run_preview", - "parameters": [ - { - "description": "优化器实例", - "is_optional": false, - "name": "optimizer", - "param_type": "&dyn cortex_mem_core::memory::MemoryOptimizer" }, { - "description": "优化请求", + "description": "Type of memory to be created (converted to MemoryType)", "is_optional": false, - "name": "request", - "param_type": "&cortex_mem_core::types::OptimizationRequest" + "name": "memory_type", + "param_type": "String" } ], "return_type": "Result<(), Box>", - "visibility": "private" + "visibility": "public" }, { - "description": "执行实际的优化操作", - "interface_type": "method", - "name": "run_optimization", + "description": "Parses conversation-formatted content into structured messages with appropriate roles", + "interface_type": "function", + "name": "parse_conversation_content", "parameters": [ { - "description": "优化器实例", + "description": "Raw conversation content to parse", "is_optional": false, - "name": "optimizer", - "param_type": "&dyn cortex_mem_core::memory::MemoryOptimizer" + "name": "content", + "param_type": "&str" }, { - "description": "优化请求", + "description": "Reference to optional user identifier", "is_optional": false, - "name": "request", - "param_type": "&cortex_mem_core::types::OptimizationRequest" + "name": "user_id", + "param_type": "&Option" }, { - "description": "是否跳过确认", - "is_optional": false, - "name": "no_confirm", - "param_type": "bool" - } - ], - "return_type": "Result<(), Box>", - "visibility": "private" - }, - { - "description": "执行状态查询命令", - "interface_type": "method", - "name": "run_status", - "parameters": [ - { - "description": "状态命令参数", - "is_optional": false, - "name": "cmd", - "param_type": "&OptimizationStatusCommand" - } - ], - "return_type": "Result<(), Box>", - "visibility": "public" - }, - { - "description": "执行配置管理命令", - "interface_type": "method", - "name": "run_config", - "parameters": [ - { - "description": "配置命令参数", - "is_optional": false, - "name": "cmd", - "param_type": "&OptimizationConfigCommand" - } - ], - "return_type": "Result<(), Box>", - "visibility": "public" - }, - { - "description": "根据命令参数构建优化请求对象", - "interface_type": "method", - "name": "build_optimization_request", - "parameters": [ - { - "description": "优化命令参数", + "description": "Reference to optional agent identifier", "is_optional": false, - "name": "cmd", - "param_type": "&OptimizeCommand" + "name": "agent_id", + "param_type": "&Option" } ], - "return_type": "Result>", + "return_type": "Vec", "visibility": "private" } ], "responsibilities": [ - "解析和执行记忆优化命令行指令", - "构建优化请求并协调优化器执行优化流程", - "提供优化计划预览和结果展示功能", - "实现用户交互和操作确认机制", - "处理API频率限制等外部依赖错误" + "Parse and validate CLI input for memory addition", + "Determine appropriate memory handling strategy (simple storage vs conversation processing)", + "Orchestrate memory addition through the MemoryManager component", + "Provide user feedback through console output and structured logging", + "Handle both structured conversation input and simple content storage" ] }, { "code_dossier": { "code_purpose": "command", - "description": "Command-line interface component for deleting memory entries with user confirmation and logging.", - "file_path": "cortex-mem-cli/src/commands/delete.rs", + "description": "Command-line interface command to list memories based on filters such as user, agent, memory type, topics, and keywords. Integrates with MemoryManager for data retrieval and formats output for CLI presentation.", + "file_path": "cortex-mem-cli/src/commands/list.rs", "functions": [ "new", "execute" ], "importance_score": 0.8, "interfaces": [ - "DeleteCommand::new", - "DeleteCommand::execute" + "ListCommand::new", + "ListCommand::execute" ], - "name": "delete.rs", - "source_summary": "use cortex_mem_core::memory::MemoryManager;\nuse tracing::{error, info};\n\npub struct DeleteCommand {\n memory_manager: MemoryManager,\n}\n\nimpl DeleteCommand {\n pub fn new(memory_manager: MemoryManager) -> Self {\n Self { memory_manager }\n }\n\n pub async fn execute(&self, id: String) -> Result<(), Box> {\n // First, try to get the memory to confirm it exists\n match self.memory_manager.get(&id).await {\n Ok(Some(memory)) => {\n println!(\"Found memory to delete:\");\n println!(\"ID: {}\", memory.id);\n println!(\"Content: {}\", memory.content);\n println!(\"Type: {:?}\", memory.metadata.memory_type);\n println!();\n\n // Confirm deletion\n print!(\"Are you sure you want to delete this memory? (y/N): \");\n use std::io::{self, Write};\n io::stdout().flush().unwrap();\n \n let mut input = String::new();\n io::stdin().read_line(&mut input).unwrap();\n \n if input.trim().to_lowercase() == \"y\" || input.trim().to_lowercase() == \"yes\" {\n match self.memory_manager.delete(&id).await {\n Ok(()) => {\n println!(\"✅ Memory deleted successfully!\");\n info!(\"Memory deleted: {}\", id);\n }\n Err(e) => {\n error!(\"Failed to delete memory: {}\", e);\n println!(\"❌ Failed to delete memory: {}\", e);\n return Err(e.into());\n }\n }\n } else {\n println!(\"❌ Deletion cancelled\");\n }\n }\n Ok(None) => {\n println!(\"❌ Memory with ID '{}' not found\", id);\n }\n Err(e) => {\n error!(\"Failed to retrieve memory: {}\", e);\n println!(\"❌ Failed to retrieve memory: {}\", e);\n return Err(e.into());\n }\n }\n\n Ok(())\n }\n}" + "name": "list.rs", + "source_summary": "use cortex_mem_core::{\n memory::MemoryManager,\n types::{Filters, MemoryType},\n};\nuse serde_json::Value;\nuse tracing::{error, info};\n\npub struct ListCommand {\n memory_manager: MemoryManager,\n}\n\nimpl ListCommand {\n pub fn new(memory_manager: MemoryManager) -> Self {\n Self { memory_manager }\n }\n\n pub async fn execute(\n &self,\n user_id: Option,\n agent_id: Option,\n memory_type: Option,\n topics: Option>,\n keywords: Option>,\n limit: usize,\n ) -> Result<(), Box> {\n let mut filters = Filters::new();\n\n if let Some(user_id) = user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(memory_type_str) = memory_type {\n filters.memory_type = Some(MemoryType::parse(&memory_type_str));\n }\n\n if let Some(topics) = topics {\n filters.topics = Some(topics);\n }\n\n if let Some(keywords) = keywords {\n filters.custom.insert(\n \"keywords\".to_string(),\n Value::Array(keywords.into_iter().map(Value::String).collect()),\n );\n }\n\n match self.memory_manager.list(&filters, Some(limit)).await {\n Ok(memories) => {\n if memories.is_empty() {\n println!(\"📝 No memories found with the specified filters\");\n } else {\n println!(\"📝 Found {} memories:\", memories.len());\n println!();\n\n for (i, memory) in memories.iter().enumerate() {\n println!(\"{}. ID: {}\", i + 1, memory.id);\n println!(\" Content: {}\", memory.content);\n println!(\" Type: {:?}\", memory.metadata.memory_type);\n println!(\n \" Created: {}\",\n memory.created_at.format(\"%Y-%m-%d %H:%M:%S\")\n );\n println!(\n \" Updated: {}\",\n memory.updated_at.format(\"%Y-%m-%d %H:%M:%S\")\n );\n\n if let Some(user_id) = &memory.metadata.user_id {\n println!(\" User: {}\", user_id);\n }\n\n if let Some(agent_id) = &memory.metadata.agent_id {\n println!(\" Agent: {}\", agent_id);\n }\n\n if let Some(role) = &memory.metadata.role {\n println!(\" Role: {}\", role);\n }\n\n // Display topics\n if !memory.metadata.topics.is_empty() {\n println!(\" Topics: {}\", memory.metadata.topics.join(\", \"));\n }\n\n // Display keywords from custom metadata\n if let Some(keywords) = memory.metadata.custom.get(\"keywords\") {\n if let Some(keywords_array) = keywords.as_array() {\n let keyword_strings: Vec = keywords_array\n .iter()\n .filter_map(|k| k.as_str())\n .map(|s| s.to_string())\n .collect();\n if !keyword_strings.is_empty() {\n println!(\" Keywords: {}\", keyword_strings.join(\", \"));\n }\n }\n }\n\n println!();\n }\n }\n\n info!(\"List completed: {} memories found\", memories.len());\n }\n Err(e) => {\n error!(\"Failed to list memories: {}\", e);\n println!(\"❌ List failed: {}\", e);\n return Err(e.into());\n }\n }\n\n Ok(())\n }\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 4.0, - "lines_of_code": 59, + "cyclomatic_complexity": 16.0, + "lines_of_code": 118, "number_of_classes": 1, "number_of_functions": 2 }, @@ -2144,163 +2231,98 @@ Code analysis results from preprocessing phase, including definitions of functio "is_external": false, "line_number": 1, "name": "cortex_mem_core::memory::MemoryManager", - "path": "cortex_mem_core::memory::MemoryManager", + "path": "cortex-mem-core/src/memory/mod.rs", "version": null }, { - "dependency_type": "crate", - "is_external": true, + "dependency_type": "struct", + "is_external": false, "line_number": 2, - "name": "tracing", - "path": "tracing", + "name": "cortex_mem_core::types::Filters", + "path": "cortex-mem-core/src/types/mod.rs", "version": null }, { - "dependency_type": "module", + "dependency_type": "enum", "is_external": false, - "line_number": 13, - "name": "std::io", - "path": "std::io", + "line_number": 2, + "name": "cortex_mem_core::types::MemoryType", + "path": "cortex-mem-core/src/types/mod.rs", "version": null - } - ], - "detailed_description": "The DeleteCommand struct implements a CLI command that safely deletes a memory entry by first retrieving it to confirm existence and display details to the user. It then prompts for confirmation before proceeding with deletion via the MemoryManager. Successful or failed operations are logged using tracing, and appropriate feedback is printed to the console. The execute method handles three main cases: memory found (with confirmation flow), memory not found, and retrieval errors.", - "interfaces": [ - { - "description": "Creates a new instance of DeleteCommand with the provided MemoryManager", - "interface_type": "constructor", - "name": "DeleteCommand::new", - "parameters": [ - { - "description": "Injected dependency for memory operations", - "is_optional": false, - "name": "memory_manager", - "param_type": "MemoryManager" - } - ], - "return_type": "DeleteCommand", - "visibility": "public" }, { - "description": "Executes the deletion process with user confirmation and returns result", - "interface_type": "method", - "name": "DeleteCommand::execute", - "parameters": [ - { - "description": "The ID of the memory entry to delete", - "is_optional": false, - "name": "id", - "param_type": "String" - } - ], - "return_type": "Result<(), Box>", - "visibility": "public" - } - ], - "responsibilities": [ - "Handle user interaction for memory deletion with confirmation prompt", - "Orchestrate the retrieval and deletion of memory entries via MemoryManager", - "Provide user feedback through console output and structured logging", - "Manage error handling and propagation for deletion operations" - ] - }, - { - "code_dossier": { - "code_purpose": "command", - "description": "Command-line interface component for searching memories with flexible filtering and metadata display.", - "file_path": "cortex-mem-cli/src/commands/search.rs", - "functions": [ - "new", - "execute" - ], - "importance_score": 0.8, - "interfaces": [ - "SearchCommand::new", - "SearchCommand::execute" - ], - "name": "search.rs", - "source_summary": "use cortex_mem_core::{memory::MemoryManager, types::Filters};\nuse serde_json::Value;\nuse tracing::info;\n\npub struct SearchCommand {\n memory_manager: MemoryManager,\n}\n\nimpl SearchCommand {\n pub fn new(memory_manager: MemoryManager) -> Self {\n Self { memory_manager }\n }\n\n pub async fn execute(\n &self,\n query: Option,\n user_id: Option,\n agent_id: Option,\n topics: Option>,\n keywords: Option>,\n limit: usize,\n ) -> Result<(), Box> {\n let mut filters = Filters::new();\n\n if let Some(user_id) = user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = agent_id {\n filters.agent_id = Some(agent_id);\n }\n \n if let Some(topics) = topics {\n filters.topics = Some(topics);\n }\n \n if let Some(keywords) = keywords {\n filters.custom.insert(\"keywords\".to_string(), Value::Array(\n keywords.into_iter().map(Value::String).collect()\n ));\n }\n\n // 如果没有查询字符串但有元数据过滤器,使用 list 方法\n let results = if let Some(query_str) = &query {\n self.memory_manager.search(query_str, &filters, limit).await?\n } else {\n // 将 list 结果转换为 ScoredMemory 格式\n let memories = self.memory_manager.list(&filters, Some(limit)).await?;\n memories.into_iter()\n .map(|memory| cortex_mem_core::types::ScoredMemory {\n memory,\n score: 0.0, // list 操作没有相似度分数\n })\n .collect()\n };\n\n if results.is_empty() {\n if let Some(query_str) = &query {\n println!(\"🔍 No memories found for query: '{}'\", query_str);\n } else {\n println!(\"🔍 No memories found with the specified filters\");\n }\n } else {\n if let Some(query_str) = &query {\n println!(\"🔍 Found {} memories for query: '{}'\", results.len(), query_str);\n } else {\n println!(\"🔍 Found {} memories with the specified filters\", results.len());\n }\n println!();\n\n for (i, scored_memory) in results.iter().enumerate() {\n println!(\n \"{}. [Score: {:.3}] ID: {}\",\n i + 1,\n scored_memory.score,\n scored_memory.memory.id\n );\n println!(\" Content: {}\", scored_memory.memory.content);\n println!(\" Type: {:?}\", scored_memory.memory.metadata.memory_type);\n println!(\n \" Created: {}\",\n scored_memory.memory.created_at.format(\"%Y-%m-%d %H:%M:%S\")\n );\n\n if let Some(user_id) = &scored_memory.memory.metadata.user_id {\n println!(\" User: {}\", user_id);\n }\n\n if let Some(agent_id) = &scored_memory.memory.metadata.agent_id {\n println!(\" Agent: {}\", agent_id);\n }\n \n // Display topics\n if !scored_memory.memory.metadata.topics.is_empty() {\n println!(\" Topics: {}\", scored_memory.memory.metadata.topics.join(\", \"));\n }\n \n // Display keywords from custom metadata\n if let Some(keywords) = scored_memory.memory.metadata.custom.get(\"keywords\") {\n if let Some(keywords_array) = keywords.as_array() {\n let keyword_strings: Vec = keywords_array\n .iter()\n .filter_map(|k| k.as_str())\n .map(|s| s.to_string())\n .collect();\n if !keyword_strings.is_empty() {\n println!(\" Keywords: {}\", keyword_strings.join(\", \"));\n }\n }\n }\n\n println!();\n }\n }\n\n info!(\"Search completed: {} results found\", results.len());\n\n Ok(())\n }\n}\n" - }, - "complexity_metrics": { - "cyclomatic_complexity": 18.0, - "lines_of_code": 120, - "number_of_classes": 1, - "number_of_functions": 2 - }, - "dependencies": [ - { - "dependency_type": "library", + "dependency_type": "struct", "is_external": true, - "line_number": 1, - "name": "cortex_mem_core", - "path": "cortex_mem_core::{memory::MemoryManager, types::Filters}", + "line_number": 3, + "name": "serde_json::Value", + "path": null, "version": null }, { - "dependency_type": "library", + "dependency_type": "function", "is_external": true, - "line_number": 2, - "name": "serde_json", - "path": "serde_json::Value", + "line_number": 4, + "name": "tracing::error", + "path": null, "version": null }, { - "dependency_type": "library", + "dependency_type": "function", "is_external": true, - "line_number": 3, - "name": "tracing", - "path": "tracing::info", + "line_number": 4, + "name": "tracing::info", + "path": null, "version": null } ], - "detailed_description": "This component implements a CLI command to search for memory entries using either a semantic query or metadata-based filters. It supports filtering by user_id, agent_id, topics, and keywords. When a query is provided, it performs a semantic search; otherwise, it lists memories matching the metadata filters. Results are formatted and printed to stdout with detailed metadata including score, content, type, timestamps, and custom keywords. The component integrates with a MemoryManager for backend operations and uses structured logging via tracing.", + "detailed_description": "The ListCommand struct is responsible for executing the 'list' functionality in a CLI tool that interfaces with a memory management system. It accepts various filter parameters (user_id, agent_id, memory_type, topics, keywords, limit) and constructs a Filters object used to query the MemoryManager. The execute method handles asynchronous retrieval of memory entries, processes the results, and prints them in a human-readable format to stdout. It includes detailed formatting of metadata such as creation time, topics, and keywords. Error handling is performed via structured logging (tracing) and user-facing error messages. This component serves as a bridge between user input and the core memory storage layer, translating high-level commands into backend queries and presenting results clearly.", "interfaces": [ { - "description": "Creates a new SearchCommand instance with injected MemoryManager", + "description": "Constructs a new instance of ListCommand with a given MemoryManager", "interface_type": "constructor", - "name": "SearchCommand::new", + "name": "ListCommand::new", "parameters": [ { - "description": "Dependency-injected memory manager for backend operations", + "description": "Injected dependency for accessing memory data", "is_optional": false, "name": "memory_manager", "param_type": "MemoryManager" } ], - "return_type": "SearchCommand", + "return_type": "ListCommand", "visibility": "public" }, { - "description": "Executes the search operation with provided filters and displays results", + "description": "Executes the list operation with provided filters and prints formatted results", "interface_type": "method", - "name": "SearchCommand::execute", + "name": "ListCommand::execute", "parameters": [ { - "description": "Optional semantic search query string", + "description": "Optional filter by user ID", "is_optional": true, - "name": "query", + "name": "user_id", "param_type": "Option" }, { - "description": "Optional user identifier filter", + "description": "Optional filter by agent ID", "is_optional": true, - "name": "user_id", + "name": "agent_id", "param_type": "Option" }, { - "description": "Optional agent identifier filter", + "description": "Optional filter by memory type", "is_optional": true, - "name": "agent_id", + "name": "memory_type", "param_type": "Option" }, { - "description": "Optional list of topic filters", + "description": "Optional filter by topic list", "is_optional": true, "name": "topics", "param_type": "Option>" }, { - "description": "Optional list of keyword filters", + "description": "Optional filter by keyword list", "is_optional": true, "name": "keywords", "param_type": "Option>" @@ -2317,2296 +2339,2236 @@ Code analysis results from preprocessing phase, including definitions of functio } ], "responsibilities": [ - "Orchestrate memory search operations using MemoryManager based on user input", - "Parse and apply multiple filter criteria (user, agent, topics, keywords) to queries", - "Format and display search results in a human-readable CLI output format", - "Handle both semantic search and metadata listing operations conditionally", - "Log operational events and results for observability" + "Parse and apply filter criteria (user, agent, type, topics, keywords) for memory listing", + "Interact with MemoryManager to retrieve filtered list of memories", + "Format and display memory entries in a readable CLI output format", + "Handle success and error cases with appropriate logging and user feedback", + "Manage optional parameters and construct complex filter conditions dynamically" ] }, { "code_dossier": { - "code_purpose": "service", - "description": "Service for handling MCP tool calls related to memory management, implementing store, query, list, and get operations for AI agent memories via defined tool interfaces.", - "file_path": "cortex-mem-mcp/src/lib.rs", + "code_purpose": "command", + "description": "CLI命令组件,用于执行记忆系统的优化操作,包括预览、执行、状态查询和配置管理。", + "file_path": "cortex-mem-cli/src/commands/optimize.rs", "functions": [ - "new", - "with_config_path", - "with_config_path_and_agent", - "store_memory", - "query_memory", - "list_memories", - "get_memory", - "find_default_config_path", - "tools_error_to_mcp_error" + "run_optimize", + "run_preview", + "run_optimization", + "run_status", + "run_config", + "build_optimization_request", + "create_optimizer", + "get_memory_details", + "format_severity", + "truncate_content", + "prompt_for_confirmation" ], "importance_score": 0.8, "interfaces": [ - "ServerHandler::get_info", - "ServerHandler::list_tools", - "ServerHandler::call_tool" + "OptimizeCommand", + "OptimizationStatusCommand", + "OptimizationConfigCommand", + "OptimizeCommandRunner" ], - "name": "lib.rs", - "source_summary": "use anyhow::Result;\nuse cortex_mem_config::Config;\nuse cortex_mem_core::{\n init::initialize_memory_system,\n memory::MemoryManager,\n};\nuse cortex_mem_tools::{MemoryOperations, MemoryToolsError, map_mcp_arguments_to_payload, tools_error_to_mcp_error_code, get_tool_error_message, get_mcp_tool_definitions};\nuse rmcp::{\n model::{\n CallToolRequestParam, CallToolResult, Content, ErrorData, ListToolsResult,\n PaginatedRequestParam, ServerCapabilities, ServerInfo, Tool,\n },\n service::RequestContext,\n RoleServer, ServerHandler,\n};\nuse serde_json::Map;\nuse std::path::{Path, PathBuf};\nuse std::sync::Arc;\nuse tracing::{error, info};\n\n/// Service for handling MCP tool calls related to memory management\npub struct MemoryMcpService {\n memory_manager: Arc,\n operations: MemoryOperations,\n agent_id: Option,\n}\n\nimpl MemoryMcpService {\n /// Create a new memory MCP service with default config path\n pub async fn new() -> Result {\n // Try to find config.toml in standard locations\n let config_path = Self::find_default_config_path()\n .unwrap_or_else(|| Path::new(\"config.toml\").to_path_buf());\n Self::with_config_path(config_path).await\n }\n\n /// Create a new memory MCP service with specific config path\n pub async fn with_config_path + Clone + std::fmt::Debug>(\n path: P,\n ) -> Result {\n Self::with_config_path_and_agent(path, None).await\n }\n\n /// Create a new memory MCP service with specific config path and agent\n pub async fn with_config_path_and_agent + Clone + std::fmt::Debug>(\n path: P,\n agent_id: Option,\n ) -> Result {\n // Load configuration from specified path\n let config = Config::load(path.clone())?;\n info!(\"Loaded configuration from: {:?}\", path);\n\n // Initialize vector store and LLM client\n let (vector_store, llm_client) = initialize_memory_system(&config).await?;\n info!(\"Initialized vector store and LLM client\");\n\n // Create memory manager\n let memory_manager = Arc::new(MemoryManager::new(\n vector_store,\n llm_client,\n config.memory.clone(),\n ));\n info!(\"Created memory manager\");\n\n // Create operations handler\n let operations = MemoryOperations::new(\n memory_manager.clone(),\n None, // Default user ID will be derived from agent ID\n agent_id.clone(),\n 100, // Default limit\n );\n\n Ok(Self {\n memory_manager,\n operations,\n agent_id,\n })\n }\n\n /// Tool implementation for storing a memory\n async fn store_memory(\n &self,\n arguments: &Map,\n ) -> Result {\n let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id);\n\n match self.operations.store_memory(payload).await {\n Ok(response) => {\n Ok(CallToolResult::success(vec![Content::text(\n serde_json::to_string_pretty(&response).unwrap(),\n )]))\n }\n Err(e) => {\n error!(\"Failed to store memory: {}\", e);\n Err(self.tools_error_to_mcp_error(e))\n }\n }\n }\n\n /// Tool implementation for querying memories\n async fn query_memory(\n &self,\n arguments: &Map,\n ) -> Result {\n let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id);\n\n match self.operations.query_memory(payload).await {\n Ok(response) => {\n Ok(CallToolResult::success(vec![Content::text(\n serde_json::to_string_pretty(&response).unwrap(),\n )]))\n }\n Err(e) => {\n error!(\"Failed to query memories: {}\", e);\n Err(self.tools_error_to_mcp_error(e))\n }\n }\n }\n\n /// Tool implementation for listing memories\n async fn list_memories(\n &self,\n arguments: &Map,\n ) -> Result {\n let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id);\n\n match self.operations.list_memories(payload).await {\n Ok(response) => {\n Ok(CallToolResult::success(vec![Content::text(\n serde_json::to_string_pretty(&response).unwrap(),\n )]))\n }\n Err(e) => {\n error!(\"Failed to list memories: {}\", e);\n Err(self.tools_error_to_mcp_error(e))\n }\n }\n }\n\n /// Tool implementation for getting a specific memory by ID\n async fn get_memory(\n &self,\n arguments: &Map,\n ) -> Result {\n let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id);\n\n match self.operations.get_memory(payload).await {\n Ok(response) => {\n Ok(CallToolResult::success(vec![Content::text(\n serde_json::to_string_pretty(&response).unwrap(),\n )]))\n }\n Err(e) => {\n error!(\"Failed to get memory: {}\", e);\n Err(self.tools_error_to_mcp_error(e))\n }\n }\n }\n\n /// Find default configuration file path\n fn find_default_config_path() -> Option {\n // Try current directory first\n if let Ok(current_dir) = std::env::current_dir() {\n let current_config = current_dir.join(\"config.toml\");\n if current_config.exists() {\n return Some(current_config);\n }\n }\n\n // Try user home directory\n if let Some(home_dir) = dirs::home_dir() {\n let user_config = home_dir.join(\".config\").join(\"memo\").join(\"config.toml\");\n if user_config.exists() {\n return Some(user_config);\n }\n }\n\n // Try system config directory (platform-specific)\n #[cfg(target_os = \"macos\")]\n let system_config = Path::new(\"/usr/local/etc/memo/config.toml\");\n\n #[cfg(target_os = \"linux\")]\n let system_config = Path::new(\"/etc/memo/config.toml\");\n\n #[cfg(target_os = \"windows\")]\n let system_config = Path::new(\"C:\\\\ProgramData\\\\memo\\\\config.toml\");\n\n if system_config.exists() {\n return Some(system_config.to_path_buf());\n }\n\n None\n }\n\n /// Helper function to convert MemoryToolsError to MCP ErrorData\n fn tools_error_to_mcp_error(&self, error: MemoryToolsError) -> ErrorData {\n ErrorData {\n code: rmcp::model::ErrorCode(tools_error_to_mcp_error_code(&error)).into(),\n message: get_tool_error_message(&error).into(),\n data: None,\n }\n }\n}\n\nimpl ServerHandler for MemoryMcpService {\n fn get_info(&self) -> ServerInfo {\n ServerInfo {\n protocol_version: rmcp::model::ProtocolVersion::V_2024_11_05,\n capabilities: ServerCapabilities::builder().enable_tools().build(),\n server_info: rmcp::model::Implementation::from_build_env(),\n instructions: Some(\n \"A memory management system for AI agents. Store, search, and retrieve memories using natural language queries. Supports different types of memories including conversational, procedural, and factual memories.\"\n .to_string(),\n ),\n }\n }\n\n fn list_tools(\n &self,\n _request: Option,\n _context: RequestContext,\n ) -> impl std::future::Future> + Send + '_ {\n async move {\n let tool_definitions = get_mcp_tool_definitions();\n let tools: Vec = tool_definitions.into_iter().map(|def| {\n Tool {\n name: def.name.into(),\n title: def.title.map(|t| t.into()),\n description: def.description.map(|d| d.into()),\n input_schema: def.input_schema.as_object().unwrap().clone().into(),\n output_schema: def.output_schema.map(|schema| schema.as_object().unwrap().clone().into()),\n annotations: None,\n icons: None,\n meta: None,\n }\n }).collect();\n\n Ok(ListToolsResult {\n tools,\n next_cursor: None,\n })\n }\n }\n\n fn call_tool(\n &self,\n request: CallToolRequestParam,\n _context: RequestContext,\n ) -> impl std::future::Future> + Send + '_ {\n async move {\n let tool_name = &request.name;\n\n match tool_name.as_ref() {\n \"store_memory\" => {\n if let Some(arguments) = &request.arguments {\n self.store_memory(arguments).await\n } else {\n Err(ErrorData {\n code: rmcp::model::ErrorCode(-32602).into(),\n message: \"Missing arguments\".into(),\n data: None,\n })\n }\n }\n \"query_memory\" => {\n if let Some(arguments) = &request.arguments {\n self.query_memory(arguments).await\n } else {\n Err(ErrorData {\n code: rmcp::model::ErrorCode(-32602).into(),\n message: \"Missing arguments\".into(),\n data: None,\n })\n }\n }\n \"list_memories\" => {\n if let Some(arguments) = &request.arguments {\n self.list_memories(arguments).await\n } else {\n Err(ErrorData {\n code: rmcp::model::ErrorCode(-32602).into(),\n message: \"Missing arguments\".into(),\n data: None,\n })\n }\n }\n \"get_memory\" => {\n if let Some(arguments) = &request.arguments {\n self.get_memory(arguments).await\n } else {\n Err(ErrorData {\n code: rmcp::model::ErrorCode(-32602).into(),\n message:\n \"Missing arguments. You must provide 'memory_id' for this tool.\"\n .into(),\n data: None,\n })\n }\n }\n _ => Err(ErrorData {\n code: rmcp::model::ErrorCode(-32601).into(),\n message: format!(\"Unknown tool: {}\", tool_name).into(),\n data: None,\n }),\n }\n }\n }\n}\n" + "name": "optimize.rs", + "source_summary": "use clap::Parser;\nuse cortex_mem_core::{\n config::Config,\n memory::{DefaultMemoryOptimizer, MemoryManager},\n};\nuse std::sync::Arc;\n\n/// 优化命令\n#[derive(Parser)]\npub struct OptimizeCommand {\n /// 优化策略\n #[arg(long, default_value = \"full\")]\n pub strategy: String,\n\n /// 用户ID过滤\n #[arg(long)]\n pub user_id: Option,\n\n /// Agent ID过滤\n #[arg(long)]\n pub agent_id: Option,\n\n /// 记忆类型过滤\n #[arg(long)]\n pub memory_type: Option,\n\n /// 预览模式(不执行)\n #[arg(long)]\n pub preview: bool,\n\n /// 激进模式(更深层优化)\n #[arg(long)]\n pub aggressive: bool,\n\n /// 跳过确认\n #[arg(long)]\n pub no_confirm: bool,\n\n /// 超时时间(分钟)\n #[arg(long, default_value = \"30\")]\n pub timeout: u64,\n\n /// 显示详细内容(预览时显示记忆摘要)\n #[arg(long)]\n pub verbose: bool,\n\n /// 限制显示的问题数量(默认10)\n #[arg(long, default_value = \"10\")]\n pub limit: usize,\n}\n\n/// 优化状态命令\n#[derive(Parser)]\npub struct OptimizationStatusCommand {\n /// 显示详细指标\n #[arg(long)]\n pub detailed: bool,\n\n /// 显示历史记录\n #[arg(long)]\n pub history: bool,\n}\n\n/// 优化配置命令\n#[derive(Parser)]\npub struct OptimizationConfigCommand {\n /// 显示当前配置\n #[arg(long)]\n pub show: bool,\n\n /// 更新配置\n #[arg(long)]\n pub update: bool,\n\n /// 配置文件路径\n #[arg(conflicts_with = \"show\")]\n pub config_file: Option,\n}\n\n/// 优化命令执行器\npub struct OptimizeCommandRunner {\n memory_manager: Arc,\n config: Config,\n}\n\nimpl OptimizeCommandRunner {\n pub fn new(memory_manager: Arc, config: Config) -> Self {\n Self {\n memory_manager,\n config,\n }\n }\n\n pub async fn run_optimize(\n &self,\n cmd: &OptimizeCommand,\n ) -> Result<(), Box> {\n // 1. 构建优化请求\n let request = self.build_optimization_request(cmd)?;\n\n // 2. 创建优化器\n let optimizer = self.create_optimizer().await?;\n\n // 3. 执行优化\n if cmd.preview {\n self.run_preview(optimizer.as_ref(), &request).await?;\n } else {\n self.run_optimization(optimizer.as_ref(), &request, cmd.no_confirm)\n .await?;\n }\n\n Ok(())\n }\n\n async fn create_optimizer(\n &self,\n ) -> Result, Box> {\n // 使用默认的优化配置\n let optimization_config = cortex_mem_core::types::OptimizationConfig::default();\n\n let optimizer =\n DefaultMemoryOptimizer::new(self.memory_manager.clone(), optimization_config);\n\n Ok(Arc::new(optimizer))\n }\n\n async fn run_preview(\n &self,\n optimizer: &dyn cortex_mem_core::memory::MemoryOptimizer,\n request: &cortex_mem_core::types::OptimizationRequest,\n ) -> Result<(), Box> {\n println!(\"🔍 优化计划预览\");\n println!(\"策略: {:?}\", request.strategy);\n println!(\"过滤器: {:?}\", request.filters);\n println!();\n\n // 创建优化计划,添加错误处理\n let plan = match optimizer\n .create_optimization_plan(request.strategy.clone())\n .await\n {\n Ok(plan) => plan,\n Err(e) => {\n // 检查是否是API限制错误\n let error_str = e.to_string().to_lowercase();\n if error_str.contains(\"too many requests\") || error_str.contains(\"429\") {\n println!(\"⚠️ API请求频率限制,无法生成优化计划\");\n println!(\"💡 请稍后再试,或使用 --limit 参数减少查询数量\");\n return Ok(());\n } else {\n return Err(Box::new(e));\n }\n }\n };\n\n // 检查是否是详细模式\n let verbose = request\n .filters\n .custom_filters\n .get(\"verbose\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 显示问题统计\n println!(\"📊 问题统计:\");\n let issue_stats = plan.issue_statistics();\n println!(\" - 总问题数: {}\", issue_stats.total());\n println!(\n \" - 严重: {} 个, 高: {} 个, 中: {} 个, 低: {} 个\",\n issue_stats.critical_count,\n issue_stats.high_count,\n issue_stats.medium_count,\n issue_stats.low_count\n );\n\n if verbose {\n println!(\n \" - 重复: {} 个, 质量: {} 个, 相关性: {} 个, 分类: {} 个, 空间: {} 个\",\n issue_stats.duplicate_issues,\n issue_stats.quality_issues,\n issue_stats.relevance_issues,\n issue_stats.classification_issues,\n issue_stats.space_issues\n );\n }\n\n println!();\n println!(\"📋 检测到的问题:\");\n\n // 获取受影响的记忆详细信息(仅在详细模式下)\n // 添加错误处理,当遇到API限制时回退到非详细模式\n let memory_details = if verbose {\n match self.get_memory_details(&plan.issues).await {\n Ok(details) => Some(details),\n Err(e) => {\n // 检查是否是API限制错误\n let error_str = e.to_string().to_lowercase();\n if error_str.contains(\"too many requests\") || error_str.contains(\"429\") {\n println!(\"⚠️ API请求频率限制,回退到非详细模式\");\n None\n } else {\n return Err(e);\n }\n }\n }\n } else {\n None\n };\n\n // 如果原本请求详细信息但失败了,更新verbose标志\n let effective_verbose = verbose && memory_details.is_some();\n\n // 限制显示的问题数量\n let display_issues: Vec<_> = plan\n .issues\n .iter()\n .take(\n request\n .filters\n .custom_filters\n .get(\"limit\")\n .and_then(|v| v.as_u64())\n .unwrap_or(10) as usize,\n )\n .collect();\n\n for (i, issue) in display_issues.iter().enumerate() {\n println!(\n \" {}. [{}] {}\",\n i + 1,\n self.format_severity(issue.severity.clone()),\n issue.description\n );\n\n // 在详细模式下显示受影响的记忆信息\n if effective_verbose {\n if let Some(ref details) = memory_details {\n for memory_id in &issue.affected_memories {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📝 记忆ID: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n println!(\n \" 📖 内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 50)\n );\n println!(\n \" 🏷️ 类型: {:?}, 重要性: {:.2}, 创建: {}\",\n memory.metadata.memory_type,\n memory.metadata.importance_score,\n memory.created_at.format(\"%Y-%m-%d\")\n );\n if memory.metadata.user_id.is_some()\n || memory.metadata.agent_id.is_some()\n {\n println!(\n \" 👤 用户: {:?}, 代理: {:?}\",\n memory.metadata.user_id, memory.metadata.agent_id\n );\n }\n } else {\n println!(\n \" 📝 记忆ID: {}... (无法获取详细信息)\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n }\n }\n } else {\n // 详细模式回退到非详细模式\n println!(\n \" 📝 影响记忆: {} 个 (详细查看受API限制)\",\n issue.affected_memories.len()\n );\n }\n } else {\n // 非详细模式,只显示记忆ID数量\n println!(\" 📝 影响记忆: {} 个\", issue.affected_memories.len());\n }\n\n println!(\" 💡 建议: {}\", issue.recommendation);\n println!();\n }\n\n if plan.issues.len() > display_issues.len() {\n println!(\n \" ... 还有 {} 个问题未显示,使用 --limit 查看更多\",\n plan.issues.len() - display_issues.len()\n );\n }\n\n println!(\"🎯 建议的操作:\");\n\n // 获取操作统计\n let action_stats = plan.action_statistics();\n println!(\"📈 操作统计:\");\n println!(\" - 总操作数: {}\", action_stats.total());\n println!(\n \" - 合并: {} 个, 删除: {} 个, 更新: {} 个, 重分类: {} 个, 归档: {} 个\",\n action_stats.merge_count,\n action_stats.delete_count,\n action_stats.update_count,\n action_stats.reclassify_count,\n action_stats.archive_count\n );\n\n println!();\n let display_actions: Vec<_> = plan\n .actions\n .iter()\n .take(display_issues.len()) // 显示与问题相同数量的操作\n .collect();\n\n for (i, action) in display_actions.iter().enumerate() {\n println!(\" {}. {:?}\", i + 1, action);\n\n // 在详细模式下为每个操作添加解释\n if verbose {\n if let Some(ref details) = memory_details {\n match action {\n cortex_mem_core::types::OptimizationAction::Delete { memory_id } => {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📖 将删除内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 30)\n );\n }\n }\n cortex_mem_core::types::OptimizationAction::Merge { memories } => {\n println!(\" 🔗 将合并 {} 个记忆\", memories.len());\n if memories.len() > 0 && details.contains_key(&memories[0]) {\n println!(\n \" 📖 示例内容: \\\"{}\\\"\",\n self.truncate_content(&details[&memories[0]].content, 30)\n );\n }\n }\n cortex_mem_core::types::OptimizationAction::Update {\n memory_id,\n updates,\n } => {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📖 更新内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 30)\n );\n if let Some(new_type) = &updates.memory_type {\n println!(\n \" 🏷️ 类型将从 {:?} 更改为 {:?}\",\n memory.metadata.memory_type, new_type\n );\n }\n }\n }\n cortex_mem_core::types::OptimizationAction::Reclassify { memory_id } => {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📖 重新分类内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 30)\n );\n println!(\" 🏷️ 当前类型: {:?}\", memory.metadata.memory_type);\n }\n }\n cortex_mem_core::types::OptimizationAction::Archive { memory_id } => {\n if let Some(memory) = details.get(memory_id) {\n println!(\n \" 📖 归档内容: \\\"{}\\\"\",\n self.truncate_content(&memory.content, 30)\n );\n println!(\n \" ⏰ 创建时间: {}\",\n memory.created_at.format(\"%Y-%m-%d %H:%M\")\n );\n }\n }\n }\n }\n } else {\n // 非详细模式,显示简单操作描述\n match action {\n cortex_mem_core::types::OptimizationAction::Delete { memory_id } => {\n println!(\n \" 🗑️ 删除记忆: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n }\n cortex_mem_core::types::OptimizationAction::Merge { memories } => {\n println!(\" 🔗 合并 {} 个记忆\", memories.len());\n }\n cortex_mem_core::types::OptimizationAction::Update { memory_id, updates } => {\n println!(\n \" ✏️ 更新记忆: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n if let Some(new_type) = &updates.memory_type {\n println!(\" 🏷️ 更新类型为 {:?}\", new_type);\n }\n }\n cortex_mem_core::types::OptimizationAction::Reclassify { memory_id } => {\n println!(\n \" 🔄 重新分类记忆: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n }\n cortex_mem_core::types::OptimizationAction::Archive { memory_id } => {\n println!(\n \" 📦 归档记忆: {}...\",\n &memory_id[..std::cmp::min(8, memory_id.len())]\n );\n }\n }\n }\n println!();\n }\n\n // 显示未处理的操作数量\n if plan.actions.len() > display_actions.len() {\n println!(\n \" ... 还有 {} 个操作未显示\",\n plan.actions.len() - display_actions.len()\n );\n }\n\n println!(\n \"✨ 预计优化后可节省空间 {:.2} MB,提升质量 {:.1}%\",\n 0.1 * plan.issues.len() as f64, // 简单估算\n 5.0 * issue_stats.total() as f64\n ); // 简单估算\n\n Ok(())\n }\n\n async fn run_optimization(\n &self,\n optimizer: &dyn cortex_mem_core::memory::MemoryOptimizer,\n request: &cortex_mem_core::types::OptimizationRequest,\n no_confirm: bool,\n ) -> Result<(), Box> {\n if !no_confirm {\n println!(\"⚠️ 此操作将修改您的memory数据库\");\n let input = prompt_for_confirmation(\"是否继续? (y/N): \");\n if !input {\n println!(\"❌ 操作已取消\");\n return Ok(());\n }\n }\n\n println!(\"🚀 开始执行优化...\");\n\n let result = optimizer.optimize(request).await?;\n\n if result.success {\n println!(\"✅ 优化完成!\");\n println!(\"📊 优化统计:\");\n println!(\" - 执行时间: {:?}\", result.end_time - result.start_time);\n println!(\" - 发现问题: {} 个\", result.issues_found.len());\n println!(\" - 执行操作: {} 个\", result.actions_performed.len());\n\n if let Some(metrics) = result.metrics {\n println!(\" - 节省空间: {:.2} MB\", metrics.saved_space_mb);\n println!(\" - 改善质量: {:.2}%\", metrics.quality_improvement * 100.0);\n }\n } else {\n println!(\n \"❌ 优化失败: {}\",\n result\n .error_message\n .unwrap_or_else(|| \"未知错误\".to_string())\n );\n }\n\n Ok(())\n }\n\n pub async fn run_status(\n &self,\n cmd: &OptimizationStatusCommand,\n ) -> Result<(), Box> {\n println!(\"📈 优化状态\");\n\n if cmd.detailed {\n println!(\"详细指标功能开发中...\");\n }\n\n if cmd.history {\n println!(\"历史记录功能开发中...\");\n }\n\n Ok(())\n }\n\n pub async fn run_config(\n &self,\n cmd: &OptimizationConfigCommand,\n ) -> Result<(), Box> {\n if cmd.show {\n println!(\"优化配置:\");\n println!(\"当前配置功能开发中...\");\n } else if cmd.update {\n println!(\"更新配置功能开发中...\");\n }\n\n Ok(())\n }\n\n fn build_optimization_request(\n &self,\n cmd: &OptimizeCommand,\n ) -> Result> {\n let memory_type = cmd\n .memory_type\n .as_ref()\n .map(|s| cortex_mem_core::types::MemoryType::parse(s));\n\n let strategy = match cmd.strategy.to_lowercase().as_str() {\n \"full\" => cortex_mem_core::types::OptimizationStrategy::Full,\n \"incremental\" => cortex_mem_core::types::OptimizationStrategy::Incremental,\n \"batch\" => cortex_mem_core::types::OptimizationStrategy::Batch,\n \"deduplication\" => cortex_mem_core::types::OptimizationStrategy::Deduplication,\n \"relevance\" => cortex_mem_core::types::OptimizationStrategy::Relevance,\n \"quality\" => cortex_mem_core::types::OptimizationStrategy::Quality,\n \"space\" => cortex_mem_core::types::OptimizationStrategy::Space,\n _ => cortex_mem_core::types::OptimizationStrategy::Full,\n };\n\n let mut custom_filters = std::collections::HashMap::new();\n custom_filters.insert(\n \"limit\".to_string(),\n serde_json::Value::Number(serde_json::Number::from(cmd.limit)),\n );\n custom_filters.insert(\"verbose\".to_string(), serde_json::Value::Bool(cmd.verbose));\n\n let filters = cortex_mem_core::types::OptimizationFilters {\n user_id: cmd.user_id.clone(),\n agent_id: cmd.agent_id.clone(),\n memory_type,\n date_range: None,\n importance_range: None,\n custom_filters,\n };\n\n Ok(cortex_mem_core::types::OptimizationRequest {\n optimization_id: None,\n strategy,\n filters,\n aggressive: cmd.aggressive,\n dry_run: cmd.preview,\n timeout_minutes: Some(cmd.timeout),\n })\n }\n}\n\nfn prompt_for_confirmation(prompt: &str) -> bool {\n use std::io::{self, Write};\n\n print!(\"{}\", prompt);\n io::stdout().flush().unwrap();\n\n let mut input = String::new();\n io::stdin().read_line(&mut input).unwrap_or_default();\n\n input.trim().to_lowercase() == \"y\" || input.trim().to_lowercase() == \"yes\"\n}\n\nimpl OptimizeCommandRunner {\n /// 获取记忆详细信息\n async fn get_memory_details(\n &self,\n issues: &[cortex_mem_core::types::OptimizationIssue],\n ) -> Result<\n std::collections::HashMap,\n Box,\n > {\n let mut memory_details = std::collections::HashMap::new();\n\n // 收集所有需要获取的记忆ID\n let mut all_memory_ids = std::collections::HashSet::new();\n for issue in issues {\n for memory_id in &issue.affected_memories {\n all_memory_ids.insert(memory_id.clone());\n }\n }\n\n // 批量获取记忆详情\n for memory_id in all_memory_ids {\n match self.memory_manager.get(&memory_id).await {\n Ok(Some(memory)) => {\n // 记录记忆内容状态\n if memory.content.trim().is_empty() {\n tracing::warn!(\"记忆 {} 内容为空\", memory_id);\n } else {\n tracing::debug!(\"记忆 {} 内容长度: {}\", memory_id, memory.content.len());\n }\n memory_details.insert(memory_id, memory);\n }\n Ok(None) => {\n tracing::warn!(\"记忆 {} 不存在\", memory_id);\n }\n Err(e) => {\n tracing::warn!(\"无法获取记忆 {} 的详细信息: {}\", memory_id, e);\n }\n }\n }\n\n Ok(memory_details)\n }\n\n /// 格式化严重程度\n fn format_severity(&self, severity: cortex_mem_core::types::IssueSeverity) -> String {\n match severity {\n cortex_mem_core::types::IssueSeverity::Critical => \"🔴 严重\".to_string(),\n cortex_mem_core::types::IssueSeverity::High => \"🟠 高\".to_string(),\n cortex_mem_core::types::IssueSeverity::Medium => \"🟡 中\".to_string(),\n cortex_mem_core::types::IssueSeverity::Low => \"🟢 低\".to_string(),\n }\n }\n\n /// 截断内容(安全处理Unicode字符)\n fn truncate_content(&self, content: &str, max_length: usize) -> String {\n if content.len() <= max_length {\n content.to_string()\n } else {\n // 安全地找到字符边界\n let end = match content.char_indices().nth(max_length) {\n Some((idx, _)) => idx,\n None => content.len(),\n };\n format!(\"{}...\", &content[..end])\n }\n }\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 23.0, - "lines_of_code": 308, - "number_of_classes": 1, - "number_of_functions": 13 + "cyclomatic_complexity": 45.0, + "lines_of_code": 631, + "number_of_classes": 4, + "number_of_functions": 11 }, "dependencies": [ { - "dependency_type": "use", + "dependency_type": "crate", "is_external": true, "line_number": 1, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": false, - "line_number": 2, - "name": "cortex_mem_config", + "name": "clap", "path": null, "version": null }, { - "dependency_type": "use", - "is_external": false, + "dependency_type": "crate", + "is_external": true, "line_number": 3, "name": "cortex_mem_core", "path": null, "version": null }, { - "dependency_type": "use", + "dependency_type": "std", "is_external": false, "line_number": 6, - "name": "cortex_mem_tools", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": 9, - "name": "rmcp", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": 16, - "name": "serde_json", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": 18, - "name": "tracing", + "name": "std", "path": null, "version": null } ], - "detailed_description": "This component implements a service layer that bridges MCP (Memory Control Protocol) tool invocation requests with the underlying memory management system. It provides structured tools for AI agents to persist, retrieve, and query memories using natural language. The service initializes the memory system (vector store and LLM client) from configuration, manages a MemoryManager instance, and routes incoming tool calls (e.g., store_memory, query_memory) to the appropriate operations via MemoryOperations. It handles error translation from domain-specific MemoryToolsError to standardized MCP ErrorData, ensuring interoperability. Configuration is loaded from multiple fallback paths (current directory, home directory, system paths), supporting flexible deployment. The component adheres to the ServerHandler trait, exposing capabilities like tool listing and execution within the MCP ecosystem.", + "detailed_description": "该组件是Cortex Memory CLI系统中的核心优化命令执行器,实现了基于CLAP的命令行接口。主要功能包括:1) 提供多种过滤选项(用户ID、Agent ID、记忆类型)和策略选择(全量、增量、去重等)的优化命令;2) 支持预览模式,安全地展示即将执行的优化计划和建议;3) 实现详细的交互式确认机制,防止误操作;4) 提供API频率限制的优雅降级处理,在请求受限时自动回退到基础模式;5) 具备丰富的可视化输出,使用emoji和格式化文本提升用户体验。组件通过MemoryManager与底层记忆系统交互,利用DefaultMemoryOptimizer执行具体的优化逻辑。代码结构清晰,遵循单一职责原则,将命令解析、请求构建、执行逻辑和UI展示分离。错误处理完善,对API限制等常见问题提供用户友好的反馈。整体设计体现了CLI工具的最佳实践,兼顾功能性、安全性和用户体验。", "interfaces": [ { - "description": "Creates a new service instance using default config path", - "interface_type": "function", - "name": "new", - "parameters": [], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "Creates a new service instance with specified config path", - "interface_type": "function", - "name": "with_config_path", + "description": "优化命令的CLI参数结构,使用CLAP派生实现命令行参数解析", + "interface_type": "struct", + "name": "OptimizeCommand", "parameters": [ { - "description": "Path to configuration file", + "description": "优化策略,支持full、incremental等多种模式", "is_optional": false, - "name": "path", - "param_type": "P" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "Creates a new service with config path and optional agent context", - "interface_type": "function", - "name": "with_config_path_and_agent", - "parameters": [ + "name": "strategy", + "param_type": "String" + }, { - "description": "Path to configuration file", - "is_optional": false, - "name": "path", - "param_type": "P" + "description": "用户ID过滤条件", + "is_optional": true, + "name": "user_id", + "param_type": "Option" }, { - "description": "Optional agent identifier", + "description": "Agent ID过滤条件", "is_optional": true, "name": "agent_id", "param_type": "Option" + }, + { + "description": "记忆类型过滤条件", + "is_optional": true, + "name": "memory_type", + "param_type": "Option" + }, + { + "description": "预览模式开关,true时仅展示计划不执行", + "is_optional": false, + "name": "preview", + "param_type": "bool" + }, + { + "description": "激进模式开关,启用更深层优化", + "is_optional": false, + "name": "aggressive", + "param_type": "bool" + }, + { + "description": "跳过确认提示开关", + "is_optional": false, + "name": "no_confirm", + "param_type": "bool" + }, + { + "description": "操作超时时间(分钟)", + "is_optional": false, + "name": "timeout", + "param_type": "u64" + }, + { + "description": "详细模式开关,展示记忆摘要", + "is_optional": false, + "name": "verbose", + "param_type": "bool" + }, + { + "description": "限制显示的问题数量", + "is_optional": false, + "name": "limit", + "param_type": "usize" } ], - "return_type": "Result", + "return_type": null, "visibility": "public" }, { - "description": "Handles storing a new memory using provided arguments", - "interface_type": "function", - "name": "store_memory", + "description": "优化状态查询命令的CLI参数结构", + "interface_type": "struct", + "name": "OptimizationStatusCommand", "parameters": [ { - "description": "Tool call arguments", + "description": "显示详细指标", "is_optional": false, - "name": "arguments", - "param_type": "&Map" + "name": "detailed", + "param_type": "bool" + }, + { + "description": "显示历史记录", + "is_optional": false, + "name": "history", + "param_type": "bool" } ], - "return_type": "Result", - "visibility": "private" + "return_type": null, + "visibility": "public" }, { - "description": "Handles querying memories using natural language or filters", - "interface_type": "function", - "name": "query_memory", + "description": "优化配置管理命令的CLI参数结构", + "interface_type": "struct", + "name": "OptimizationConfigCommand", "parameters": [ { - "description": "Tool call arguments", + "description": "显示当前配置", "is_optional": false, - "name": "arguments", - "param_type": "&Map" + "name": "show", + "param_type": "bool" + }, + { + "description": "更新配置", + "is_optional": false, + "name": "update", + "param_type": "bool" + }, + { + "description": "配置文件路径", + "is_optional": true, + "name": "config_file", + "param_type": "Option" } ], - "return_type": "Result", - "visibility": "private" + "return_type": null, + "visibility": "public" }, { - "description": "Handles listing memories with optional pagination and filtering", - "interface_type": "function", - "name": "list_memories", + "description": "优化命令执行器,协调优化流程的核心组件", + "interface_type": "struct", + "name": "OptimizeCommandRunner", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "执行优化命令的主入口方法", + "interface_type": "method", + "name": "run_optimize", "parameters": [ { - "description": "Tool call arguments", + "description": "优化命令参数", "is_optional": false, - "name": "arguments", - "param_type": "&Map" + "name": "cmd", + "param_type": "&OptimizeCommand" } ], - "return_type": "Result", - "visibility": "private" + "return_type": "Result<(), Box>", + "visibility": "public" }, { - "description": "Retrieves a specific memory by ID", - "interface_type": "function", - "name": "get_memory", + "description": "执行优化预览,展示优化计划", + "interface_type": "method", + "name": "run_preview", "parameters": [ { - "description": "Tool call arguments", + "description": "优化器实例", "is_optional": false, - "name": "arguments", - "param_type": "&Map" + "name": "optimizer", + "param_type": "&dyn cortex_mem_core::memory::MemoryOptimizer" + }, + { + "description": "优化请求", + "is_optional": false, + "name": "request", + "param_type": "&cortex_mem_core::types::OptimizationRequest" } ], - "return_type": "Result", - "visibility": "private" - }, - { - "description": "Finds config file in standard locations with fallback precedence", - "interface_type": "function", - "name": "find_default_config_path", - "parameters": [], - "return_type": "Option", + "return_type": "Result<(), Box>", "visibility": "private" }, { - "description": "Converts internal tool errors to standardized MCP error format", - "interface_type": "function", - "name": "tools_error_to_mcp_error", + "description": "执行实际的优化操作", + "interface_type": "method", + "name": "run_optimization", "parameters": [ { - "description": "Domain-specific error to convert", + "description": "优化器实例", "is_optional": false, - "name": "error", - "param_type": "MemoryToolsError" + "name": "optimizer", + "param_type": "&dyn cortex_mem_core::memory::MemoryOptimizer" + }, + { + "description": "优化请求", + "is_optional": false, + "name": "request", + "param_type": "&cortex_mem_core::types::OptimizationRequest" + }, + { + "description": "是否跳过确认", + "is_optional": false, + "name": "no_confirm", + "param_type": "bool" } ], - "return_type": "ErrorData", + "return_type": "Result<(), Box>", "visibility": "private" }, { - "description": "Implements ServerHandler trait to return server metadata and capabilities", - "interface_type": "trait_method", - "name": "get_info", - "parameters": [], - "return_type": "ServerInfo", + "description": "执行状态查询命令", + "interface_type": "method", + "name": "run_status", + "parameters": [ + { + "description": "状态命令参数", + "is_optional": false, + "name": "cmd", + "param_type": "&OptimizationStatusCommand" + } + ], + "return_type": "Result<(), Box>", "visibility": "public" }, { - "description": "Returns list of available memory management tools for discovery", - "interface_type": "trait_method", - "name": "list_tools", + "description": "执行配置管理命令", + "interface_type": "method", + "name": "run_config", "parameters": [ { - "description": null, - "is_optional": true, - "name": "_request", - "param_type": "Option" - }, - { - "description": null, + "description": "配置命令参数", "is_optional": false, - "name": "_context", - "param_type": "RequestContext" + "name": "cmd", + "param_type": "&OptimizationConfigCommand" } ], - "return_type": "impl Future>", + "return_type": "Result<(), Box>", "visibility": "public" }, { - "description": "Routes incoming tool calls to appropriate handler methods", - "interface_type": "trait_method", - "name": "call_tool", + "description": "根据命令参数构建优化请求对象", + "interface_type": "method", + "name": "build_optimization_request", "parameters": [ { - "description": null, - "is_optional": false, - "name": "request", - "param_type": "CallToolRequestParam" - }, - { - "description": null, + "description": "优化命令参数", "is_optional": false, - "name": "_context", - "param_type": "RequestContext" + "name": "cmd", + "param_type": "&OptimizeCommand" } ], - "return_type": "impl Future>", - "visibility": "public" + "return_type": "Result>", + "visibility": "private" } ], "responsibilities": [ - "Initialize and manage the memory system (vector store, LLM client) based on configuration", - "Handle MCP tool calls for memory operations (store, query, list, get)", - "Translate between MCP tool request format and internal memory operation payloads", - "Provide server metadata and tool discovery via ServerHandler implementation", - "Convert domain-specific errors into standardized MCP error responses" + "解析和执行记忆优化命令行指令", + "构建优化请求并协调优化器执行优化流程", + "提供优化计划预览和结果展示功能", + "实现用户交互和操作确认机制", + "处理API频率限制等外部依赖错误" ] }, { "code_dossier": { - "code_purpose": "api", - "description": "API客户端类,用于与Cortex-mem-service后端服务通信,提供记忆管理、搜索、统计和优化等功能。", - "file_path": "cortex-mem-insights/src/server/integrations/cortex-mem.ts", + "code_purpose": "command", + "description": "Command-line interface component for deleting memory entries with user confirmation and logging.", + "file_path": "cortex-mem-cli/src/commands/delete.rs", "functions": [ - "healthCheck", - "listMemories", - "searchMemories", - "getMemory", - "createMemory", - "updateMemory", - "deleteMemory", - "batchDelete", - "getStatistics", - "optimize", - "getOptimizationStatus", - "cancelOptimization", - "getOptimizationHistory", - "analyzeOptimization", - "getOptimizationStatistics", - "cleanupOptimizationHistory", - "getLLMStatus", - "llmHealthCheck" + "new", + "execute" ], "importance_score": 0.8, "interfaces": [ - "CortexMemServiceClient" + "DeleteCommand::new", + "DeleteCommand::execute" ], - "name": "cortex-mem.ts", - "source_summary": "import { MemoryResponse, SearchResponse, ListResponse, HealthResponse } from '../api/types';\n\n// Cortex-mem-service API 客户端\nexport class CortexMemServiceClient {\n private baseUrl: string;\n \n constructor(baseUrl: string = 'http://localhost:3000') {\n this.baseUrl = baseUrl;\n }\n \n // 健康检查\n async healthCheck(): Promise {\n try {\n const response = await fetch(`${this.baseUrl}/health`);\n if (!response.ok) {\n throw new Error(`Health check failed: ${response.statusText}`);\n }\n return await response.json();\n } catch (error) {\n console.error('Health check error:', error);\n return {\n status: 'unhealthy',\n vector_store: false,\n llm_service: false,\n timestamp: new Date().toISOString()\n };\n }\n }\n \n // 获取记忆列表\n async listMemories(params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n }): Promise {\n try {\n const queryParams = new URLSearchParams();\n if (params?.user_id) queryParams.append('user_id', params.user_id);\n if (params?.agent_id) queryParams.append('agent_id', params.agent_id);\n if (params?.run_id) queryParams.append('run_id', params.run_id);\n if (params?.actor_id) queryParams.append('actor_id', params.actor_id);\n if (params?.memory_type) queryParams.append('memory_type', params.memory_type);\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n \n const url = `${this.baseUrl}/memories${queryParams.toString() ? `?${queryParams}` : ''}`;\n \n const response = await fetch(url);\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('获取记忆列表失败 - 错误响应:', errorText);\n throw new Error(`List memories failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return result;\n } catch (error) {\n console.error('获取记忆列表错误:', error);\n return {\n total: 0,\n memories: [],\n };\n }\n }\n \n // 搜索记忆\n async searchMemories(query: string, params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n }): Promise {\n try {\n const requestBody = {\n query,\n user_id: params?.user_id,\n agent_id: params?.agent_id,\n run_id: params?.run_id,\n actor_id: params?.actor_id,\n memory_type: params?.memory_type,\n limit: params?.limit,\n similarity_threshold: params?.similarity_threshold,\n };\n \n const response = await fetch(`${this.baseUrl}/memories/search`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('搜索记忆失败 - 错误响应:', errorText);\n throw new Error(`Search memories failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return result;\n } catch (error) {\n console.error('搜索记忆错误:', error);\n return {\n total: 0,\n results: [],\n };\n }\n }\n \n // 获取单个记忆\n async getMemory(id: string): Promise {\n try {\n const response = await fetch(`${this.baseUrl}/memories/${id}`);\n \n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`Get memory failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('Get memory error:', error);\n return null;\n }\n }\n \n // 创建记忆\n async createMemory(content: string, metadata?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type?: string;\n custom?: Record;\n }): Promise<{ success: boolean; id?: string; message: string }> {\n try {\n const requestBody = {\n content,\n user_id: metadata?.user_id,\n agent_id: metadata?.agent_id,\n run_id: metadata?.run_id,\n actor_id: metadata?.actor_id,\n role: metadata?.role,\n memory_type: metadata?.memory_type,\n custom: metadata?.custom,\n };\n \n const response = await fetch(`${this.baseUrl}/memories`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n });\n \n if (!response.ok) {\n throw new Error(`Create memory failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return {\n success: true,\n id: result.id,\n message: result.message,\n };\n } catch (error) {\n console.error('Create memory error:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to create memory',\n };\n }\n }\n \n // 更新记忆\n async updateMemory(id: string, content: string): Promise<{ success: boolean; message: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/memories/${id}`, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ content }),\n });\n \n if (!response.ok) {\n throw new Error(`Update memory failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return {\n success: true,\n message: result.message,\n };\n } catch (error) {\n console.error('Update memory error:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to update memory',\n };\n }\n }\n \n // 删除记忆\n async deleteMemory(id: string): Promise<{ success: boolean; message: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/memories/${id}`, {\n method: 'DELETE',\n });\n \n if (!response.ok) {\n throw new Error(`Delete memory failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return {\n success: true,\n message: result.message,\n };\n } catch (error) {\n console.error('Delete memory error:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to delete memory',\n };\n }\n }\n \n // 批量操作\n async batchDelete(ids: string[]): Promise<{ success: boolean; message: string; failed: string[] }> {\n const failed: string[] = [];\n \n for (const id of ids) {\n try {\n await this.deleteMemory(id);\n } catch (error) {\n failed.push(id);\n }\n }\n \n return {\n success: failed.length === 0,\n message: failed.length === 0 \n ? 'All memories deleted successfully' \n : `Failed to delete ${failed.length} memories`,\n failed,\n };\n }\n \n // 统计信息\n async getStatistics(): Promise<{\n total_memories: number;\n by_type: Record;\n by_user: Record;\n by_agent: Record;\n recent_activity: Array<{ date: string; count: number }>;\n }> {\n try {\n // 获取所有记忆\n const listResponse = await this.listMemories({ limit: 1000 });\n \n // 统计类型分布\n const byType: Record = {};\n const byUser: Record = {};\n const byAgent: Record = {};\n \n // 按日期统计最近活动(最近7天)\n const recentActivity: Array<{ date: string; count: number }> = [];\n const today = new Date();\n \n for (let i = 6; i >= 0; i--) {\n const date = new Date(today);\n date.setDate(date.getDate() - i);\n const dateStr = date.toISOString().split('T')[0];\n recentActivity.push({ date: dateStr, count: 0 });\n }\n \n for (const memory of listResponse.memories) {\n // 统计类型\n const type = memory.metadata.memory_type;\n byType[type] = (byType[type] || 0) + 1;\n \n // 统计用户\n if (memory.metadata.user_id) {\n byUser[memory.metadata.user_id] = (byUser[memory.metadata.user_id] || 0) + 1;\n }\n \n // 统计代理\n if (memory.metadata.agent_id) {\n byAgent[memory.metadata.agent_id] = (byAgent[memory.metadata.agent_id] || 0) + 1;\n }\n \n // 统计最近活动\n const memoryDate = new Date(memory.created_at).toISOString().split('T')[0];\n const activityEntry = recentActivity.find(a => a.date === memoryDate);\n if (activityEntry) {\n activityEntry.count++;\n }\n }\n \n return {\n total_memories: listResponse.total,\n by_type: byType,\n by_user: byUser,\n by_agent: byAgent,\n recent_activity: recentActivity,\n };\n } catch (error) {\n console.error('Get statistics error:', error);\n return {\n total_memories: 0,\n by_type: {},\n by_user: {},\n by_agent: {},\n recent_activity: [],\n };\n }\n }\n\n // 优化相关方法\n \n // 启动优化任务\n async optimize(params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n strategy?: string;\n aggressive?: boolean;\n timeout_minutes?: number;\n }): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(params || {}),\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('启动优化失败 - 错误响应:', errorText);\n throw new Error(`Optimize failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('启动优化错误:', error);\n return {\n success: false,\n error: {\n code: 'OPTIMIZE_FAILED',\n message: error instanceof Error ? error.message : '启动优化失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 获取优化任务状态\n async getOptimizationStatus(jobId: string): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/${jobId}`);\n \n if (!response.ok) {\n if (response.status === 404) {\n return {\n success: false,\n error: {\n code: 'JOB_NOT_FOUND',\n message: `优化任务 ${jobId} 不存在`,\n },\n timestamp: new Date().toISOString(),\n };\n }\n throw new Error(`Get optimization status failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('获取优化状态错误:', error);\n return {\n success: false,\n error: {\n code: 'GET_STATUS_FAILED',\n message: error instanceof Error ? error.message : '获取状态失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 取消优化任务\n async cancelOptimization(jobId: string): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/${jobId}/cancel`, {\n method: 'POST',\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('取消优化失败 - 错误响应:', errorText);\n throw new Error(`Cancel optimization failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('取消优化错误:', error);\n return {\n success: false,\n error: {\n code: 'CANCEL_FAILED',\n message: error instanceof Error ? error.message : '取消优化失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 获取优化历史\n async getOptimizationHistory(params?: {\n limit?: number;\n offset?: number;\n status?: string;\n start_date?: string;\n end_date?: string;\n }): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const queryParams = new URLSearchParams();\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.offset) queryParams.append('offset', params.offset.toString());\n if (params?.status) queryParams.append('status', params.status);\n if (params?.start_date) queryParams.append('start_date', params.start_date);\n if (params?.end_date) queryParams.append('end_date', params.end_date);\n \n const url = `${this.baseUrl}/optimization/history${queryParams.toString() ? `?${queryParams}` : ''}`;\n const response = await fetch(url);\n \n if (!response.ok) {\n throw new Error(`Get optimization history failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('获取优化历史错误:', error);\n return {\n success: false,\n error: {\n code: 'GET_HISTORY_FAILED',\n message: error instanceof Error ? error.message : '获取历史失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 分析优化问题(预览模式)\n async analyzeOptimization(params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n }): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/analyze`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(params || {}),\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('分析优化失败 - 错误响应:', errorText);\n throw new Error(`Analyze optimization failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('分析优化错误:', error);\n return {\n success: false,\n error: {\n code: 'ANALYZE_FAILED',\n message: error instanceof Error ? error.message : '分析失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 获取优化统计\n async getOptimizationStatistics(): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/statistics`);\n \n if (!response.ok) {\n throw new Error(`Get optimization statistics failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('获取优化统计错误:', error);\n return {\n success: false,\n error: {\n code: 'GET_STATISTICS_FAILED',\n message: error instanceof Error ? error.message : '获取统计失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 清理优化历史\n async cleanupOptimizationHistory(maxAgeDays?: number): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/cleanup`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ max_age_days: maxAgeDays || 7 }),\n });\n \n if (!response.ok) {\n throw new Error(`Cleanup optimization history failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('清理优化历史错误:', error);\n return {\n success: false,\n error: {\n code: 'CLEANUP_FAILED',\n message: error instanceof Error ? error.message : '清理失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // LLM服务状态检测\n \n // 获取详细的LLM服务状态\n async getLLMStatus(): Promise<{\n overall_status: string;\n completion_model: {\n available: boolean;\n provider: string;\n model_name: string;\n latency_ms?: number;\n error_message?: string;\n last_check: string;\n };\n embedding_model: {\n available: boolean;\n provider: string;\n model_name: string;\n latency_ms?: number;\n error_message?: string;\n last_check: string;\n };\n timestamp: string;\n }> {\n try {\n const response = await fetch(`${this.baseUrl}/llm/status`);\n \n if (!response.ok) {\n throw new Error(`Get LLM status failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('获取LLM状态错误:', error);\n return {\n overall_status: 'error',\n completion_model: {\n available: false,\n provider: 'unknown',\n model_name: 'unknown',\n error_message: error instanceof Error ? error.message : 'Failed to connect',\n last_check: new Date().toISOString(),\n },\n embedding_model: {\n available: false,\n provider: 'unknown',\n model_name: 'unknown',\n error_message: error instanceof Error ? error.message : 'Failed to connect',\n last_check: new Date().toISOString(),\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 简单的LLM健康检查\n async llmHealthCheck(): Promise<{\n completion_model_available: boolean;\n embedding_model_available: boolean;\n timestamp: string;\n }> {\n try {\n const response = await fetch(`${this.baseUrl}/llm/health-check`);\n \n if (!response.ok) {\n throw new Error(`LLM health check failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('LLM健康检查错误:', error);\n return {\n completion_model_available: false,\n embedding_model_available: false,\n timestamp: new Date().toISOString(),\n };\n }\n }\n}\n\n// 创建默认客户端实例\nexport const cortexMemService = new CortexMemServiceClient(\n process.env.CORTEX_MEM_SERVICE_URL || 'http://localhost:3000'\n);" + "name": "delete.rs", + "source_summary": "use cortex_mem_core::memory::MemoryManager;\nuse tracing::{error, info};\n\npub struct DeleteCommand {\n memory_manager: MemoryManager,\n}\n\nimpl DeleteCommand {\n pub fn new(memory_manager: MemoryManager) -> Self {\n Self { memory_manager }\n }\n\n pub async fn execute(&self, id: String) -> Result<(), Box> {\n // First, try to get the memory to confirm it exists\n match self.memory_manager.get(&id).await {\n Ok(Some(memory)) => {\n println!(\"Found memory to delete:\");\n println!(\"ID: {}\", memory.id);\n println!(\"Content: {}\", memory.content);\n println!(\"Type: {:?}\", memory.metadata.memory_type);\n println!();\n\n // Confirm deletion\n print!(\"Are you sure you want to delete this memory? (y/N): \");\n use std::io::{self, Write};\n io::stdout().flush().unwrap();\n \n let mut input = String::new();\n io::stdin().read_line(&mut input).unwrap();\n \n if input.trim().to_lowercase() == \"y\" || input.trim().to_lowercase() == \"yes\" {\n match self.memory_manager.delete(&id).await {\n Ok(()) => {\n println!(\"✅ Memory deleted successfully!\");\n info!(\"Memory deleted: {}\", id);\n }\n Err(e) => {\n error!(\"Failed to delete memory: {}\", e);\n println!(\"❌ Failed to delete memory: {}\", e);\n return Err(e.into());\n }\n }\n } else {\n println!(\"❌ Deletion cancelled\");\n }\n }\n Ok(None) => {\n println!(\"❌ Memory with ID '{}' not found\", id);\n }\n Err(e) => {\n error!(\"Failed to retrieve memory: {}\", e);\n println!(\"❌ Failed to retrieve memory: {}\", e);\n return Err(e.into());\n }\n }\n\n Ok(())\n }\n}" }, "complexity_metrics": { - "cyclomatic_complexity": 36.0, - "lines_of_code": 643, + "cyclomatic_complexity": 4.0, + "lines_of_code": 59, "number_of_classes": 1, - "number_of_functions": 23 + "number_of_functions": 2 }, "dependencies": [ { - "dependency_type": "import", + "dependency_type": "struct", "is_external": false, "line_number": 1, - "name": "../api/types", - "path": "cortex-mem-insights/src/server/api/types", + "name": "cortex_mem_core::memory::MemoryManager", + "path": "cortex_mem_core::memory::MemoryManager", + "version": null + }, + { + "dependency_type": "crate", + "is_external": true, + "line_number": 2, + "name": "tracing", + "path": "tracing", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 13, + "name": "std::io", + "path": "std::io", "version": null } ], - "detailed_description": "该组件是一个TypeScript编写的API客户端,封装了对cortex-mem-service服务的HTTP调用。它提供了完整的记忆生命周期管理功能(CRUD)、基于语义的搜索、批量操作、系统健康检查、LLM服务状态检测以及记忆优化相关的任务管理(启动、状态查询、取消、历史记录等)。所有方法均采用异步模式并包含完整的错误处理和日志记录,返回结构化的结果对象。客户端支持可配置的基础URL,并导出一个默认实例以便在应用中全局使用。", + "detailed_description": "The DeleteCommand struct implements a CLI command that safely deletes a memory entry by first retrieving it to confirm existence and display details to the user. It then prompts for confirmation before proceeding with deletion via the MemoryManager. Successful or failed operations are logged using tracing, and appropriate feedback is printed to the console. The execute method handles three main cases: memory found (with confirmation flow), memory not found, and retrieval errors.", "interfaces": [ { - "description": "Cortex-mem-service API客户端主类", - "interface_type": "class", - "name": "CortexMemServiceClient", - "parameters": [], - "return_type": null, - "visibility": "export" + "description": "Creates a new instance of DeleteCommand with the provided MemoryManager", + "interface_type": "constructor", + "name": "DeleteCommand::new", + "parameters": [ + { + "description": "Injected dependency for memory operations", + "is_optional": false, + "name": "memory_manager", + "param_type": "MemoryManager" + } + ], + "return_type": "DeleteCommand", + "visibility": "public" + }, + { + "description": "Executes the deletion process with user confirmation and returns result", + "interface_type": "method", + "name": "DeleteCommand::execute", + "parameters": [ + { + "description": "The ID of the memory entry to delete", + "is_optional": false, + "name": "id", + "param_type": "String" + } + ], + "return_type": "Result<(), Box>", + "visibility": "public" } ], "responsibilities": [ - "提供与cortex-mem-service后端服务的HTTP通信接口", - "实现记忆数据的增删改查及搜索功能", - "管理记忆优化任务的生命周期(启动、查询、取消、清理)", - "监控和报告系统健康状态及LLM服务可用性", - "提供记忆数据的统计分析功能" + "Handle user interaction for memory deletion with confirmation prompt", + "Orchestrate the retrieval and deletion of memory entries via MemoryManager", + "Provide user feedback through console output and structured logging", + "Manage error handling and propagation for deletion operations" ] }, { "code_dossier": { "code_purpose": "command", - "description": "Implements CLI integration for cortex-mem-cli tool, providing memory management operations via command-line interface.", - "file_path": "cortex-mem-insights/src/server/integrations/cortex-mem-cli.ts", + "description": "Command-line interface component for searching memories with flexible filtering and metadata display.", + "file_path": "cortex-mem-cli/src/commands/search.rs", "functions": [ - "optimize", - "list", - "search", - "add", - "delete", - "version", - "checkAvailability" + "new", + "execute" ], "importance_score": 0.8, "interfaces": [ - "CortexMemCliClient", - "optimize", - "list", - "search" + "SearchCommand::new", + "SearchCommand::execute" ], - "name": "cortex-mem-cli.ts", - "source_summary": "import { exec } from 'child_process';\nimport { promisify } from 'util';\n\nconst execAsync = promisify(exec);\n\n// Cortex-mem-cli 集成客户端\nexport class CortexMemCliClient {\n private cliPath: string;\n \n constructor(cliPath: string = 'cortex-mem-cli') {\n this.cliPath = cliPath;\n }\n \n // 执行优化命令\n async optimize(params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n }): Promise<{\n success: boolean;\n message: string;\n data?: any;\n error?: string;\n }> {\n try {\n // 构建命令参数\n const args: string[] = ['optimize'];\n \n if (params?.memory_type) {\n args.push('--memory-type', params.memory_type);\n }\n \n if (params?.user_id) {\n args.push('--user-id', params.user_id);\n }\n \n if (params?.agent_id) {\n args.push('--agent-id', params.agent_id);\n }\n \n if (params?.run_id) {\n args.push('--run-id', params.run_id);\n }\n \n if (params?.actor_id) {\n args.push('--actor-id', params.actor_id);\n }\n \n if (params?.similarity_threshold) {\n args.push('--similarity-threshold', params.similarity_threshold.toString());\n }\n \n if (params?.dry_run) {\n args.push('--dry-run');\n }\n \n if (params?.verbose) {\n args.push('--verbose');\n }\n \n // 执行命令\n const command = `${this.cliPath} ${args.join(' ')}`;\n console.log('Executing command:', command);\n \n const { stdout, stderr } = await execAsync(command);\n \n if (stderr && !stderr.includes('warning')) {\n console.error('CLI stderr:', stderr);\n }\n \n // 解析输出\n let data: any = null;\n let message = 'Optimization completed successfully';\n \n try {\n // 尝试解析JSON输出\n const jsonMatch = stdout.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n data = JSON.parse(jsonMatch[0]);\n message = data.message || message;\n } else {\n // 如果没有JSON,使用原始输出\n data = { output: stdout.trim() };\n }\n } catch (parseError) {\n // 如果解析失败,使用原始输出\n data = { output: stdout.trim() };\n }\n \n return {\n success: true,\n message,\n data,\n };\n } catch (error) {\n console.error('Optimize command error:', error);\n \n let errorMessage = 'Failed to execute optimize command';\n if (error instanceof Error) {\n errorMessage = error.message;\n }\n \n return {\n success: false,\n message: errorMessage,\n error: errorMessage,\n };\n }\n }\n \n // 列出记忆\n async list(params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n format?: 'json' | 'table' | 'csv';\n }): Promise<{\n success: boolean;\n message: string;\n data?: any;\n error?: string;\n }> {\n try {\n const args: string[] = ['list'];\n \n if (params?.user_id) {\n args.push('--user-id', params.user_id);\n }\n \n if (params?.agent_id) {\n args.push('--agent-id', params.agent_id);\n }\n \n if (params?.run_id) {\n args.push('--run-id', params.run_id);\n }\n \n if (params?.actor_id) {\n args.push('--actor-id', params.actor_id);\n }\n \n if (params?.memory_type) {\n args.push('--memory-type', params.memory_type);\n }\n \n if (params?.limit) {\n args.push('--limit', params.limit.toString());\n }\n \n if (params?.format) {\n args.push('--format', params.format);\n } else {\n args.push('--format', 'json');\n }\n \n const command = `${this.cliPath} ${args.join(' ')}`;\n console.log('Executing command:', command);\n \n const { stdout, stderr } = await execAsync(command);\n \n if (stderr && !stderr.includes('warning')) {\n console.error('CLI stderr:', stderr);\n }\n \n let data: any = null;\n let message = 'List command completed';\n \n try {\n if (params?.format === 'json' || !params?.format) {\n data = JSON.parse(stdout);\n message = `Found ${data.total || data.length || 0} memories`;\n } else {\n data = { output: stdout.trim() };\n }\n } catch (parseError) {\n data = { output: stdout.trim() };\n }\n \n return {\n success: true,\n message,\n data,\n };\n } catch (error) {\n console.error('List command error:', error);\n \n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to execute list command',\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n \n // 搜索记忆\n async search(query: string, params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n format?: 'json' | 'table' | 'csv';\n }): Promise<{\n success: boolean;\n message: string;\n data?: any;\n error?: string;\n }> {\n try {\n const args: string[] = ['search', query];\n \n if (params?.user_id) {\n args.push('--user-id', params.user_id);\n }\n \n if (params?.agent_id) {\n args.push('--agent-id', params.agent_id);\n }\n \n if (params?.run_id) {\n args.push('--run-id', params.run_id);\n }\n \n if (params?.actor_id) {\n args.push('--actor-id', params.actor_id);\n }\n \n if (params?.memory_type) {\n args.push('--memory-type', params.memory_type);\n }\n \n if (params?.limit) {\n args.push('--limit', params.limit.toString());\n }\n \n if (params?.similarity_threshold) {\n args.push('--similarity-threshold', params.similarity_threshold.toString());\n }\n \n if (params?.format) {\n args.push('--format', params.format);\n } else {\n args.push('--format', 'json');\n }\n \n const command = `${this.cliPath} ${args.join(' ')}`;\n console.log('Executing command:', command);\n \n const { stdout, stderr } = await execAsync(command);\n \n if (stderr && !stderr.includes('warning')) {\n console.error('CLI stderr:', stderr);\n }\n \n let data: any = null;\n let message = 'Search completed';\n \n try {\n if (params?.format === 'json' || !params?.format) {\n data = JSON.parse(stdout);\n message = `Found ${data.total || data.results?.length || 0} results`;\n } else {\n data = { output: stdout.trim() };\n }\n } catch (parseError) {\n data = { output: stdout.trim() };\n }\n \n return {\n success: true,\n message,\n data,\n };\n } catch (error) {\n console.error('Search command error:', error);\n \n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to execute search command',\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n \n // 添加记忆\n async add(content: string, params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type?: string;\n }): Promise<{\n success: boolean;\n message: string;\n data?: any;\n error?: string;\n }> {\n try {\n const args: string[] = ['add', `\"${content.replace(/\"/g, '\\\\\"')}\"`];\n \n if (params?.user_id) {\n args.push('--user-id', params.user_id);\n }\n \n if (params?.agent_id) {\n args.push('--agent-id', params.agent_id);\n }\n \n if (params?.run_id) {\n args.push('--run-id', params.run_id);\n }\n \n if (params?.actor_id) {\n args.push('--actor-id', params.actor_id);\n }\n \n if (params?.role) {\n args.push('--role', params.role);\n }\n \n if (params?.memory_type) {\n args.push('--memory-type', params.memory_type);\n }\n \n const command = `${this.cliPath} ${args.join(' ')}`;\n console.log('Executing command:', command);\n \n const { stdout, stderr } = await execAsync(command);\n \n if (stderr && !stderr.includes('warning')) {\n console.error('CLI stderr:', stderr);\n }\n \n let data: any = null;\n let message = 'Memory added successfully';\n \n try {\n const jsonMatch = stdout.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n data = JSON.parse(jsonMatch[0]);\n message = data.message || message;\n } else {\n data = { output: stdout.trim() };\n }\n } catch (parseError) {\n data = { output: stdout.trim() };\n }\n \n return {\n success: true,\n message,\n data,\n };\n } catch (error) {\n console.error('Add command error:', error);\n \n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to execute add command',\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n \n // 删除记忆\n async delete(id: string): Promise<{\n success: boolean;\n message: string;\n data?: any;\n error?: string;\n }> {\n try {\n const command = `${this.cliPath} delete ${id}`;\n console.log('Executing command:', command);\n \n const { stdout, stderr } = await execAsync(command);\n \n if (stderr && !stderr.includes('warning')) {\n console.error('CLI stderr:', stderr);\n }\n \n let data: any = null;\n let message = 'Memory deleted successfully';\n \n try {\n const jsonMatch = stdout.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n data = JSON.parse(jsonMatch[0]);\n message = data.message || message;\n } else {\n data = { output: stdout.trim() };\n }\n } catch (parseError) {\n data = { output: stdout.trim() };\n }\n \n return {\n success: true,\n message,\n data,\n };\n } catch (error) {\n console.error('Delete command error:', error);\n \n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to execute delete command',\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n \n // 获取CLI版本\n async version(): Promise<{\n success: boolean;\n version?: string;\n error?: string;\n }> {\n try {\n const command = `${this.cliPath} --version`;\n const { stdout } = await execAsync(command);\n \n const versionMatch = stdout.match(/cortex-mem-cli\\s+([\\d.]+)/);\n if (versionMatch) {\n return {\n success: true,\n version: versionMatch[1],\n };\n }\n \n return {\n success: true,\n version: stdout.trim(),\n };\n } catch (error) {\n console.error('Version command error:', error);\n \n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get version',\n };\n }\n }\n \n // 检查CLI是否可用\n async checkAvailability(): Promise<{\n available: boolean;\n version?: string;\n error?: string;\n }> {\n try {\n const versionResult = await this.version();\n \n if (versionResult.success && versionResult.version) {\n return {\n available: true,\n version: versionResult.version,\n };\n }\n \n return {\n available: false,\n error: versionResult.error || 'CLI not found',\n };\n } catch (error) {\n return {\n available: false,\n error: error instanceof Error ? error.message : 'CLI check failed',\n };\n }\n }\n}\n\n// 创建默认客户端实例\nexport const cortexMemCli = new CortexMemCliClient();" + "name": "search.rs", + "source_summary": "use cortex_mem_core::{memory::MemoryManager, types::Filters};\nuse serde_json::Value;\nuse tracing::info;\n\npub struct SearchCommand {\n memory_manager: MemoryManager,\n}\n\nimpl SearchCommand {\n pub fn new(memory_manager: MemoryManager) -> Self {\n Self { memory_manager }\n }\n\n pub async fn execute(\n &self,\n query: Option,\n user_id: Option,\n agent_id: Option,\n topics: Option>,\n keywords: Option>,\n limit: usize,\n ) -> Result<(), Box> {\n let mut filters = Filters::new();\n\n if let Some(user_id) = user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = agent_id {\n filters.agent_id = Some(agent_id);\n }\n \n if let Some(topics) = topics {\n filters.topics = Some(topics);\n }\n \n if let Some(keywords) = keywords {\n filters.custom.insert(\"keywords\".to_string(), Value::Array(\n keywords.into_iter().map(Value::String).collect()\n ));\n }\n\n // 如果没有查询字符串但有元数据过滤器,使用 list 方法\n let results = if let Some(query_str) = &query {\n self.memory_manager.search(query_str, &filters, limit).await?\n } else {\n // 将 list 结果转换为 ScoredMemory 格式\n let memories = self.memory_manager.list(&filters, Some(limit)).await?;\n memories.into_iter()\n .map(|memory| cortex_mem_core::types::ScoredMemory {\n memory,\n score: 0.0, // list 操作没有相似度分数\n })\n .collect()\n };\n\n if results.is_empty() {\n if let Some(query_str) = &query {\n println!(\"🔍 No memories found for query: '{}'\", query_str);\n } else {\n println!(\"🔍 No memories found with the specified filters\");\n }\n } else {\n if let Some(query_str) = &query {\n println!(\"🔍 Found {} memories for query: '{}'\", results.len(), query_str);\n } else {\n println!(\"🔍 Found {} memories with the specified filters\", results.len());\n }\n println!();\n\n for (i, scored_memory) in results.iter().enumerate() {\n println!(\n \"{}. [Score: {:.3}] ID: {}\",\n i + 1,\n scored_memory.score,\n scored_memory.memory.id\n );\n println!(\" Content: {}\", scored_memory.memory.content);\n println!(\" Type: {:?}\", scored_memory.memory.metadata.memory_type);\n println!(\n \" Created: {}\",\n scored_memory.memory.created_at.format(\"%Y-%m-%d %H:%M:%S\")\n );\n\n if let Some(user_id) = &scored_memory.memory.metadata.user_id {\n println!(\" User: {}\", user_id);\n }\n\n if let Some(agent_id) = &scored_memory.memory.metadata.agent_id {\n println!(\" Agent: {}\", agent_id);\n }\n \n // Display topics\n if !scored_memory.memory.metadata.topics.is_empty() {\n println!(\" Topics: {}\", scored_memory.memory.metadata.topics.join(\", \"));\n }\n \n // Display keywords from custom metadata\n if let Some(keywords) = scored_memory.memory.metadata.custom.get(\"keywords\") {\n if let Some(keywords_array) = keywords.as_array() {\n let keyword_strings: Vec = keywords_array\n .iter()\n .filter_map(|k| k.as_str())\n .map(|s| s.to_string())\n .collect();\n if !keyword_strings.is_empty() {\n println!(\" Keywords: {}\", keyword_strings.join(\", \"));\n }\n }\n }\n\n println!();\n }\n }\n\n info!(\"Search completed: {} results found\", results.len());\n\n Ok(())\n }\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 43.0, - "lines_of_code": 486, + "cyclomatic_complexity": 18.0, + "lines_of_code": 120, "number_of_classes": 1, - "number_of_functions": 8 + "number_of_functions": 2 }, "dependencies": [ { - "dependency_type": "builtin", - "is_external": false, + "dependency_type": "library", + "is_external": true, "line_number": 1, - "name": "child_process", - "path": "child_process", + "name": "cortex_mem_core", + "path": "cortex_mem_core::{memory::MemoryManager, types::Filters}", "version": null }, { - "dependency_type": "builtin", - "is_external": false, + "dependency_type": "library", + "is_external": true, "line_number": 2, - "name": "util", - "path": "util", + "name": "serde_json", + "path": "serde_json::Value", + "version": null + }, + { + "dependency_type": "library", + "is_external": true, + "line_number": 3, + "name": "tracing", + "path": "tracing::info", "version": null } ], - "detailed_description": "This component serves as a Node.js integration client for the 'cortex-mem-cli' command-line tool, enabling programmatic interaction with a memory management system. It exports a CortexMemCliClient class that wraps CLI commands for various operations including optimizing, listing, searching, adding, and deleting memory entries. The client uses child_process.exec to invoke the CLI and parses the output, supporting structured (JSON) responses and fallback to raw output. It provides error handling with detailed logging, parameter validation through TypeScript types, and output formatting options. The component also includes utility methods to check CLI availability and retrieve version information. All commands support optional parameters for filtering by user, agent, run, actor IDs, memory types, and other criteria. The implementation handles string escaping for content insertion and supports dry-run and verbose modes for safe execution and debugging.", + "detailed_description": "This component implements a CLI command to search for memory entries using either a semantic query or metadata-based filters. It supports filtering by user_id, agent_id, topics, and keywords. When a query is provided, it performs a semantic search; otherwise, it lists memories matching the metadata filters. Results are formatted and printed to stdout with detailed metadata including score, content, type, timestamps, and custom keywords. The component integrates with a MemoryManager for backend operations and uses structured logging via tracing.", "interfaces": [ { - "description": "Main client class for interacting with cortex-mem-cli", - "interface_type": "class", - "name": "CortexMemCliClient", + "description": "Creates a new SearchCommand instance with injected MemoryManager", + "interface_type": "constructor", + "name": "SearchCommand::new", "parameters": [ { - "description": "Path to the cortex-mem-cli executable", - "is_optional": true, - "name": "cliPath", - "param_type": "string" + "description": "Dependency-injected memory manager for backend operations", + "is_optional": false, + "name": "memory_manager", + "param_type": "MemoryManager" } ], - "return_type": null, + "return_type": "SearchCommand", "visibility": "public" }, { - "description": "Execute memory optimization command", + "description": "Executes the search operation with provided filters and displays results", "interface_type": "method", - "name": "optimize", + "name": "SearchCommand::execute", "parameters": [ { - "description": "Optional parameters including memory_type, user_id, agent_id, run_id, actor_id, similarity_threshold, dry_run, verbose", + "description": "Optional semantic search query string", "is_optional": true, - "name": "params", - "param_type": "object" - } - ], - "return_type": "Promise<{success: boolean, message: string, data?: any, error?: string}>", - "visibility": "public" - }, - { - "description": "List memories with optional filtering", - "interface_type": "method", - "name": "list", - "parameters": [ + "name": "query", + "param_type": "Option" + }, { - "description": "Optional parameters including user_id, agent_id, run_id, actor_id, memory_type, limit, format", + "description": "Optional user identifier filter", "is_optional": true, - "name": "params", - "param_type": "object" - } - ], - "return_type": "Promise<{success: boolean, message: string, data?: any, error?: string}>", - "visibility": "public" - }, - { - "description": "Search memories by query", - "interface_type": "method", - "name": "search", - "parameters": [ + "name": "user_id", + "param_type": "Option" + }, { - "description": "Search query string", - "is_optional": false, - "name": "query", - "param_type": "string" + "description": "Optional agent identifier filter", + "is_optional": true, + "name": "agent_id", + "param_type": "Option" }, { - "description": "Optional search parameters including user_id, agent_id, run_id, actor_id, memory_type, limit, similarity_threshold, format", + "description": "Optional list of topic filters", "is_optional": true, - "name": "params", - "param_type": "object" + "name": "topics", + "param_type": "Option>" + }, + { + "description": "Optional list of keyword filters", + "is_optional": true, + "name": "keywords", + "param_type": "Option>" + }, + { + "description": "Maximum number of results to return", + "is_optional": false, + "name": "limit", + "param_type": "usize" } ], - "return_type": "Promise<{success: boolean, message: string, data?: any, error?: string}>", + "return_type": "Result<(), Box>", "visibility": "public" } ], "responsibilities": [ - "Execute cortex-mem-cli commands through programmatic interface", - "Manage memory lifecycle operations (add, list, search, delete, optimize)", - "Parse and normalize CLI command output into structured responses", - "Handle error conditions and provide meaningful feedback", - "Validate and sanitize command parameters before execution" + "Orchestrate memory search operations using MemoryManager based on user input", + "Parse and apply multiple filter criteria (user, agent, topics, keywords) to queries", + "Format and display search results in a human-readable CLI output format", + "Handle both semantic search and metadata listing operations conditionally", + "Log operational events and results for observability" ] }, { "code_dossier": { - "code_purpose": "types", - "description": "Defines comprehensive type interfaces for API responses, memory management, system status, search, pagination, filtering, batch operations, and export functionality.", - "file_path": "cortex-mem-insights/src/server/api/types.ts", - "functions": [], + "code_purpose": "service", + "description": "Service for handling MCP tool calls related to memory management, implementing store, query, list, and get operations for AI agent memories via defined tool interfaces.", + "file_path": "cortex-mem-mcp/src/lib.rs", + "functions": [ + "new", + "with_config_path", + "with_config_path_and_agent", + "store_memory", + "query_memory", + "list_memories", + "get_memory", + "find_default_config_path", + "tools_error_to_mcp_error" + ], "importance_score": 0.8, "interfaces": [ - "ApiResponse", - "MemoryMetadataResponse", - "MemoryResponse", - "ScoredMemoryResponse", - "ListResponse", - "SearchResponse", - "HealthResponse", - "OptimizationRequest", - "OptimizationResult", - "OptimizationHistory", - "SystemStatus", - "PerformanceMetrics", - "SystemInfo", - "LogEntry", - "Statistics", - "PaginationParams", - "FilterParams", - "SearchParams", - "CreateMemoryRequest", - "UpdateMemoryRequest", - "BatchOperationRequest", - "BatchOperationResponse", - "ExportFormat", - "ExportResponse" + "ServerHandler::get_info", + "ServerHandler::list_tools", + "ServerHandler::call_tool" ], - "name": "types.ts", - "source_summary": "// API 响应类型\nexport interface ApiResponse {\n success: boolean;\n data?: T;\n error?: string;\n message?: string;\n timestamp: string;\n}\n\n// 记忆相关类型\nexport interface MemoryMetadataResponse {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type: string;\n hash: string;\n custom?: Record;\n}\n\nexport interface MemoryResponse {\n id: string;\n content: string;\n metadata: MemoryMetadataResponse;\n created_at: string;\n updated_at: string;\n}\n\nexport interface ScoredMemoryResponse {\n memory: MemoryResponse;\n score: number;\n}\n\n// 列表响应\nexport interface ListResponse {\n total: number;\n memories: MemoryResponse[];\n}\n\n// 搜索响应\nexport interface SearchResponse {\n total: number;\n results: ScoredMemoryResponse[];\n}\n\n// 健康检查响应\nexport interface HealthResponse {\n status: string;\n vector_store: boolean;\n llm_service: boolean;\n timestamp: string;\n}\n\n// 优化相关类型\nexport interface OptimizationRequest {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n}\n\nexport interface OptimizationResult {\n job_id: string;\n status: 'pending' | 'running' | 'completed' | 'failed';\n total_memories: number;\n processed_memories: number;\n deduplicated: number;\n merged: number;\n enhanced: number;\n errors: number;\n start_time: string;\n end_time?: string;\n duration?: number;\n message?: string;\n}\n\nexport interface OptimizationHistory {\n job_id: string;\n status: string;\n total_memories: number;\n processed_memories: number;\n start_time: string;\n end_time?: string;\n duration?: number;\n}\n\n// 系统相关类型\nexport interface SystemStatus {\n status: 'healthy' | 'unhealthy';\n vector_store: boolean;\n llm_service: boolean;\n timestamp: string;\n}\n\nexport interface PerformanceMetrics {\n cpu_usage: number;\n memory_usage: number;\n disk_usage: number;\n active_connections: number;\n request_count: number;\n error_rate: number;\n response_time_avg: number;\n timestamp: string;\n}\n\nexport interface SystemInfo {\n version: string;\n uptime: string;\n platform: string;\n arch: string;\n node_version: string;\n memory_total: number;\n memory_used: number;\n cpu_count: number;\n hostname: string;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: 'info' | 'warn' | 'error' | 'debug';\n message: string;\n source: string;\n metadata?: Record;\n}\n\n// 统计类型\nexport interface Statistics {\n total_memories: number;\n by_type: Record;\n by_user: Record;\n by_agent: Record;\n recent_activity: Array<{ date: string; count: number }>;\n}\n\n// 分页参数\nexport interface PaginationParams {\n page?: number;\n limit?: number;\n sort_by?: string;\n sort_order?: 'asc' | 'desc';\n}\n\n// 过滤参数\nexport interface FilterParams {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n start_date?: string;\n end_date?: string;\n min_score?: number;\n max_score?: number;\n}\n\n// 搜索参数\nexport interface SearchParams extends FilterParams {\n query: string;\n limit?: number;\n similarity_threshold?: number;\n}\n\n// 创建记忆请求\nexport interface CreateMemoryRequest {\n content: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type?: string;\n custom?: Record;\n}\n\n// 更新记忆请求\nexport interface UpdateMemoryRequest {\n content: string;\n}\n\n// 批量操作请求\nexport interface BatchOperationRequest {\n ids: string[];\n operation: 'delete' | 'export' | 'tag';\n tags?: string[];\n}\n\n// 批量操作响应\nexport interface BatchOperationResponse {\n success: boolean;\n message: string;\n total: number;\n succeeded: number;\n failed: number;\n failed_ids?: string[];\n}\n\n// 导出格式\nexport interface ExportFormat {\n format: 'json' | 'csv' | 'txt';\n include_metadata?: boolean;\n include_scores?: boolean;\n compress?: boolean;\n}\n\n// 导出响应\nexport interface ExportResponse {\n success: boolean;\n download_url?: string;\n file_size?: number;\n format: string;\n item_count: number;\n message?: string;\n}" + "name": "lib.rs", + "source_summary": "use anyhow::Result;\nuse cortex_mem_config::Config;\nuse cortex_mem_core::{\n init::initialize_memory_system,\n memory::MemoryManager,\n};\nuse cortex_mem_tools::{MemoryOperations, MemoryToolsError, map_mcp_arguments_to_payload, tools_error_to_mcp_error_code, get_tool_error_message, get_mcp_tool_definitions};\nuse rmcp::{\n model::{\n CallToolRequestParam, CallToolResult, Content, ErrorData, ListToolsResult,\n PaginatedRequestParam, ServerCapabilities, ServerInfo, Tool,\n },\n service::RequestContext,\n RoleServer, ServerHandler,\n};\nuse serde_json::Map;\nuse std::path::{Path, PathBuf};\nuse std::sync::Arc;\nuse tracing::{error, info};\n\n/// Service for handling MCP tool calls related to memory management\npub struct MemoryMcpService {\n memory_manager: Arc,\n operations: MemoryOperations,\n agent_id: Option,\n}\n\nimpl MemoryMcpService {\n /// Create a new memory MCP service with default config path\n pub async fn new() -> Result {\n // Try to find config.toml in standard locations\n let config_path = Self::find_default_config_path()\n .unwrap_or_else(|| Path::new(\"config.toml\").to_path_buf());\n Self::with_config_path(config_path).await\n }\n\n /// Create a new memory MCP service with specific config path\n pub async fn with_config_path + Clone + std::fmt::Debug>(\n path: P,\n ) -> Result {\n Self::with_config_path_and_agent(path, None).await\n }\n\n /// Create a new memory MCP service with specific config path and agent\n pub async fn with_config_path_and_agent + Clone + std::fmt::Debug>(\n path: P,\n agent_id: Option,\n ) -> Result {\n // Load configuration from specified path\n let config = Config::load(path.clone())?;\n info!(\"Loaded configuration from: {:?}\", path);\n\n // Initialize vector store and LLM client\n let (vector_store, llm_client) = initialize_memory_system(&config).await?;\n info!(\"Initialized vector store and LLM client\");\n\n // Create memory manager\n let memory_manager = Arc::new(MemoryManager::new(\n vector_store,\n llm_client,\n config.memory.clone(),\n ));\n info!(\"Created memory manager\");\n\n // Create operations handler\n let operations = MemoryOperations::new(\n memory_manager.clone(),\n None, // Default user ID will be derived from agent ID\n agent_id.clone(),\n 100, // Default limit\n );\n\n Ok(Self {\n memory_manager,\n operations,\n agent_id,\n })\n }\n\n /// Tool implementation for storing a memory\n async fn store_memory(\n &self,\n arguments: &Map,\n ) -> Result {\n let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id);\n\n match self.operations.store_memory(payload).await {\n Ok(response) => {\n Ok(CallToolResult::success(vec![Content::text(\n serde_json::to_string_pretty(&response).unwrap(),\n )]))\n }\n Err(e) => {\n error!(\"Failed to store memory: {}\", e);\n Err(self.tools_error_to_mcp_error(e))\n }\n }\n }\n\n /// Tool implementation for querying memories\n async fn query_memory(\n &self,\n arguments: &Map,\n ) -> Result {\n let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id);\n\n match self.operations.query_memory(payload).await {\n Ok(response) => {\n Ok(CallToolResult::success(vec![Content::text(\n serde_json::to_string_pretty(&response).unwrap(),\n )]))\n }\n Err(e) => {\n error!(\"Failed to query memories: {}\", e);\n Err(self.tools_error_to_mcp_error(e))\n }\n }\n }\n\n /// Tool implementation for listing memories\n async fn list_memories(\n &self,\n arguments: &Map,\n ) -> Result {\n let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id);\n\n match self.operations.list_memories(payload).await {\n Ok(response) => {\n Ok(CallToolResult::success(vec![Content::text(\n serde_json::to_string_pretty(&response).unwrap(),\n )]))\n }\n Err(e) => {\n error!(\"Failed to list memories: {}\", e);\n Err(self.tools_error_to_mcp_error(e))\n }\n }\n }\n\n /// Tool implementation for getting a specific memory by ID\n async fn get_memory(\n &self,\n arguments: &Map,\n ) -> Result {\n let payload = map_mcp_arguments_to_payload(arguments, &self.agent_id);\n\n match self.operations.get_memory(payload).await {\n Ok(response) => {\n Ok(CallToolResult::success(vec![Content::text(\n serde_json::to_string_pretty(&response).unwrap(),\n )]))\n }\n Err(e) => {\n error!(\"Failed to get memory: {}\", e);\n Err(self.tools_error_to_mcp_error(e))\n }\n }\n }\n\n /// Find default configuration file path\n fn find_default_config_path() -> Option {\n // Try current directory first\n if let Ok(current_dir) = std::env::current_dir() {\n let current_config = current_dir.join(\"config.toml\");\n if current_config.exists() {\n return Some(current_config);\n }\n }\n\n // Try user home directory\n if let Some(home_dir) = dirs::home_dir() {\n let user_config = home_dir.join(\".config\").join(\"memo\").join(\"config.toml\");\n if user_config.exists() {\n return Some(user_config);\n }\n }\n\n // Try system config directory (platform-specific)\n #[cfg(target_os = \"macos\")]\n let system_config = Path::new(\"/usr/local/etc/memo/config.toml\");\n\n #[cfg(target_os = \"linux\")]\n let system_config = Path::new(\"/etc/memo/config.toml\");\n\n #[cfg(target_os = \"windows\")]\n let system_config = Path::new(\"C:\\\\ProgramData\\\\memo\\\\config.toml\");\n\n if system_config.exists() {\n return Some(system_config.to_path_buf());\n }\n\n None\n }\n\n /// Helper function to convert MemoryToolsError to MCP ErrorData\n fn tools_error_to_mcp_error(&self, error: MemoryToolsError) -> ErrorData {\n ErrorData {\n code: rmcp::model::ErrorCode(tools_error_to_mcp_error_code(&error)).into(),\n message: get_tool_error_message(&error).into(),\n data: None,\n }\n }\n}\n\nimpl ServerHandler for MemoryMcpService {\n fn get_info(&self) -> ServerInfo {\n ServerInfo {\n protocol_version: rmcp::model::ProtocolVersion::V_2024_11_05,\n capabilities: ServerCapabilities::builder().enable_tools().build(),\n server_info: rmcp::model::Implementation::from_build_env(),\n instructions: Some(\n \"A memory management system for AI agents. Store, search, and retrieve memories using natural language queries. Supports different types of memories including conversational, procedural, and factual memories.\"\n .to_string(),\n ),\n }\n }\n\n fn list_tools(\n &self,\n _request: Option,\n _context: RequestContext,\n ) -> impl std::future::Future> + Send + '_ {\n async move {\n let tool_definitions = get_mcp_tool_definitions();\n let tools: Vec = tool_definitions.into_iter().map(|def| {\n Tool {\n name: def.name.into(),\n title: def.title.map(|t| t.into()),\n description: def.description.map(|d| d.into()),\n input_schema: def.input_schema.as_object().unwrap().clone().into(),\n output_schema: def.output_schema.map(|schema| schema.as_object().unwrap().clone().into()),\n annotations: None,\n icons: None,\n meta: None,\n }\n }).collect();\n\n Ok(ListToolsResult {\n tools,\n next_cursor: None,\n })\n }\n }\n\n fn call_tool(\n &self,\n request: CallToolRequestParam,\n _context: RequestContext,\n ) -> impl std::future::Future> + Send + '_ {\n async move {\n let tool_name = &request.name;\n\n match tool_name.as_ref() {\n \"store_memory\" => {\n if let Some(arguments) = &request.arguments {\n self.store_memory(arguments).await\n } else {\n Err(ErrorData {\n code: rmcp::model::ErrorCode(-32602).into(),\n message: \"Missing arguments\".into(),\n data: None,\n })\n }\n }\n \"query_memory\" => {\n if let Some(arguments) = &request.arguments {\n self.query_memory(arguments).await\n } else {\n Err(ErrorData {\n code: rmcp::model::ErrorCode(-32602).into(),\n message: \"Missing arguments\".into(),\n data: None,\n })\n }\n }\n \"list_memories\" => {\n if let Some(arguments) = &request.arguments {\n self.list_memories(arguments).await\n } else {\n Err(ErrorData {\n code: rmcp::model::ErrorCode(-32602).into(),\n message: \"Missing arguments\".into(),\n data: None,\n })\n }\n }\n \"get_memory\" => {\n if let Some(arguments) = &request.arguments {\n self.get_memory(arguments).await\n } else {\n Err(ErrorData {\n code: rmcp::model::ErrorCode(-32602).into(),\n message:\n \"Missing arguments. You must provide 'memory_id' for this tool.\"\n .into(),\n data: None,\n })\n }\n }\n _ => Err(ErrorData {\n code: rmcp::model::ErrorCode(-32601).into(),\n message: format!(\"Unknown tool: {}\", tool_name).into(),\n data: None,\n }),\n }\n }\n }\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 218, - "number_of_classes": 0, - "number_of_functions": 0 + "cyclomatic_complexity": 23.0, + "lines_of_code": 308, + "number_of_classes": 1, + "number_of_functions": 13 }, - "dependencies": [], - "detailed_description": "This file serves as the central type definition hub for the server-side API layer of a memory insights system. It defines structured interfaces for all major data payloads including memory records, API responses, search and filter parameters, optimization jobs, system monitoring metrics, and batch operations. The types support both internal consistency and external contract clarity between client and server. Notably, it includes rich metadata handling for memory entries, comprehensive status and performance monitoring models, and flexible parameter structures for querying and manipulation operations.", - "interfaces": [ + "dependencies": [ { - "description": "Generic API response wrapper with success flag, optional data/error, and timestamp", - "interface_type": "interface", - "name": "ApiResponse", - "parameters": [], - "return_type": "T", - "visibility": "export" - }, - { - "description": "Represents a memory entry with content, metadata, and timestamps", - "interface_type": "interface", - "name": "MemoryResponse", - "parameters": [], - "return_type": null, - "visibility": "export" - }, - { - "description": "Structured response for memory search operations including scored results", - "interface_type": "interface", - "name": "SearchResponse", - "parameters": [], - "return_type": null, - "visibility": "export" + "dependency_type": "use", + "is_external": true, + "line_number": 1, + "name": "anyhow", + "path": null, + "version": null }, { - "description": "Request payload for memory optimization jobs with filtering and execution options", - "interface_type": "interface", - "name": "OptimizationRequest", - "parameters": [], - "return_type": null, - "visibility": "export" + "dependency_type": "use", + "is_external": false, + "line_number": 2, + "name": "cortex_mem_config", + "path": null, + "version": null }, { - "description": "System health status with component-level availability reporting", - "interface_type": "interface", - "name": "SystemStatus", - "parameters": [], - "return_type": null, - "visibility": "export" + "dependency_type": "use", + "is_external": false, + "line_number": 3, + "name": "cortex_mem_core", + "path": null, + "version": null }, { - "description": "Request structure for creating new memory entries with optional metadata", - "interface_type": "interface", - "name": "CreateMemoryRequest", - "parameters": [], - "return_type": null, - "visibility": "export" + "dependency_type": "use", + "is_external": false, + "line_number": 6, + "name": "cortex_mem_tools", + "path": null, + "version": null }, { - "description": "Search parameters extending filter criteria with query text and similarity threshold", - "interface_type": "interface", - "name": "SearchParams", - "parameters": [], - "return_type": null, - "visibility": "export" - } - ], - "responsibilities": [ - "Define standardized API response structure with success/error handling", - "Model memory data structures with metadata and scoring capabilities", - "Provide parameter interfaces for filtering, searching, and pagination", - "Define system monitoring and health check response formats", - "Support batch operations and data export functionality through structured request/response types" - ] - }, - { - "code_dossier": { - "code_purpose": "api", - "description": "API routes for memory optimization operations including optimization execution, history retrieval, status checks, and cleanup.", - "file_path": "cortex-mem-insights/src/server/api/optimization.ts", - "functions": [ - "optimize", - "getOptimizationHistory", - "getOptimizationStatistics", - "analyzeOptimization", - "getOptimizationStatus", - "cancelOptimization", - "cleanupOptimizationHistory" - ], - "importance_score": 0.8, - "interfaces": [ - "OptimizationRequest" - ], - "name": "optimization.ts", - "source_summary": "import { Elysia, t } from 'elysia';\nimport { cortexMemService } from '../integrations/cortex-mem';\n\n// 类型定义\ninterface OptimizationRequest {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n strategy?: string;\n aggressive?: boolean;\n timeout_minutes?: number;\n}\n\n\n\n// 优化API路由\nexport const optimizationRoutes = new Elysia({ prefix: '/api/optimization' })\n // 启动优化\n .post('/', async ({ body }) => {\n try {\n // 直接调用cortex-mem-service的API\n const result = await cortexMemService.optimize(body);\n return result;\n } catch (error) {\n console.error('启动优化失败:', error);\n return {\n success: false,\n error: {\n code: 'OPTIMIZE_FAILED',\n message: error instanceof Error ? error.message : '启动优化失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n body: t.Object({\n memory_type: t.Optional(t.String()),\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n similarity_threshold: t.Optional(t.Number({ default: 0.7 })),\n dry_run: t.Optional(t.Boolean({ default: false })),\n verbose: t.Optional(t.Boolean({ default: false })),\n strategy: t.Optional(t.String()),\n aggressive: t.Optional(t.Boolean({ default: false })),\n timeout_minutes: t.Optional(t.Number()),\n })\n })\n \n // 获取优化历史 - 必须在 /:jobId 之前定义,避免 \"history\" 被当作 jobId\n .get('/history', async ({ query }) => {\n try {\n const result = await cortexMemService.getOptimizationHistory({\n limit: query.limit ? parseInt(query.limit) : 20,\n offset: query.offset ? parseInt(query.offset) : 0,\n status: query.status,\n start_date: query.start_date,\n end_date: query.end_date,\n });\n return result;\n } catch (error) {\n console.error('获取优化历史失败:', error);\n return {\n success: false,\n error: {\n code: 'GET_HISTORY_FAILED',\n message: error instanceof Error ? error.message : '获取历史失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n query: t.Object({\n limit: t.Optional(t.String()),\n offset: t.Optional(t.String()),\n status: t.Optional(t.String()),\n start_date: t.Optional(t.String()),\n end_date: t.Optional(t.String()),\n })\n })\n \n // 获取优化统计 - 也要在 /:jobId 之前\n .get('/statistics', async () => {\n try {\n const result = await cortexMemService.getOptimizationStatistics();\n return result;\n } catch (error) {\n console.error('获取优化统计失败:', error);\n return {\n success: false,\n error: {\n code: 'GET_STATISTICS_FAILED',\n message: error instanceof Error ? error.message : '获取统计失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n })\n \n // 分析优化问题(预览)- 也要在 /:jobId 之前\n .post('/analyze', async ({ body }) => {\n try {\n const result = await cortexMemService.analyzeOptimization(body);\n return result;\n } catch (error) {\n console.error('分析优化问题失败:', error);\n return {\n success: false,\n error: {\n code: 'ANALYZE_FAILED',\n message: error instanceof Error ? error.message : '分析失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n body: t.Object({\n memory_type: t.Optional(t.String()),\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n similarity_threshold: t.Optional(t.Number({ default: 0.7 })),\n })\n })\n \n // 获取优化状态 - 动态路由必须放在静态路由之后\n .get('/:jobId', async ({ params }) => {\n try {\n const result = await cortexMemService.getOptimizationStatus(params.jobId);\n return result;\n } catch (error) {\n console.error('获取优化状态失败:', error);\n return {\n success: false,\n error: {\n code: 'GET_STATUS_FAILED',\n message: error instanceof Error ? error.message : '获取状态失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n params: t.Object({\n jobId: t.String()\n })\n })\n \n // 取消优化\n .post('/:jobId/cancel', async ({ params }) => {\n try {\n const result = await cortexMemService.cancelOptimization(params.jobId);\n return result;\n } catch (error) {\n console.error('取消优化失败:', error);\n return {\n success: false,\n error: {\n code: 'CANCEL_FAILED',\n message: error instanceof Error ? error.message : '取消优化失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n params: t.Object({\n jobId: t.String()\n })\n })\n \n // 清理旧的历史记录 - 也要在 /:jobId 之前\n .post('/cleanup', async ({ body }) => {\n try {\n const result = await cortexMemService.cleanupOptimizationHistory(body.max_age_days);\n return result;\n } catch (error) {\n console.error('清理历史记录失败:', error);\n return {\n success: false,\n error: {\n code: 'CLEANUP_FAILED',\n message: error instanceof Error ? error.message : '清理失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n body: t.Object({\n max_age_days: t.Number({ default: 7 })\n })\n });" - }, - "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 197, - "number_of_classes": 0, - "number_of_functions": 6 - }, - "dependencies": [ - { - "dependency_type": "module", + "dependency_type": "use", "is_external": true, - "line_number": 1, - "name": "elysia", + "line_number": 9, + "name": "rmcp", "path": null, "version": null }, { - "dependency_type": "module", + "dependency_type": "use", "is_external": true, - "line_number": 1, - "name": "t", + "line_number": 16, + "name": "serde_json", "path": null, "version": null }, { - "dependency_type": "service", - "is_external": false, - "line_number": 2, - "name": "cortexMemService", - "path": "../integrations/cortex-mem", + "dependency_type": "use", + "is_external": true, + "line_number": 18, + "name": "tracing", + "path": null, "version": null } ], - "detailed_description": "This component defines a set of API endpoints using the Elysia framework to manage memory optimization tasks. It provides functionality to start an optimization job, analyze potential issues, retrieve historical records and statistics, check the status of a specific job, cancel running jobs, and clean up old history. All logic is delegated to an external service `cortexMemService`, making this a thin API layer. The routes are carefully ordered to prevent dynamic segments like `/:jobId` from shadowing static ones such as `/history`. Input validation is implemented via Elysia's type system (`t.Object`, etc.), ensuring robust handling of optional parameters with defaults. Error handling is centralized with structured error responses containing codes and timestamps.", + "detailed_description": "This component implements a service layer that bridges MCP (Memory Control Protocol) tool invocation requests with the underlying memory management system. It provides structured tools for AI agents to persist, retrieve, and query memories using natural language. The service initializes the memory system (vector store and LLM client) from configuration, manages a MemoryManager instance, and routes incoming tool calls (e.g., store_memory, query_memory) to the appropriate operations via MemoryOperations. It handles error translation from domain-specific MemoryToolsError to standardized MCP ErrorData, ensuring interoperability. Configuration is loaded from multiple fallback paths (current directory, home directory, system paths), supporting flexible deployment. The component adheres to the ServerHandler trait, exposing capabilities like tool listing and execution within the MCP ecosystem.", "interfaces": [ { - "description": "Defines optional input parameters for optimization operations including memory type, user/agent/run/actor IDs, similarity threshold, dry-run mode, verbosity, strategy, aggressiveness, and timeout.", - "interface_type": "interface", - "name": "OptimizationRequest", + "description": "Creates a new service instance using default config path", + "interface_type": "function", + "name": "new", + "parameters": [], + "return_type": "Result", + "visibility": "public" + }, + { + "description": "Creates a new service instance with specified config path", + "interface_type": "function", + "name": "with_config_path", "parameters": [ { - "description": null, - "is_optional": true, - "name": "memory_type", - "param_type": "string" - }, + "description": "Path to configuration file", + "is_optional": false, + "name": "path", + "param_type": "P" + } + ], + "return_type": "Result", + "visibility": "public" + }, + { + "description": "Creates a new service with config path and optional agent context", + "interface_type": "function", + "name": "with_config_path_and_agent", + "parameters": [ { - "description": null, - "is_optional": true, - "name": "user_id", - "param_type": "string" + "description": "Path to configuration file", + "is_optional": false, + "name": "path", + "param_type": "P" }, { - "description": null, + "description": "Optional agent identifier", "is_optional": true, "name": "agent_id", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "run_id", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "actor_id", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "similarity_threshold", - "param_type": "number" - }, - { - "description": null, - "is_optional": true, - "name": "dry_run", - "param_type": "boolean" - }, - { - "description": null, - "is_optional": true, - "name": "verbose", - "param_type": "boolean" - }, + "param_type": "Option" + } + ], + "return_type": "Result", + "visibility": "public" + }, + { + "description": "Handles storing a new memory using provided arguments", + "interface_type": "function", + "name": "store_memory", + "parameters": [ { - "description": null, - "is_optional": true, - "name": "strategy", - "param_type": "string" - }, + "description": "Tool call arguments", + "is_optional": false, + "name": "arguments", + "param_type": "&Map" + } + ], + "return_type": "Result", + "visibility": "private" + }, + { + "description": "Handles querying memories using natural language or filters", + "interface_type": "function", + "name": "query_memory", + "parameters": [ { - "description": null, - "is_optional": true, - "name": "aggressive", - "param_type": "boolean" - }, + "description": "Tool call arguments", + "is_optional": false, + "name": "arguments", + "param_type": "&Map" + } + ], + "return_type": "Result", + "visibility": "private" + }, + { + "description": "Handles listing memories with optional pagination and filtering", + "interface_type": "function", + "name": "list_memories", + "parameters": [ { - "description": null, - "is_optional": true, - "name": "timeout_minutes", - "param_type": "number" + "description": "Tool call arguments", + "is_optional": false, + "name": "arguments", + "param_type": "&Map" } ], - "return_type": null, + "return_type": "Result", "visibility": "private" - } - ], - "responsibilities": [ - "Expose RESTful API endpoints for initiating and managing memory optimization processes", - "Validate incoming request payloads and query parameters using type-safe schemas", - "Delegate business logic execution to the cortex-mem-service integration layer", - "Provide structured error responses with appropriate error codes and messages", - "Ensure correct routing precedence by placing static paths before dynamic ones" - ] - }, - { - "code_dossier": { - "code_purpose": "api", - "description": "Provides RESTful API endpoints for managing and retrieving memory data through integration with cortexMemService. Supports CRUD operations, search, batch processing, and statistical analysis of memory records.", - "file_path": "cortex-mem-insights/src/server/api/memory.ts", - "functions": [ - "GET /", - "POST /search", - "GET /:id", - "POST /", - "PUT /:id", - "DELETE /:id", - "GET /stats/summary", - "GET /stats/types", - "POST /batch/delete" - ], - "importance_score": 0.8, - "interfaces": [ - "MemoryResponse", - "ListResponse", - "SearchRequest", - "SearchResponse" - ], - "name": "memory.ts", - "source_summary": "import { Elysia, t } from 'elysia';\nimport { cortexMemService } from '../integrations/cortex-mem';\n\n// 类型定义\ninterface MemoryResponse {\n id: string;\n content: string;\n metadata: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type: string;\n hash: string;\n custom: Record;\n };\n created_at: string;\n updated_at: string;\n}\n\ninterface ListResponse {\n total: number;\n memories: MemoryResponse[];\n}\n\ninterface SearchRequest {\n query: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n}\n\ninterface SearchResponse {\n total: number;\n results: Array<{\n memory: MemoryResponse;\n score: number;\n }>;\n}\n\n// 内存API路由\nexport const memoryRoutes = new Elysia({ prefix: '/api/memories' })\n // 获取记忆列表\n .get('/', async ({ query }) => {\n try {\n const response = await cortexMemService.listMemories({\n user_id: query.user_id,\n agent_id: query.agent_id,\n run_id: query.run_id,\n actor_id: query.actor_id,\n memory_type: query.memory_type,\n limit: query.limit ? parseInt(query.limit) : undefined\n });\n \n return {\n total: response.total,\n memories: response.memories\n };\n } catch (error) {\n console.error('获取记忆列表失败:', error);\n throw error;\n }\n }, {\n query: t.Object({\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n memory_type: t.Optional(t.String()),\n limit: t.Optional(t.String())\n })\n })\n \n // 搜索记忆\n .post('/search', async ({ body }) => {\n try {\n const { query, ...params } = body;\n const response = await cortexMemService.searchMemories(query, params);\n return response;\n } catch (error) {\n console.error('搜索记忆失败:', error);\n throw error;\n }\n }, {\n body: t.Object({\n query: t.String(),\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n memory_type: t.Optional(t.String()),\n limit: t.Optional(t.Number()),\n similarity_threshold: t.Optional(t.Number())\n })\n })\n \n // 获取单个记忆\n .get('/:id', async ({ params }) => {\n try {\n const memory = await cortexMemService.getMemory(params.id);\n return memory;\n } catch (error) {\n console.error(`获取记忆 ${params.id} 失败:`, error);\n throw error;\n }\n }, {\n params: t.Object({\n id: t.String()\n })\n })\n \n // 创建记忆\n .post('/', async ({ body }) => {\n try {\n const response = await cortexMemService.createMemory(body);\n return response;\n } catch (error) {\n console.error('创建记忆失败:', error);\n throw error;\n }\n }, {\n body: t.Object({\n content: t.String(),\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n role: t.Optional(t.String()),\n memory_type: t.Optional(t.String()),\n custom: t.Optional(t.Record(t.String(), t.Any()))\n })\n })\n \n // 更新记忆\n .put('/:id', async ({ params, body }) => {\n try {\n const response = await cortexMemService.updateMemory(params.id, body.content);\n return response;\n } catch (error) {\n console.error(`更新记忆 ${params.id} 失败:`, error);\n throw error;\n }\n }, {\n params: t.Object({\n id: t.String()\n }),\n body: t.Object({\n content: t.String()\n })\n })\n \n // 删除记忆\n .delete('/:id', async ({ params }) => {\n try {\n const response = await cortexMemService.deleteMemory(params.id);\n return response;\n } catch (error) {\n console.error(`删除记忆 ${params.id} 失败:`, error);\n throw error;\n }\n }, {\n params: t.Object({\n id: t.String()\n })\n })\n \n // 获取统计信息\n .get('/stats/summary', async () => {\n try {\n const memories = await cortexMemService.listMemories({});\n \n // 计算基本统计\n const total = memories.total;\n const types = memories.memories.reduce((acc, memory) => {\n const type = memory.metadata.memory_type;\n acc[type] = (acc[type] || 0) + 1;\n return acc;\n }, {} as Record);\n \n // 按用户分组\n const users = memories.memories.reduce((acc, memory) => {\n const userId = memory.metadata.user_id || 'unknown';\n acc[userId] = (acc[userId] || 0) + 1;\n return acc;\n }, {} as Record);\n \n // 最近记忆\n const recent = memories.memories\n .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())\n .slice(0, 10);\n \n return {\n total,\n types,\n users: Object.keys(users).length,\n user_distribution: users,\n recent_count: recent.length\n };\n } catch (error) {\n console.error('获取统计信息失败:', error);\n throw error;\n }\n })\n \n // 获取类型分布\n .get('/stats/types', async () => {\n try {\n const memories = await cortexMemService.listMemories({});\n \n const typeDistribution = memories.memories.reduce((acc, memory) => {\n const type = memory.metadata.memory_type;\n acc[type] = (acc[type] || 0) + 1;\n return acc;\n }, {} as Record);\n \n return {\n distribution: typeDistribution,\n total: memories.total\n };\n } catch (error) {\n console.error('获取类型分布失败:', error);\n throw error;\n }\n })\n \n // 批量操作\n .post('/batch/delete', async ({ body }) => {\n try {\n const results = await Promise.allSettled(\n body.ids.map((id: string) => cortexMemService.deleteMemory(id))\n );\n \n const succeeded = results.filter(r => r.status === 'fulfilled').length;\n const failed = results.filter(r => r.status === 'rejected').length;\n \n return {\n total: body.ids.length,\n succeeded,\n failed,\n results: results.map((r, i) => ({\n id: body.ids[i],\n status: r.status,\n error: r.status === 'rejected' ? (r as PromiseRejectedResult).reason.message : undefined\n }))\n };\n } catch (error) {\n console.error('批量删除失败:', error);\n throw error;\n }\n }, {\n body: t.Object({\n ids: t.Array(t.String())\n })\n });" - }, - "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 259, - "number_of_classes": 0, - "number_of_functions": 9 - }, - "dependencies": [ - { - "dependency_type": "framework", - "is_external": true, - "line_number": 1, - "name": "elysia", - "path": null, - "version": null }, { - "dependency_type": "service", - "is_external": false, - "line_number": 2, - "name": "cortex-mem-service", - "path": "../integrations/cortex-mem", - "version": null - } - ], - "detailed_description": "This component defines a comprehensive API layer for memory management using the Elysia framework. It exposes multiple endpoints to interact with memory data via the cortexMemService integration. The API supports listing memories with filtering capabilities, searching via semantic queries, retrieving individual records, creating and updating memories, and deleting both single and multiple entries. Additional analytical endpoints provide statistical summaries including type distribution and recent activity. All routes include proper error handling and request validation through Elysia's type system (t.*). The code uses consistent async/await patterns and structured error logging. Response transformations are applied where necessary to match expected output formats.", - "interfaces": [ - { - "description": "Represents the structure of a memory record returned by the API", - "interface_type": "interface", - "name": "MemoryResponse", + "description": "Retrieves a specific memory by ID", + "interface_type": "function", + "name": "get_memory", "parameters": [ { - "description": null, - "is_optional": false, - "name": "id", - "param_type": "string" - }, - { - "description": null, - "is_optional": false, - "name": "content", - "param_type": "string" - }, - { - "description": "Contains contextual information about the memory", - "is_optional": false, - "name": "metadata", - "param_type": "object" - }, - { - "description": null, - "is_optional": false, - "name": "created_at", - "param_type": "string" - }, - { - "description": null, + "description": "Tool call arguments", "is_optional": false, - "name": "updated_at", - "param_type": "string" + "name": "arguments", + "param_type": "&Map" } ], - "return_type": null, - "visibility": "public" + "return_type": "Result", + "visibility": "private" }, { - "description": "Structure for paginated memory list responses", - "interface_type": "interface", - "name": "ListResponse", + "description": "Finds config file in standard locations with fallback precedence", + "interface_type": "function", + "name": "find_default_config_path", + "parameters": [], + "return_type": "Option", + "visibility": "private" + }, + { + "description": "Converts internal tool errors to standardized MCP error format", + "interface_type": "function", + "name": "tools_error_to_mcp_error", "parameters": [ { - "description": null, - "is_optional": false, - "name": "total", - "param_type": "number" - }, - { - "description": null, + "description": "Domain-specific error to convert", "is_optional": false, - "name": "memories", - "param_type": "MemoryResponse[]" + "name": "error", + "param_type": "MemoryToolsError" } ], - "return_type": null, + "return_type": "ErrorData", + "visibility": "private" + }, + { + "description": "Implements ServerHandler trait to return server metadata and capabilities", + "interface_type": "trait_method", + "name": "get_info", + "parameters": [], + "return_type": "ServerInfo", "visibility": "public" }, { - "description": "Request payload structure for memory search operations", - "interface_type": "interface", - "name": "SearchRequest", + "description": "Returns list of available memory management tools for discovery", + "interface_type": "trait_method", + "name": "list_tools", "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "user_id", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "agent_id", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "run_id", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "actor_id", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "memory_type", - "param_type": "string" - }, { "description": null, "is_optional": true, - "name": "limit", - "param_type": "number" + "name": "_request", + "param_type": "Option" }, { "description": null, - "is_optional": true, - "name": "similarity_threshold", - "param_type": "number" + "is_optional": false, + "name": "_context", + "param_type": "RequestContext" } ], - "return_type": null, + "return_type": "impl Future>", "visibility": "public" }, { - "description": "Structure for search results with similarity scoring", - "interface_type": "interface", - "name": "SearchResponse", + "description": "Routes incoming tool calls to appropriate handler methods", + "interface_type": "trait_method", + "name": "call_tool", "parameters": [ { "description": null, "is_optional": false, - "name": "total", - "param_type": "number" + "name": "request", + "param_type": "CallToolRequestParam" }, { "description": null, "is_optional": false, - "name": "results", - "param_type": "Array<{ memory: MemoryResponse; score: number }>" + "name": "_context", + "param_type": "RequestContext" } ], - "return_type": null, + "return_type": "impl Future>", "visibility": "public" } ], "responsibilities": [ - "Expose RESTful endpoints for memory CRUD operations", - "Handle validation and transformation of incoming API requests", - "Orchestrate calls to the underlying cortexMemService for data operations", - "Provide statistical insights and analytics on memory data", - "Support batch operations for efficient memory management" + "Initialize and manage the memory system (vector store, LLM client) based on configuration", + "Handle MCP tool calls for memory operations (store, query, list, get)", + "Translate between MCP tool request format and internal memory operation payloads", + "Provide server metadata and tool discovery via ServerHandler implementation", + "Convert domain-specific errors into standardized MCP error responses" ] }, { "code_dossier": { "code_purpose": "api", - "description": "Provides system monitoring and management endpoints for health checks, performance metrics, logs, and resource usage in the Cortex-Mem Insights backend.", - "file_path": "cortex-mem-insights/src/server/api/system.ts", + "description": "Provides a comprehensive API client for interacting with the Cortex Memory Service, supporting CRUD operations, search, optimization tasks, and system health monitoring.", + "file_path": "cortex-mem-insights/src/server/integrations/cortex-mem.ts", "functions": [ - "systemRoutes.get('/status')", - "systemRoutes.get('/vector-store/status')", - "systemRoutes.get('/llm/status')", - "systemRoutes.get('/metrics')", - "systemRoutes.get('/info')", - "systemRoutes.get('/logs')", - "systemRoutes.get('/health')", - "systemRoutes.get('/resources')", - "systemRoutes.post('/clear-cache')", - "systemRoutes.post('/restart')" + "healthCheck", + "listMemories", + "searchMemories", + "getMemory", + "createMemory", + "updateMemory", + "deleteMemory", + "batchDelete", + "getStatistics", + "optimize", + "getOptimizationStatus", + "cancelOptimization", + "getOptimizationHistory", + "analyzeOptimization", + "getOptimizationStatistics", + "cleanupOptimizationHistory", + "getLLMStatus", + "llmHealthCheck" ], "importance_score": 0.8, "interfaces": [ - "SystemStatus", - "PerformanceMetrics", - "SystemInfo", - "LogEntry" + "MemoryResponse", + "SearchResponse", + "ListResponse", + "HealthResponse" ], - "name": "system.ts", - "source_summary": "import { Elysia, t } from 'elysia';\nimport { cors } from '@elysiajs/cors';\nimport { cortexMemService } from '../integrations/cortex-mem';\n\n// 系统状态接口\ninterface SystemStatus {\n status: 'healthy' | 'unhealthy';\n vector_store: boolean;\n llm_service: boolean;\n timestamp: string;\n}\n\n// 性能指标接口\ninterface PerformanceMetrics {\n cpu_usage: number;\n memory_usage: number;\n disk_usage: number;\n active_connections: number;\n request_count: number;\n error_rate: number;\n response_time_avg: number;\n timestamp: string;\n}\n\n// 系统信息接口\ninterface SystemInfo {\n version: string;\n uptime: string;\n platform: string;\n arch: string;\n node_version: string;\n memory_total: number;\n memory_used: number;\n cpu_count: number;\n hostname: string;\n}\n\n// 日志条目接口\ninterface LogEntry {\n timestamp: string;\n level: 'info' | 'warn' | 'error' | 'debug';\n message: string;\n source: string;\n metadata?: Record;\n}\n\n// 模拟数据\nconst mockSystemStatus: SystemStatus = {\n status: 'healthy',\n vector_store: true,\n llm_service: true,\n timestamp: new Date().toISOString(),\n};\n\nconst mockPerformanceMetrics: PerformanceMetrics = {\n cpu_usage: 45.2,\n memory_usage: 68.7,\n disk_usage: 32.1,\n active_connections: 12,\n request_count: 1250,\n error_rate: 0.5,\n response_time_avg: 125.3,\n timestamp: new Date().toISOString(),\n};\n\nconst mockSystemInfo: SystemInfo = {\n version: '0.1.0',\n uptime: '2 days, 3 hours, 45 minutes',\n platform: 'win32',\n arch: 'x64',\n node_version: '22.12.0',\n memory_total: 16384,\n memory_used: 11264,\n cpu_count: 8,\n hostname: 'cortex-mem-insights',\n};\n\nconst mockLogs: LogEntry[] = [\n {\n timestamp: new Date(Date.now() - 60000).toISOString(),\n level: 'info',\n message: 'System health check completed',\n source: 'health-check',\n },\n {\n timestamp: new Date(Date.now() - 120000).toISOString(),\n level: 'info',\n message: 'Memory search request processed',\n source: 'memory-api',\n metadata: { query: 'test', results: 5 },\n },\n {\n timestamp: new Date(Date.now() - 180000).toISOString(),\n level: 'warn',\n message: 'High memory usage detected',\n source: 'monitor',\n metadata: { usage: 85.2 },\n },\n {\n timestamp: new Date(Date.now() - 240000).toISOString(),\n level: 'info',\n message: 'Optimization job started',\n source: 'optimization-api',\n metadata: { job_id: 'opt-123' },\n },\n {\n timestamp: new Date(Date.now() - 300000).toISOString(),\n level: 'error',\n message: 'Failed to connect to vector store',\n source: 'vector-store',\n metadata: { error: 'Connection timeout' },\n },\n];\n\n// 创建系统API路由\nexport const systemRoutes = new Elysia({ prefix: '/api/system' })\n .use(cors())\n \n // 获取系统状态\n .get('/status', async () => {\n try {\n // 获取真实的cortex-mem-service状态\n const llmStatus = await cortexMemService.getLLMStatus();\n const healthCheck = await cortexMemService.healthCheck();\n \n // 检查Qdrant状态(通过cortex-mem-service的健康检查)\n const vectorStoreStatus = healthCheck.vector_store;\n const llmServiceStatus = healthCheck.llm_service;\n\n const systemStatus = {\n status: vectorStoreStatus && llmServiceStatus ? 'healthy' : 'unhealthy',\n vector_store: vectorStoreStatus,\n llm_service: llmServiceStatus,\n llm_details: {\n completion_model: llmStatus.completion_model,\n embedding_model: llmStatus.embedding_model,\n overall_status: llmStatus.overall_status,\n },\n timestamp: new Date().toISOString(),\n };\n\n return {\n success: true,\n data: systemStatus,\n timestamp: new Date().toISOString(),\n };\n } catch (error) {\n console.error('获取系统状态失败:', error);\n return {\n success: false,\n error: {\n code: 'STATUS_CHECK_FAILED',\n message: error instanceof Error ? error.message : '获取系统状态失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n })\n \n // 获取向量存储状态\n .get('/vector-store/status', async () => {\n try {\n const healthCheck = await cortexMemService.healthCheck();\n \n return {\n success: true,\n data: {\n status: healthCheck.vector_store ? 'connected' : 'disconnected',\n available: healthCheck.vector_store,\n type: 'qdrant',\n last_check: healthCheck.timestamp,\n },\n timestamp: new Date().toISOString(),\n };\n } catch (error) {\n console.error('获取向量存储状态失败:', error);\n return {\n success: false,\n error: {\n code: 'VECTOR_STORE_CHECK_FAILED',\n message: error instanceof Error ? error.message : '获取向量存储状态失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n })\n \n // 获取LLM服务详细状态\n .get('/llm/status', async () => {\n try {\n const llmStatus = await cortexMemService.getLLMStatus();\n \n return {\n success: true,\n data: llmStatus,\n timestamp: new Date().toISOString(),\n };\n } catch (error) {\n console.error('获取LLM服务状态失败:', error);\n return {\n success: false,\n error: {\n code: 'LLM_STATUS_CHECK_FAILED',\n message: error instanceof Error ? error.message : '获取LLM服务状态失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n })\n \n // 获取性能指标\n .get('/metrics', () => {\n return {\n success: true,\n data: mockPerformanceMetrics,\n timestamp: new Date().toISOString(),\n };\n })\n \n // 获取系统信息\n .get('/info', () => {\n return {\n success: true,\n data: mockSystemInfo,\n timestamp: new Date().toISOString(),\n };\n })\n \n // 获取实时日志\n .get('/logs', ({ query }) => {\n const { limit = 50, level, source } = query as {\n limit?: number;\n level?: string;\n source?: string;\n };\n \n let filteredLogs = [...mockLogs];\n \n if (level) {\n filteredLogs = filteredLogs.filter(log => log.level === level);\n }\n \n if (source) {\n filteredLogs = filteredLogs.filter(log => log.source === source);\n }\n \n filteredLogs = filteredLogs.slice(0, limit);\n \n return {\n success: true,\n data: filteredLogs,\n total: filteredLogs.length,\n timestamp: new Date().toISOString(),\n };\n })\n \n // 健康检查\n .get('/health', () => {\n return {\n success: true,\n status: 'healthy',\n timestamp: new Date().toISOString(),\n services: {\n api: true,\n database: true,\n vector_store: true,\n llm_service: true,\n },\n };\n })\n \n // 获取资源使用情况\n .get('/resources', () => {\n return {\n success: true,\n data: {\n memory: {\n total: mockSystemInfo.memory_total,\n used: mockSystemInfo.memory_used,\n free: mockSystemInfo.memory_total - mockSystemInfo.memory_used,\n percentage: (mockSystemInfo.memory_used / mockSystemInfo.memory_total) * 100,\n },\n cpu: {\n usage: mockPerformanceMetrics.cpu_usage,\n cores: mockSystemInfo.cpu_count,\n },\n disk: {\n usage: mockPerformanceMetrics.disk_usage,\n },\n network: {\n active_connections: mockPerformanceMetrics.active_connections,\n },\n },\n timestamp: new Date().toISOString(),\n };\n })\n \n // 清理系统缓存\n .post('/clear-cache', () => {\n return {\n success: true,\n message: 'System cache cleared successfully',\n timestamp: new Date().toISOString(),\n };\n })\n \n // 重启服务\n .post('/restart', () => {\n return {\n success: true,\n message: 'Service restart initiated',\n timestamp: new Date().toISOString(),\n restart_time: new Date(Date.now() + 5000).toISOString(),\n };\n })\n \n // 错误处理\n .onError(({ code, error }) => {\n console.error('System API error:', error);\n \n return {\n success: false,\n error: {\n code: code || 'INTERNAL_ERROR',\n message: error.message || 'An unexpected error occurred',\n },\n timestamp: new Date().toISOString(),\n };\n });" + "name": "cortex-mem.ts", + "source_summary": "import { MemoryResponse, SearchResponse, ListResponse, HealthResponse } from '../api/types';\n\n// Cortex-mem-service API 客户端\nexport class CortexMemServiceClient {\n private baseUrl: string;\n \n constructor(baseUrl: string = 'http://localhost:3000') {\n this.baseUrl = baseUrl;\n }\n \n // 健康检查\n async healthCheck(): Promise {\n const response = await fetch(`${this.baseUrl}/health`);\n if (!response.ok) {\n throw new Error(`Health check failed: ${response.statusText}`);\n }\n return await response.json();\n }\n \n // 获取记忆列表\n async listMemories(params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n }): Promise {\n try {\n const queryParams = new URLSearchParams();\n if (params?.user_id) queryParams.append('user_id', params.user_id);\n if (params?.agent_id) queryParams.append('agent_id', params.agent_id);\n if (params?.run_id) queryParams.append('run_id', params.run_id);\n if (params?.actor_id) queryParams.append('actor_id', params.actor_id);\n if (params?.memory_type) queryParams.append('memory_type', params.memory_type);\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n \n const url = `${this.baseUrl}/memories${queryParams.toString() ? `?${queryParams}` : ''}`;\n \n const response = await fetch(url);\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('获取记忆列表失败 - 错误响应:', errorText);\n throw new Error(`List memories failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return result;\n } catch (error) {\n console.error('获取记忆列表错误:', error);\n return {\n total: 0,\n memories: [],\n };\n }\n }\n \n // 搜索记忆\n async searchMemories(query: string, params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n }): Promise {\n try {\n const requestBody = {\n query,\n user_id: params?.user_id,\n agent_id: params?.agent_id,\n run_id: params?.run_id,\n actor_id: params?.actor_id,\n memory_type: params?.memory_type,\n limit: params?.limit,\n similarity_threshold: params?.similarity_threshold,\n };\n \n const response = await fetch(`${this.baseUrl}/memories/search`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('搜索记忆失败 - 错误响应:', errorText);\n throw new Error(`Search memories failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return result;\n } catch (error) {\n console.error('搜索记忆错误:', error);\n return {\n total: 0,\n results: [],\n };\n }\n }\n \n // 获取单个记忆\n async getMemory(id: string): Promise {\n try {\n const response = await fetch(`${this.baseUrl}/memories/${id}`);\n \n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`Get memory failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('Get memory error:', error);\n return null;\n }\n }\n \n // 创建记忆\n async createMemory(content: string, metadata?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type?: string;\n custom?: Record;\n }): Promise<{ success: boolean; id?: string; message: string }> {\n try {\n const requestBody = {\n content,\n user_id: metadata?.user_id,\n agent_id: metadata?.agent_id,\n run_id: metadata?.run_id,\n actor_id: metadata?.actor_id,\n role: metadata?.role,\n memory_type: metadata?.memory_type,\n custom: metadata?.custom,\n };\n \n const response = await fetch(`${this.baseUrl}/memories`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody),\n });\n \n if (!response.ok) {\n throw new Error(`Create memory failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return {\n success: true,\n id: result.id,\n message: result.message,\n };\n } catch (error) {\n console.error('Create memory error:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to create memory',\n };\n }\n }\n \n // 更新记忆\n async updateMemory(id: string, content: string): Promise<{ success: boolean; message: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/memories/${id}`, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ content }),\n });\n \n if (!response.ok) {\n throw new Error(`Update memory failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return {\n success: true,\n message: result.message,\n };\n } catch (error) {\n console.error('Update memory error:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to update memory',\n };\n }\n }\n \n // 删除记忆\n async deleteMemory(id: string): Promise<{ success: boolean; message: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/memories/${id}`, {\n method: 'DELETE',\n });\n \n if (!response.ok) {\n throw new Error(`Delete memory failed: ${response.statusText}`);\n }\n \n const result = await response.json();\n return {\n success: true,\n message: result.message,\n };\n } catch (error) {\n console.error('Delete memory error:', error);\n return {\n success: false,\n message: error instanceof Error ? error.message : 'Failed to delete memory',\n };\n }\n }\n \n // 批量操作\n async batchDelete(ids: string[]): Promise<{ success: boolean; message: string; failed: string[] }> {\n const failed: string[] = [];\n \n for (const id of ids) {\n try {\n await this.deleteMemory(id);\n } catch (error) {\n failed.push(id);\n }\n }\n \n return {\n success: failed.length === 0,\n message: failed.length === 0 \n ? 'All memories deleted successfully' \n : `Failed to delete ${failed.length} memories`,\n failed,\n };\n }\n \n // 统计信息\n async getStatistics(): Promise<{\n total_memories: number;\n by_type: Record;\n by_user: Record;\n by_agent: Record;\n recent_activity: Array<{ date: string; count: number }>;\n }> {\n try {\n // 获取所有记忆\n const listResponse = await this.listMemories({ limit: 1000 });\n \n // 统计类型分布\n const byType: Record = {};\n const byUser: Record = {};\n const byAgent: Record = {};\n \n // 按日期统计最近活动(最近7天)\n const recentActivity: Array<{ date: string; count: number }> = [];\n const today = new Date();\n \n for (let i = 6; i >= 0; i--) {\n const date = new Date(today);\n date.setDate(date.getDate() - i);\n const dateStr = date.toISOString().split('T')[0];\n recentActivity.push({ date: dateStr, count: 0 });\n }\n \n for (const memory of listResponse.memories) {\n // 统计类型\n const type = memory.metadata.memory_type;\n byType[type] = (byType[type] || 0) + 1;\n \n // 统计用户\n if (memory.metadata.user_id) {\n byUser[memory.metadata.user_id] = (byUser[memory.metadata.user_id] || 0) + 1;\n }\n \n // 统计代理\n if (memory.metadata.agent_id) {\n byAgent[memory.metadata.agent_id] = (byAgent[memory.metadata.agent_id] || 0) + 1;\n }\n \n // 统计最近活动\n const memoryDate = new Date(memory.created_at).toISOString().split('T')[0];\n const activityEntry = recentActivity.find(a => a.date === memoryDate);\n if (activityEntry) {\n activityEntry.count++;\n }\n }\n \n return {\n total_memories: listResponse.total,\n by_type: byType,\n by_user: byUser,\n by_agent: byAgent,\n recent_activity: recentActivity,\n };\n } catch (error) {\n console.error('Get statistics error:', error);\n return {\n total_memories: 0,\n by_type: {},\n by_user: {},\n by_agent: {},\n recent_activity: [],\n };\n }\n }\n\n // 优化相关方法\n \n // 启动优化任务\n async optimize(params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n strategy?: string;\n aggressive?: boolean;\n timeout_minutes?: number;\n }): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(params || {}),\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('启动优化失败 - 错误响应:', errorText);\n throw new Error(`Optimize failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('启动优化错误:', error);\n return {\n success: false,\n error: {\n code: 'OPTIMIZE_FAILED',\n message: error instanceof Error ? error.message : '启动优化失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 获取优化任务状态\n async getOptimizationStatus(jobId: string): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/${jobId}`);\n \n if (!response.ok) {\n if (response.status === 404) {\n return {\n success: false,\n error: {\n code: 'JOB_NOT_FOUND',\n message: `优化任务 ${jobId} 不存在`,\n },\n timestamp: new Date().toISOString(),\n };\n }\n throw new Error(`Get optimization status failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('获取优化状态错误:', error);\n return {\n success: false,\n error: {\n code: 'GET_STATUS_FAILED',\n message: error instanceof Error ? error.message : '获取状态失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 取消优化任务\n async cancelOptimization(jobId: string): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/${jobId}/cancel`, {\n method: 'POST',\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('取消优化失败 - 错误响应:', errorText);\n throw new Error(`Cancel optimization failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('取消优化错误:', error);\n return {\n success: false,\n error: {\n code: 'CANCEL_FAILED',\n message: error instanceof Error ? error.message : '取消优化失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 获取优化历史\n async getOptimizationHistory(params?: {\n limit?: number;\n offset?: number;\n status?: string;\n start_date?: string;\n end_date?: string;\n }): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const queryParams = new URLSearchParams();\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.offset) queryParams.append('offset', params.offset.toString());\n if (params?.status) queryParams.append('status', params.status);\n if (params?.start_date) queryParams.append('start_date', params.start_date);\n if (params?.end_date) queryParams.append('end_date', params.end_date);\n \n const url = `${this.baseUrl}/optimization/history${queryParams.toString() ? `?${queryParams}` : ''}`;\n const response = await fetch(url);\n \n if (!response.ok) {\n throw new Error(`Get optimization history failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('获取优化历史错误:', error);\n return {\n success: false,\n error: {\n code: 'GET_HISTORY_FAILED',\n message: error instanceof Error ? error.message : '获取历史失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 分析优化问题(预览模式)\n async analyzeOptimization(params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n }): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/analyze`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(params || {}),\n });\n \n if (!response.ok) {\n const errorText = await response.text();\n console.error('分析优化失败 - 错误响应:', errorText);\n throw new Error(`Analyze optimization failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('分析优化错误:', error);\n return {\n success: false,\n error: {\n code: 'ANALYZE_FAILED',\n message: error instanceof Error ? error.message : '分析失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 获取优化统计\n async getOptimizationStatistics(): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/statistics`);\n \n if (!response.ok) {\n throw new Error(`Get optimization statistics failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('获取优化统计错误:', error);\n return {\n success: false,\n error: {\n code: 'GET_STATISTICS_FAILED',\n message: error instanceof Error ? error.message : '获取统计失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // 清理优化历史\n async cleanupOptimizationHistory(maxAgeDays?: number): Promise<{ success: boolean; data?: any; error?: any; timestamp: string }> {\n try {\n const response = await fetch(`${this.baseUrl}/optimization/cleanup`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ max_age_days: maxAgeDays || 7 }),\n });\n \n if (!response.ok) {\n throw new Error(`Cleanup optimization history failed: ${response.statusText}`);\n }\n \n return await response.json();\n } catch (error) {\n console.error('清理优化历史错误:', error);\n return {\n success: false,\n error: {\n code: 'CLEANUP_FAILED',\n message: error instanceof Error ? error.message : '清理失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }\n\n // LLM服务状态检测\n \n // 获取详细的LLM服务状态\n async getLLMStatus(): Promise<{\n overall_status: string;\n completion_model: {\n available: boolean;\n provider: string;\n model_name: string;\n latency_ms?: number;\n error_message?: string;\n last_check: string;\n };\n embedding_model: {\n available: boolean;\n provider: string;\n model_name: string;\n latency_ms?: number;\n error_message?: string;\n last_check: string;\n };\n timestamp: string;\n }> {\n const response = await fetch(`${this.baseUrl}/llm/status`);\n \n if (!response.ok) {\n throw new Error(`Get LLM status failed: ${response.statusText}`);\n }\n \n return await response.json();\n }\n\n // 简单的LLM健康检查\n async llmHealthCheck(): Promise<{\n completion_model_available: boolean;\n embedding_model_available: boolean;\n timestamp: string;\n }> {\n const response = await fetch(`${this.baseUrl}/llm/health-check`);\n \n if (!response.ok) {\n throw new Error(`LLM health check failed: ${response.statusText}`);\n }\n \n return await response.json();\n }\n}\n\n// 创建默认客户端实例\nexport const cortexMemService = new CortexMemServiceClient(\n process.env.CORTEX_MEM_SERVICE_URL || 'http://localhost:3000'\n);" }, "complexity_metrics": { - "cyclomatic_complexity": 3.0, - "lines_of_code": 329, - "number_of_classes": 0, - "number_of_functions": 12 + "cyclomatic_complexity": 36.0, + "lines_of_code": 602, + "number_of_classes": 1, + "number_of_functions": 20 }, "dependencies": [ { - "dependency_type": "framework", - "is_external": true, - "line_number": 1, - "name": "elysia", - "path": null, + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "MemoryResponse", + "path": "../api/types", "version": null }, { - "dependency_type": "plugin", - "is_external": true, - "line_number": 2, - "name": "@elysiajs/cors", - "path": null, + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "SearchResponse", + "path": "../api/types", "version": null }, { - "dependency_type": "service", + "dependency_type": "import", "is_external": false, - "line_number": 3, - "name": "cortex-mem-service", - "path": "../integrations/cortex-mem", + "line_number": null, + "name": "ListResponse", + "path": "../api/types", + "version": null + }, + { + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "HealthResponse", + "path": "../api/types", "version": null } ], - "detailed_description": "This component defines a comprehensive set of API endpoints under the '/api/system' prefix using Elysia.js for monitoring and managing the backend system. It exposes interfaces for retrieving real-time system health status (including vector store and LLM service connectivity), performance metrics, system information, filtered logs, and resource utilization. The API also supports management operations such as cache clearing and service restart. While some data (metrics, system info, logs) is currently mocked, critical health status is retrieved from the actual cortex-mem-service integration. Error handling is centralized via Elysia's onError hook, ensuring consistent response formatting across all endpoints. All responses follow a standardized structure with success flags, data/error payloads, and timestamps.", + "detailed_description": "This component implements a full-featured TypeScript client for the Cortex Memory Service API. It encapsulates RESTful HTTP interactions into intuitive methods, providing operations for memory management (CRUD), semantic search, batch processing, statistical analysis, optimization workflows, and LLM service monitoring. The client handles error cases gracefully with try-catch blocks, detailed logging, and fallback responses. It supports flexible parameterization through optional query parameters and request body metadata. The implementation uses modern async/await syntax and follows REST API best practices with proper HTTP methods and status code handling. The component also includes utility methods for system monitoring and maintenance tasks like optimization job management and historical data cleanup.", "interfaces": [ { - "description": "Represents the overall system health status with component-level availability", - "interface_type": "interface", - "name": "SystemStatus", + "description": "Performs health check on the Cortex Memory Service", + "interface_type": "method", + "name": "healthCheck", "parameters": [], - "return_type": "object", - "visibility": "private" + "return_type": "Promise", + "visibility": "public" }, { - "description": "Defines system performance metrics including CPU, memory, disk usage and request statistics", - "interface_type": "interface", - "name": "PerformanceMetrics", - "parameters": [], - "return_type": "object", - "visibility": "private" + "description": "Retrieves list of memories with optional filtering", + "interface_type": "method", + "name": "listMemories", + "parameters": [ + { + "description": "Filter parameters including user_id, agent_id, run_id, actor_id, memory_type, and limit", + "is_optional": true, + "name": "params", + "param_type": "object" + } + ], + "return_type": "Promise", + "visibility": "public" }, { - "description": "Contains detailed system information such as version, uptime, hardware and runtime details", - "interface_type": "interface", - "name": "SystemInfo", - "parameters": [], - "return_type": "object", - "visibility": "private" + "description": "Performs semantic search on memories", + "interface_type": "method", + "name": "searchMemories", + "parameters": [ + { + "description": "Search query string", + "is_optional": false, + "name": "query", + "param_type": "string" + }, + { + "description": "Additional search parameters including filters and similarity threshold", + "is_optional": true, + "name": "params", + "param_type": "object" + } + ], + "return_type": "Promise", + "visibility": "public" }, { - "description": "Represents a single log entry with timestamp, level, message, source and optional metadata", - "interface_type": "interface", - "name": "LogEntry", - "parameters": [], - "return_type": "object", - "visibility": "private" - } - ], - "responsibilities": [ - "Provide system health monitoring endpoints for both frontend and operations teams", - "Expose real-time status of critical services including vector store and LLM connectivity", - "Serve performance metrics and system resource utilization data", - "Deliver filtered real-time log entries with query capabilities", - "Support system management operations like cache clearing and restart" - ] - }, - { - "code_dossier": { - "code_purpose": "specificfeature", - "description": "Manages state and operations for memory optimization jobs including running optimizations, tracking job status, loading history, handling cancellation, and computing derived metrics.", - "file_path": "cortex-mem-insights/src/lib/stores/optimization.ts", - "functions": [ - "runOptimization", - "getJobStatus", - "loadHistory", - "cancelOptimization", - "loadStatistics", - "setFilter", - "clearFilters", - "setPagination", - "clearCurrentJob", - "reset" - ], - "importance_score": 0.8, - "interfaces": [ - "optimizationStore", - "optimizationStatsStore", - "optimizationStatus", - "recentOptimizations", - "optimizationMetrics" - ], - "name": "optimization.ts", - "source_summary": "import { writable, derived } from 'svelte/store';\nimport { optimizationApi } from '../api/client';\nimport type { OptimizationResult, OptimizationHistory } from '../../server/api/types';\n\n// 优化状态\ninterface OptimizationState {\n currentJob: OptimizationResult | null;\n history: OptimizationHistory[];\n loading: boolean;\n error: string | null;\n filters: {\n status?: string;\n start_date?: string;\n end_date?: string;\n };\n pagination: {\n page: number;\n limit: number;\n total: number;\n };\n}\n\n// 初始状态\nconst initialState: OptimizationState = {\n currentJob: null,\n history: [],\n loading: false,\n error: null,\n filters: {\n status: undefined,\n start_date: undefined,\n end_date: undefined,\n },\n pagination: {\n page: 1,\n limit: 20,\n total: 0,\n },\n};\n\n// 创建store\nfunction createOptimizationStore() {\n const { subscribe, set, update } = writable(initialState);\n\n return {\n subscribe,\n \n // 执行优化\n runOptimization: async (params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await optimizationApi.optimize(params);\n \n update(state => ({\n ...state,\n currentJob: response.data,\n loading: false,\n }));\n \n return { success: true, jobId: response.data?.job_id };\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to run optimization',\n }));\n \n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to run optimization',\n };\n }\n },\n \n // 获取优化状态\n getJobStatus: async (jobId: string) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await optimizationApi.getStatus(jobId);\n \n update(state => ({\n ...state,\n currentJob: response.data,\n loading: false,\n }));\n \n return { success: true, job: response.data };\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to get job status',\n }));\n \n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get job status',\n };\n }\n },\n \n // 加载优化历史\n loadHistory: async (params?: {\n page?: number;\n limit?: number;\n status?: string;\n start_date?: string;\n end_date?: string;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await optimizationApi.history({\n limit: params?.limit || initialState.pagination.limit,\n offset: ((params?.page || 1) - 1) * (params?.limit || initialState.pagination.limit),\n status: params?.status,\n start_date: params?.start_date,\n end_date: params?.end_date,\n });\n \n update(state => ({\n ...state,\n history: response.data?.history || [],\n loading: false,\n filters: {\n ...state.filters,\n status: params?.status,\n start_date: params?.start_date,\n end_date: params?.end_date,\n },\n pagination: {\n ...state.pagination,\n page: params?.page || 1,\n limit: params?.limit || state.pagination.limit,\n total: response.data?.total || 0,\n },\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load optimization history',\n }));\n }\n },\n \n // 取消优化\n cancelOptimization: async (jobId: string) => {\n try {\n await optimizationApi.cancel(jobId);\n \n update(state => {\n if (state.currentJob?.job_id === jobId) {\n return {\n ...state,\n currentJob: {\n ...state.currentJob,\n status: 'failed',\n message: 'Optimization cancelled by user',\n },\n };\n }\n \n return {\n ...state,\n history: state.history.map(job => \n job.job_id === jobId \n ? { ...job, status: 'cancelled' }\n : job\n ),\n };\n });\n \n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to cancel optimization',\n };\n }\n },\n \n // 获取优化统计\n loadStatistics: async () => {\n try {\n const response = await optimizationApi.statistics();\n return { success: true, statistics: response.data };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to load optimization statistics',\n };\n }\n },\n \n // 设置过滤器\n setFilter: (filter: keyof OptimizationState['filters'], value: string | undefined) => {\n update(state => ({\n ...state,\n filters: {\n ...state.filters,\n [filter]: value,\n },\n }));\n },\n \n // 清除过滤器\n clearFilters: () => {\n update(state => ({\n ...state,\n filters: initialState.filters,\n }));\n },\n \n // 设置分页\n setPagination: (page: number, limit?: number) => {\n update(state => ({\n ...state,\n pagination: {\n ...state.pagination,\n page,\n limit: limit || state.pagination.limit,\n },\n }));\n },\n \n // 清除当前任务\n clearCurrentJob: () => {\n update(state => ({\n ...state,\n currentJob: null,\n }));\n },\n \n // 重置状态\n reset: () => {\n set(initialState);\n },\n };\n}\n\nexport const optimizationStore = createOptimizationStore();\n\n// 优化统计状态\ninterface OptimizationStatsState {\n statistics: {\n total_jobs: number;\n successful_jobs: number;\n failed_jobs: number;\n cancelled_jobs: number;\n total_memories_processed: number;\n total_memories_deduplicated: number;\n total_memories_merged: number;\n total_memories_enhanced: number;\n avg_duration: number;\n last_run: string | null;\n } | null;\n loading: boolean;\n error: string | null;\n}\n\nfunction createOptimizationStatsStore() {\n const { subscribe, set, update } = writable({\n statistics: null,\n loading: false,\n error: null,\n });\n\n return {\n subscribe,\n \n // 加载统计信息\n loadStatistics: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await optimizationApi.statistics();\n update(state => ({ ...state, statistics: response.data, loading: false }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load optimization statistics',\n }));\n }\n },\n \n // 清除状态\n clear: () => {\n set({ statistics: null, loading: false, error: null });\n },\n };\n}\n\nexport const optimizationStatsStore = createOptimizationStatsStore();\n\n// 导出派生store\nexport const optimizationStatus = derived(optimizationStore, ($optimization) => {\n if (!$optimization.currentJob) return null;\n \n return {\n jobId: $optimization.currentJob.job_id,\n status: $optimization.currentJob.status,\n progress: $optimization.currentJob.processed_memories / $optimization.currentJob.total_memories * 100,\n message: $optimization.currentJob.message,\n duration: $optimization.currentJob.duration,\n };\n});\n\nexport const recentOptimizations = derived(optimizationStore, ($optimization) => {\n return $optimization.history\n .sort((a, b) => new Date(b.start_time).getTime() - new Date(a.start_time).getTime())\n .slice(0, 10);\n});\n\nexport const optimizationMetrics = derived(optimizationStatsStore, ($stats) => {\n if (!$stats.statistics) return null;\n \n return {\n successRate: $stats.statistics.total_jobs > 0 \n ? ($stats.statistics.successful_jobs / $stats.statistics.total_jobs) * 100 \n : 0,\n avgMemoriesPerJob: $stats.statistics.total_jobs > 0\n ? $stats.statistics.total_memories_processed / $stats.statistics.total_jobs\n : 0,\n deduplicationRate: $stats.statistics.total_memories_processed > 0\n ? ($stats.statistics.total_memories_deduplicated / $stats.statistics.total_memories_processed) * 100\n : 0,\n mergeRate: $stats.statistics.total_memories_processed > 0\n ? ($stats.statistics.total_memories_merged / $stats.statistics.total_memories_processed) * 100\n : 0,\n enhancementRate: $stats.statistics.total_memories_processed > 0\n ? ($stats.statistics.total_memories_enhanced / $stats.statistics.total_memories_processed) * 100\n : 0,\n };\n});" - }, - "complexity_metrics": { - "cyclomatic_complexity": 4.0, - "lines_of_code": 347, - "number_of_classes": 0, - "number_of_functions": 14 - }, - "dependencies": [ - { - "dependency_type": "import", - "is_external": true, - "line_number": 1, - "name": "writable", - "path": "svelte/store", - "version": null + "description": "Retrieves a single memory record by ID", + "interface_type": "method", + "name": "getMemory", + "parameters": [ + { + "description": "Memory record ID", + "is_optional": false, + "name": "id", + "param_type": "string" + } + ], + "return_type": "Promise", + "visibility": "public" }, { - "dependency_type": "import", - "is_external": true, - "line_number": 1, - "name": "derived", - "path": "svelte/store", - "version": null + "description": "Creates a new memory record", + "interface_type": "method", + "name": "createMemory", + "parameters": [ + { + "description": "Memory content", + "is_optional": false, + "name": "content", + "param_type": "string" + }, + { + "description": "Additional metadata including user_id, agent_id, role, memory_type, and custom fields", + "is_optional": true, + "name": "metadata", + "param_type": "object" + } + ], + "return_type": "Promise<{ success: boolean; id?: string; message: string }>", + "visibility": "public" }, { - "dependency_type": "import", - "is_external": false, - "line_number": 2, - "name": "optimizationApi", - "path": "../api/client", - "version": null + "description": "Updates an existing memory record", + "interface_type": "method", + "name": "updateMemory", + "parameters": [ + { + "description": "Memory record ID", + "is_optional": false, + "name": "id", + "param_type": "string" + }, + { + "description": "Updated content", + "is_optional": false, + "name": "content", + "param_type": "string" + } + ], + "return_type": "Promise<{ success: boolean; message: string }>", + "visibility": "public" }, { - "dependency_type": "type_import", - "is_external": false, - "line_number": 3, - "name": "OptimizationResult", - "path": "../../server/api/types", - "version": null + "description": "Deletes a memory record", + "interface_type": "method", + "name": "deleteMemory", + "parameters": [ + { + "description": "Memory record ID", + "is_optional": false, + "name": "id", + "param_type": "string" + } + ], + "return_type": "Promise<{ success: boolean; message: string }>", + "visibility": "public" }, { - "dependency_type": "type_import", - "is_external": false, - "line_number": 3, - "name": "OptimizationHistory", - "path": "../../server/api/types", - "version": null - } - ], - "detailed_description": "This component implements a Svelte store for managing memory optimization workflows in a frontend application. It handles core operations such as initiating optimization jobs via `runOptimization`, retrieving job status, loading historical records with pagination and filtering support, and allowing job cancellation. The store maintains both current job state and optimization history, with comprehensive error handling and loading states. Two primary stores are created: `optimizationStore` for job management and `optimizationStatsStore` for statistics. Several derived stores (`optimizationStatus`, `recentOptimizations`, `optimizationMetrics`) provide computed views of the data for UI consumption. All operations are implemented asynchronously with proper state updates using Svelte's writable store pattern, ensuring reactivity throughout the application.", - "interfaces": [ + "description": "Performs batch deletion of memory records", + "interface_type": "method", + "name": "batchDelete", + "parameters": [ + { + "description": "Array of memory record IDs to delete", + "is_optional": false, + "name": "ids", + "param_type": "string[]" + } + ], + "return_type": "Promise<{ success: boolean; message: string; failed: string[] }>", + "visibility": "public" + }, { - "description": "Main store for optimization job management with methods to control the optimization workflow", - "interface_type": "store", - "name": "optimizationStore", + "description": "Retrieves comprehensive statistics about memory records", + "interface_type": "method", + "name": "getStatistics", "parameters": [], - "return_type": "Readable & { runOptimization: Function; getJobStatus: Function; loadHistory: Function; cancelOptimization: Function; loadStatistics: Function; setFilter: Function; clearFilters: Function; setPagination: Function; clearCurrentJob: Function; reset: Function }", + "return_type": "Promise<{ total_memories: number; by_type: Record; by_user: Record; by_agent: Record; recent_activity: Array<{ date: string; count: number }> }>", "visibility": "public" }, { - "description": "Store dedicated to optimization statistics with loading and clearing functionality", - "interface_type": "store", - "name": "optimizationStatsStore", - "parameters": [], - "return_type": "Readable & { loadStatistics: Function; clear: Function }", + "description": "Initiates memory optimization process", + "interface_type": "method", + "name": "optimize", + "parameters": [ + { + "description": "Optimization parameters including filters, strategy, and execution options", + "is_optional": true, + "name": "params", + "param_type": "object" + } + ], + "return_type": "Promise<{ success: boolean; data?: any; error?: any; timestamp: string }>", "visibility": "public" }, { - "description": "Derived store computing current optimization job status and progress percentage", - "interface_type": "derived", - "name": "optimizationStatus", - "parameters": [], - "return_type": "Readable<{ jobId: string; status: string; progress: number; message: string; duration: number } | null>", + "description": "Retrieves status of an optimization job", + "interface_type": "method", + "name": "getOptimizationStatus", + "parameters": [ + { + "description": "Optimization job ID", + "is_optional": false, + "name": "jobId", + "param_type": "string" + } + ], + "return_type": "Promise<{ success: boolean; data?: any; error?: any; timestamp: string }>", "visibility": "public" }, { - "description": "Derived store providing sorted list of 10 most recent optimization jobs", - "interface_type": "derived", - "name": "recentOptimizations", - "parameters": [], - "return_type": "Readable", + "description": "Cancels a running optimization job", + "interface_type": "method", + "name": "cancelOptimization", + "parameters": [ + { + "description": "Optimization job ID to cancel", + "is_optional": false, + "name": "jobId", + "param_type": "string" + } + ], + "return_type": "Promise<{ success: boolean; data?: any; error?: any; timestamp: string }>", "visibility": "public" }, { - "description": "Derived store calculating key performance metrics from optimization statistics", - "interface_type": "derived", - "name": "optimizationMetrics", + "description": "Retrieves history of optimization jobs", + "interface_type": "method", + "name": "getOptimizationHistory", + "parameters": [ + { + "description": "Filter parameters for history retrieval", + "is_optional": true, + "name": "params", + "param_type": "object" + } + ], + "return_type": "Promise<{ success: boolean; data?: any; error?: any; timestamp: string }>", + "visibility": "public" + }, + { + "description": "Performs analysis of potential optimizations (preview mode)", + "interface_type": "method", + "name": "analyzeOptimization", + "parameters": [ + { + "description": "Parameters for optimization analysis", + "is_optional": true, + "name": "params", + "param_type": "object" + } + ], + "return_type": "Promise<{ success: boolean; data?: any; error?: any; timestamp: string }>", + "visibility": "public" + }, + { + "description": "Retrieves statistics about optimization activities", + "interface_type": "method", + "name": "getOptimizationStatistics", "parameters": [], - "return_type": "Readable<{ successRate: number; avgMemoriesPerJob: number; deduplicationRate: number; mergeRate: number; enhancementRate: number } | null>", + "return_type": "Promise<{ success: boolean; data?: any; error?: any; timestamp: string }>", "visibility": "public" - } - ], - "responsibilities": [ - "Managing the state of active and historical memory optimization jobs", - "Providing reactive interfaces for running, monitoring, and canceling optimization tasks", - "Handling pagination, filtering, and sorting of optimization history", - "Maintaining and exposing derived metrics and statistics for optimization performance", - "Synchronizing frontend state with backend optimization API through error-resilient requests" - ] + }, + { + "description": "Cleans up old optimization history records", + "interface_type": "method", + "name": "cleanupOptimizationHistory", + "parameters": [ + { + "description": "Maximum age of history records to retain (default: 7 days)", + "is_optional": true, + "name": "maxAgeDays", + "param_type": "number" + } + ], + "return_type": "Promise<{ success: boolean; data?: any; error?: any; timestamp: string }>", + "visibility": "public" + }, + { + "description": "Retrieves detailed status of LLM services", + "interface_type": "method", + "name": "getLLMStatus", + "parameters": [], + "return_type": "Promise<{ overall_status: string; completion_model: { available: boolean; provider: string; model_name: string; latency_ms?: number; error_message?: string; last_check: string }; embedding_model: { available: boolean; provider: string; model_name: string; latency_ms?: number; error_message?: string; last_check: string }; timestamp: string }>", + "visibility": "public" + }, + { + "description": "Performs simple health check on LLM services", + "interface_type": "method", + "name": "llmHealthCheck", + "parameters": [], + "return_type": "Promise<{ completion_model_available: boolean; embedding_model_available: boolean; timestamp: string }>", + "visibility": "public" + } + ], + "responsibilities": [ + "Provide HTTP client interface for Cortex Memory Service API", + "Handle CRUD operations for memory records with proper error handling", + "Support semantic search and batch operations on memory data", + "Manage optimization workflows including job submission, status tracking, and cancellation", + "Monitor system health and LLM service availability" + ] }, { "code_dossier": { - "code_purpose": "widget", - "description": "Svelte stores for managing memory data state including list, detail, and statistics. Provides reactive state management for memory operations like loading, searching, filtering, pagination, deletion, and updates.", - "file_path": "cortex-mem-insights/src/lib/stores/memory.ts", - "functions": [ - "createMemoryStore", - "createMemoryDetailStore", - "createMemoryStatsStore" - ], + "code_purpose": "types", + "description": "Defines shared TypeScript interfaces for API responses, memory structures, system status, optimization operations, and request/response payloads across the backend service.", + "file_path": "cortex-mem-insights/src/server/api/types.ts", + "functions": [], "importance_score": 0.8, "interfaces": [ - "memoryStore", - "memoryDetailStore", - "memoryStatsStore", - "memoryTypes", - "topUsers", - "topAgents" + "ApiResponse", + "MemoryMetadataResponse", + "MemoryResponse", + "ScoredMemoryResponse", + "ListResponse", + "SearchResponse", + "HealthResponse", + "OptimizationRequest", + "OptimizationResult", + "OptimizationHistory", + "SystemStatus", + "PerformanceMetrics", + "SystemInfo", + "LogEntry", + "Statistics", + "PaginationParams", + "FilterParams", + "SearchParams", + "CreateMemoryRequest", + "UpdateMemoryRequest", + "BatchOperationRequest", + "BatchOperationResponse", + "ExportFormat", + "ExportResponse" ], - "name": "memory.ts", - "source_summary": "import { writable, derived } from 'svelte/store';\nimport { memoryApi } from '../api/client';\nimport type { MemoryResponse, SearchResponse, ListResponse } from '../../server/api/types';\n\n// 记忆列表状态\ninterface MemoryListState {\n memories: MemoryResponse[];\n total: number;\n loading: boolean;\n error: string | null;\n filters: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n search_query?: string;\n };\n pagination: {\n page: number;\n limit: number;\n total_pages: number;\n };\n}\n\n// 初始状态\nconst initialState: MemoryListState = {\n memories: [],\n total: 0,\n loading: false,\n error: null,\n filters: {\n user_id: undefined,\n agent_id: undefined,\n run_id: undefined,\n actor_id: undefined,\n memory_type: undefined,\n search_query: undefined,\n },\n pagination: {\n page: 1,\n limit: 20,\n total_pages: 1,\n },\n};\n\n// 创建store\nfunction createMemoryStore() {\n const { subscribe, set, update } = writable(initialState);\n\n return {\n subscribe,\n \n // 加载记忆列表\n loadMemories: async (params?: {\n page?: number;\n limit?: number;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await memoryApi.list({\n ...params,\n limit: params?.limit || initialState.pagination.limit,\n }) as ListResponse;\n \n update(state => ({\n ...state,\n memories: response.memories,\n total: response.total,\n loading: false,\n filters: {\n ...state.filters,\n user_id: params?.user_id,\n agent_id: params?.agent_id,\n run_id: params?.run_id,\n actor_id: params?.actor_id,\n memory_type: params?.memory_type,\n },\n pagination: {\n ...state.pagination,\n page: params?.page || 1,\n limit: params?.limit || state.pagination.limit,\n total_pages: Math.ceil(response.total / (params?.limit || state.pagination.limit)),\n },\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load memories',\n }));\n }\n },\n \n // 搜索记忆\n searchMemories: async (query: string, params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await memoryApi.search(query, params) as SearchResponse;\n \n update(state => ({\n ...state,\n memories: response.results.map(r => r.memory),\n total: response.total,\n loading: false,\n filters: {\n ...state.filters,\n user_id: params?.user_id,\n agent_id: params?.agent_id,\n run_id: params?.run_id,\n actor_id: params?.actor_id,\n memory_type: params?.memory_type,\n search_query: query,\n },\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to search memories',\n }));\n }\n },\n \n // 清除搜索\n clearSearch: () => {\n update(state => ({\n ...state,\n filters: {\n ...state.filters,\n search_query: undefined,\n },\n }));\n },\n \n // 设置过滤器\n setFilter: (filter: keyof MemoryListState['filters'], value: string | undefined) => {\n update(state => ({\n ...state,\n filters: {\n ...state.filters,\n [filter]: value,\n },\n }));\n },\n \n // 清除所有过滤器\n clearFilters: () => {\n update(state => ({\n ...state,\n filters: initialState.filters,\n }));\n },\n \n // 设置分页\n setPagination: (page: number, limit?: number) => {\n update(state => ({\n ...state,\n pagination: {\n ...state.pagination,\n page,\n limit: limit || state.pagination.limit,\n },\n }));\n },\n \n // 删除记忆\n deleteMemory: async (id: string) => {\n try {\n await memoryApi.delete(id);\n \n update(state => ({\n ...state,\n memories: state.memories.filter(memory => memory.id !== id),\n total: state.total - 1,\n }));\n \n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to delete memory',\n };\n }\n },\n \n // 批量删除\n batchDelete: async (ids: string[]) => {\n try {\n await memoryApi.batchDelete(ids);\n \n update(state => ({\n ...state,\n memories: state.memories.filter(memory => !ids.includes(memory.id)),\n total: state.total - ids.length,\n }));\n \n return { success: true, deleted: ids.length };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to delete memories',\n };\n }\n },\n \n // 重置状态\n reset: () => {\n set(initialState);\n },\n };\n}\n\nexport const memoryStore = createMemoryStore();\n\n// 单个记忆状态\ninterface MemoryDetailState {\n memory: MemoryResponse | null;\n loading: boolean;\n error: string | null;\n}\n\nfunction createMemoryDetailStore() {\n const { subscribe, set, update } = writable({\n memory: null,\n loading: false,\n error: null,\n });\n\n return {\n subscribe,\n \n // 加载记忆详情\n loadMemory: async (id: string) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const memory = await memoryApi.get(id) as MemoryResponse;\n update(state => ({ ...state, memory, loading: false }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load memory',\n }));\n }\n },\n \n // 更新记忆\n updateMemory: async (id: string, content: string) => {\n try {\n await memoryApi.update(id, content);\n \n update(state => {\n if (state.memory?.id === id) {\n return {\n ...state,\n memory: {\n ...state.memory,\n content,\n updated_at: new Date().toISOString(),\n },\n };\n }\n return state;\n });\n \n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to update memory',\n };\n }\n },\n \n // 清除状态\n clear: () => {\n set({ memory: null, loading: false, error: null });\n },\n };\n}\n\nexport const memoryDetailStore = createMemoryDetailStore();\n\n// 记忆统计状态\ninterface MemoryStatsState {\n statistics: {\n total_memories: number;\n by_type: Record;\n by_user: Record;\n by_agent: Record;\n recent_activity: Array<{ date: string; count: number }>;\n } | null;\n loading: boolean;\n error: string | null;\n}\n\nfunction createMemoryStatsStore() {\n const { subscribe, set, update } = writable({\n statistics: null,\n loading: false,\n error: null,\n });\n\n return {\n subscribe,\n \n // 加载统计信息\n loadStatistics: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const statistics = await memoryApi.statistics();\n update(state => ({ ...state, statistics, loading: false }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load statistics',\n }));\n }\n },\n \n // 清除状态\n clear: () => {\n set({ statistics: null, loading: false, error: null });\n },\n };\n}\n\nexport const memoryStatsStore = createMemoryStatsStore();\n\n// 导出派生store\nexport const memoryTypes = derived(memoryStatsStore, ($stats) => {\n if (!$stats.statistics) return [];\n \n return Object.entries($stats.statistics.by_type)\n .map(([type, count]) => ({ type, count }))\n .sort((a, b) => b.count - a.count);\n});\n\nexport const topUsers = derived(memoryStatsStore, ($stats) => {\n if (!$stats.statistics) return [];\n \n return Object.entries($stats.statistics.by_user)\n .map(([user, count]) => ({ user, count }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 10);\n});\n\nexport const topAgents = derived(memoryStatsStore, ($stats) => {\n if (!$stats.statistics) return [];\n \n return Object.entries($stats.statistics.by_agent)\n .map(([agent, count]) => ({ agent, count }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 10);\n});" + "name": "types.ts", + "source_summary": "// API 响应类型\nexport interface ApiResponse {\n success: boolean;\n data?: T;\n error?: string;\n message?: string;\n timestamp: string;\n}\n\n// 记忆相关类型\nexport interface MemoryMetadataResponse {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type: string;\n hash: string;\n importance_score?: number;\n custom?: Record;\n}\n\nexport interface MemoryResponse {\n id: string;\n content: string;\n metadata: MemoryMetadataResponse;\n created_at: string;\n updated_at: string;\n}\n\nexport interface ScoredMemoryResponse {\n memory: MemoryResponse;\n score: number;\n}\n\n// 列表响应\nexport interface ListResponse {\n total: number;\n memories: MemoryResponse[];\n}\n\n// 搜索响应\nexport interface SearchResponse {\n total: number;\n results: ScoredMemoryResponse[];\n}\n\n// 健康检查响应\nexport interface HealthResponse {\n status: string;\n vector_store: boolean;\n llm_service: boolean;\n timestamp: string;\n}\n\n// 优化相关类型\nexport interface OptimizationRequest {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n}\n\nexport interface OptimizationResult {\n job_id: string;\n status: 'pending' | 'running' | 'completed' | 'failed';\n total_memories: number;\n processed_memories: number;\n deduplicated: number;\n merged: number;\n enhanced: number;\n errors: number;\n start_time: string;\n end_time?: string;\n duration?: number;\n message?: string;\n}\n\nexport interface OptimizationHistory {\n job_id: string;\n status: string;\n total_memories: number;\n processed_memories: number;\n start_time: string;\n end_time?: string;\n duration?: number;\n}\n\n// 系统相关类型\nexport interface SystemStatus {\n status: 'healthy' | 'unhealthy';\n vector_store: boolean;\n llm_service: boolean;\n timestamp: string;\n}\n\nexport interface PerformanceMetrics {\n cpu_usage: number;\n memory_usage: number;\n disk_usage: number;\n active_connections: number;\n request_count: number;\n error_rate: number;\n response_time_avg: number;\n timestamp: string;\n}\n\nexport interface SystemInfo {\n version: string;\n uptime: string;\n platform: string;\n arch: string;\n node_version: string;\n memory_total: number;\n memory_used: number;\n cpu_count: number;\n hostname: string;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: 'info' | 'warn' | 'error' | 'debug';\n message: string;\n source: string;\n metadata?: Record;\n}\n\n// 统计类型\nexport interface Statistics {\n total_memories: number;\n by_type: Record;\n by_user: Record;\n by_agent: Record;\n recent_activity: Array<{ date: string; count: number }>;\n}\n\n// 分页参数\nexport interface PaginationParams {\n page?: number;\n limit?: number;\n sort_by?: string;\n sort_order?: 'asc' | 'desc';\n}\n\n// 过滤参数\nexport interface FilterParams {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n start_date?: string;\n end_date?: string;\n min_score?: number;\n max_score?: number;\n}\n\n// 搜索参数\nexport interface SearchParams extends FilterParams {\n query: string;\n limit?: number;\n similarity_threshold?: number;\n}\n\n// 创建记忆请求\nexport interface CreateMemoryRequest {\n content: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type?: string;\n custom?: Record;\n}\n\n// 更新记忆请求\nexport interface UpdateMemoryRequest {\n content: string;\n}\n\n// 批量操作请求\nexport interface BatchOperationRequest {\n ids: string[];\n operation: 'delete' | 'export' | 'tag';\n tags?: string[];\n}\n\n// 批量操作响应\nexport interface BatchOperationResponse {\n success: boolean;\n message: string;\n total: number;\n succeeded: number;\n failed: number;\n failed_ids?: string[];\n}\n\n// 导出格式\nexport interface ExportFormat {\n format: 'json' | 'csv' | 'txt';\n include_metadata?: boolean;\n include_scores?: boolean;\n compress?: boolean;\n}\n\n// 导出响应\nexport interface ExportResponse {\n success: boolean;\n download_url?: string;\n file_size?: number;\n format: string;\n item_count: number;\n message?: string;\n}" }, "complexity_metrics": { - "cyclomatic_complexity": 5.0, - "lines_of_code": 374, + "cyclomatic_complexity": 1.0, + "lines_of_code": 219, "number_of_classes": 0, - "number_of_functions": 3 + "number_of_functions": 0 }, - "dependencies": [ + "dependencies": [], + "detailed_description": "This file serves as the central type definition hub for the API layer of a memory management and optimization system. It defines structured data contracts used throughout request handling, response formatting, and internal service communication. The types support core functionalities including CRUD operations on memories, search and filtering with metadata, optimization jobs (deduplication, merging), system monitoring, batch processing, and data export. All interfaces are designed to be serializable and align with RESTful API conventions, ensuring type safety between client and server. Notably, it uses generic patterns (e.g., ApiResponse) for consistent response handling and leverages utility types like Record for extensible metadata.", + "interfaces": [ { - "dependency_type": "import", - "is_external": true, - "line_number": 1, - "name": "svelte/store", - "path": "svelte/store", - "version": null + "description": "Generic wrapper for all API responses containing success flag, optional data/error, and timestamp", + "interface_type": "interface", + "name": "ApiResponse", + "parameters": [], + "return_type": "T", + "visibility": "export" }, { - "dependency_type": "import", - "is_external": false, - "line_number": 2, - "name": "memoryApi", - "path": "../api/client", - "version": null + "description": "Represents a complete memory object with content, metadata, and timestamps", + "interface_type": "interface", + "name": "MemoryResponse", + "parameters": [], + "return_type": null, + "visibility": "export" }, { - "dependency_type": "import", - "is_external": false, - "line_number": 3, - "name": "MemoryResponse, SearchResponse, ListResponse", - "path": "../../server/api/types", - "version": null - } - ], - "detailed_description": "This component implements three Svelte stores (memoryStore, memoryDetailStore, memoryStatsStore) to manage the state of memory data in a reactive frontend application. The memoryStore handles the list of memories with support for filtering, pagination, searching, and batch operations. The memoryDetailStore manages the state of a single memory item for viewing and editing. The memoryStatsStore provides aggregate statistics about memories. Additionally, derived stores (memoryTypes, topUsers, topAgents) transform and expose statistics data for UI consumption. All operations are implemented asynchronously with proper error handling and loading states, making it suitable for integration with API calls through the injected memoryApi service.", - "interfaces": [ - { - "description": "Primary store for managing memory list state with operations for loading, searching, filtering, and deleting memories", - "interface_type": "store", - "name": "memoryStore", + "description": "Structured response for memory search queries containing scored results", + "interface_type": "interface", + "name": "SearchResponse", "parameters": [], - "return_type": "Readable & { loadMemories, searchMemories, clearSearch, setFilter, clearFilters, setPagination, deleteMemory, batchDelete, reset }", + "return_type": null, "visibility": "export" }, { - "description": "Store for managing individual memory detail state with operations for loading and updating a single memory", - "interface_type": "store", - "name": "memoryDetailStore", + "description": "Input payload for initiating memory optimization jobs with filtering and execution options", + "interface_type": "interface", + "name": "OptimizationRequest", "parameters": [], - "return_type": "Readable & { loadMemory, updateMemory, clear }", + "return_type": null, "visibility": "export" }, { - "description": "Store for managing memory statistics state with operation to load aggregate data", - "interface_type": "store", - "name": "memoryStatsStore", + "description": "Response structure for data export requests including download URL and format details", + "interface_type": "interface", + "name": "ExportResponse", "parameters": [], - "return_type": "Readable & { loadStatistics, clear }", + "return_type": null, "visibility": "export" } ], "responsibilities": [ - "Manage reactive state for memory list with filtering, pagination and search capabilities", - "Handle CRUD operations for memory entities through API integration", - "Maintain state for individual memory details and support real-time updates", - "Provide aggregated memory statistics and derived data for visualization", - "Implement proper error handling and loading states for all asynchronous operations" + "Define standardized API response structure using generic ApiResponse pattern", + "Model memory data and metadata for storage, retrieval, and search operations", + "Specify request payloads for memory creation, update, search, and optimization", + "Provide types for system monitoring, health checks, and performance metrics", + "Support pagination, filtering, and batch operations through dedicated parameter and result interfaces" ] }, { "code_dossier": { - "code_purpose": "widget", - "description": "Svelte stores for managing system status, application connectivity, and UI theme state in a frontend monitoring dashboard.", - "file_path": "cortex-mem-insights/src/lib/stores/system.ts", + "code_purpose": "api", + "description": "API routes for memory optimization operations including optimization execution, history retrieval, status checks, and cleanup.", + "file_path": "cortex-mem-insights/src/server/api/optimization.ts", "functions": [ - "createSystemStore", - "createAppStore", - "createThemeStore" + "optimize", + "getOptimizationHistory", + "getOptimizationStatistics", + "analyzeOptimization", + "getOptimizationStatus", + "cancelOptimization", + "cleanupOptimizationHistory" ], "importance_score": 0.8, "interfaces": [ - "loadStatus", - "loadMetrics", - "loadInfo", - "loadLogs", - "loadResources", - "clearCache", - "restartService", - "refreshAll", - "reset", - "checkConnection", - "setConnected", - "setError", - "toggleDarkMode", - "toggleSidebar", - "loadSettings" + "OptimizationRequest" ], - "name": "system.ts", - "source_summary": "import { writable, derived } from 'svelte/store';\nimport { systemApi } from '../api/client';\nimport type { SystemStatus, PerformanceMetrics, SystemInfo, LogEntry } from '../../server/api/types';\n\n// 系统状态\ninterface SystemState {\n status: SystemStatus | null;\n metrics: PerformanceMetrics | null;\n info: SystemInfo | null;\n logs: LogEntry[];\n loading: boolean;\n error: string | null;\n lastUpdated: string | null;\n}\n\n// 初始状态\nconst initialState: SystemState = {\n status: null,\n metrics: null,\n info: null,\n logs: [],\n loading: false,\n error: null,\n lastUpdated: null,\n};\n\n// 创建store\nfunction createSystemStore() {\n const { subscribe, set, update } = writable(initialState);\n\n return {\n subscribe,\n \n // 加载系统状态\n loadStatus: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.status();\n update(state => ({ \n ...state, \n status: response.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load system status',\n }));\n }\n },\n \n // 加载性能指标\n loadMetrics: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.metrics();\n update(state => ({ \n ...state, \n metrics: response.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load performance metrics',\n }));\n }\n },\n \n // 加载系统信息\n loadInfo: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.info();\n update(state => ({ \n ...state, \n info: response.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load system info',\n }));\n }\n },\n \n // 加载日志\n loadLogs: async (params?: {\n limit?: number;\n level?: string;\n source?: string;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.logs(params);\n update(state => ({ \n ...state, \n logs: response.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load logs',\n }));\n }\n },\n \n // 加载资源使用情况\n loadResources: async () => {\n try {\n const response = await systemApi.resources();\n return { success: true, resources: response.data };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to load resources',\n };\n }\n },\n \n // 清理缓存\n clearCache: async () => {\n try {\n await systemApi.clearCache();\n return { success: true, message: 'Cache cleared successfully' };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to clear cache',\n };\n }\n },\n \n // 重启服务\n restartService: async () => {\n try {\n await systemApi.restart();\n return { success: true, message: 'Service restart initiated' };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to restart service',\n };\n }\n },\n \n // 刷新所有数据\n refreshAll: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const [status, metrics, info, logs] = await Promise.all([\n systemApi.status(),\n systemApi.metrics(),\n systemApi.info(),\n systemApi.logs({ limit: 50 }),\n ]);\n \n update(state => ({\n ...state,\n status: status.data,\n metrics: metrics.data,\n info: info.data,\n logs: logs.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to refresh system data',\n }));\n }\n },\n \n // 重置状态\n reset: () => {\n set(initialState);\n },\n };\n}\n\nexport const systemStore = createSystemStore();\n\n// 应用状态\ninterface AppState {\n connected: boolean;\n loading: boolean;\n error: string | null;\n lastConnectionCheck: string | null;\n}\n\nfunction createAppStore() {\n const { subscribe, set, update } = writable({\n connected: false,\n loading: false,\n error: null,\n lastConnectionCheck: null,\n });\n\n return {\n subscribe,\n \n // 检查连接\n checkConnection: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.health();\n \n update(state => ({\n ...state,\n connected: response.status === 'healthy',\n loading: false,\n lastConnectionCheck: new Date().toISOString(),\n }));\n \n return { success: true, connected: response.status === 'healthy' };\n } catch (error) {\n update(state => ({\n ...state,\n connected: false,\n loading: false,\n error: error instanceof Error ? error.message : 'Connection failed',\n lastConnectionCheck: new Date().toISOString(),\n }));\n \n return { success: false, connected: false };\n }\n },\n \n // 设置连接状态\n setConnected: (connected: boolean) => {\n update(state => ({ ...state, connected }));\n },\n \n // 设置错误\n setError: (error: string | null) => {\n update(state => ({ ...state, error }));\n },\n \n // 重置状态\n reset: () => {\n set({\n connected: false,\n loading: false,\n error: null,\n lastConnectionCheck: null,\n });\n },\n };\n}\n\nexport const appStore = createAppStore();\n\n// 导出派生store\nexport const systemHealth = derived(systemStore, ($system) => {\n if (!$system.status) return null;\n \n return {\n overall: $system.status.status === 'healthy',\n vectorStore: $system.status.vector_store,\n llmService: $system.status.llm_service,\n timestamp: $system.status.timestamp,\n };\n});\n\nexport const performanceSummary = derived(systemStore, ($system) => {\n if (!$system.metrics) return null;\n \n return {\n cpuUsage: $system.metrics.cpu_usage,\n memoryUsage: $system.metrics.memory_usage,\n diskUsage: $system.metrics.disk_usage,\n activeConnections: $system.metrics.active_connections,\n errorRate: $system.metrics.error_rate,\n responseTime: $system.metrics.response_time_avg,\n };\n});\n\nexport const recentLogs = derived(systemStore, ($system) => {\n return $system.logs\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 20);\n});\n\nexport const errorLogs = derived(systemStore, ($system) => {\n return $system.logs\n .filter(log => log.level === 'error')\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 10);\n});\n\nexport const warningLogs = derived(systemStore, ($system) => {\n return $system.logs\n .filter(log => log.level === 'warn')\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 10);\n});\n\n// 主题状态\ninterface ThemeState {\n darkMode: boolean;\n sidebarCollapsed: boolean;\n}\n\nfunction createThemeStore() {\n const { subscribe, set, update } = writable({\n darkMode: false,\n sidebarCollapsed: false,\n });\n\n return {\n subscribe,\n \n // 切换暗黑模式\n toggleDarkMode: () => {\n update(state => {\n const darkMode = !state.darkMode;\n \n // 保存到localStorage\n if (typeof window !== 'undefined') {\n localStorage.setItem('darkMode', darkMode.toString());\n }\n \n return { ...state, darkMode };\n });\n },\n \n // 切换侧边栏\n toggleSidebar: () => {\n update(state => {\n const sidebarCollapsed = !state.sidebarCollapsed;\n \n // 保存到localStorage\n if (typeof window !== 'undefined') {\n localStorage.setItem('sidebarCollapsed', sidebarCollapsed.toString());\n }\n \n return { ...state, sidebarCollapsed };\n });\n },\n \n // 从localStorage加载设置\n loadSettings: () => {\n if (typeof window === 'undefined') return;\n \n const darkMode = localStorage.getItem('darkMode') === 'true';\n const sidebarCollapsed = localStorage.getItem('sidebarCollapsed') === 'true';\n \n set({ darkMode, sidebarCollapsed });\n },\n \n // 重置设置\n reset: () => {\n set({ darkMode: false, sidebarCollapsed: false });\n \n if (typeof window !== 'undefined') {\n localStorage.removeItem('darkMode');\n localStorage.removeItem('sidebarCollapsed');\n }\n },\n };\n}\n\nexport const themeStore = createThemeStore();" + "name": "optimization.ts", + "source_summary": "import { Elysia, t } from 'elysia';\nimport { cortexMemService } from '../integrations/cortex-mem';\n\n// 类型定义\ninterface OptimizationRequest {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n strategy?: string;\n aggressive?: boolean;\n timeout_minutes?: number;\n}\n\n\n\n// 优化API路由\nexport const optimizationRoutes = new Elysia({ prefix: '/api/optimization' })\n // 启动优化\n .post('/', async ({ body }) => {\n try {\n // 直接调用cortex-mem-service的API\n const result = await cortexMemService.optimize(body);\n return result;\n } catch (error) {\n console.error('启动优化失败:', error);\n return {\n success: false,\n error: {\n code: 'OPTIMIZE_FAILED',\n message: error instanceof Error ? error.message : '启动优化失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n body: t.Object({\n memory_type: t.Optional(t.String()),\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n similarity_threshold: t.Optional(t.Number({ default: 0.7 })),\n dry_run: t.Optional(t.Boolean({ default: false })),\n verbose: t.Optional(t.Boolean({ default: false })),\n strategy: t.Optional(t.String()),\n aggressive: t.Optional(t.Boolean({ default: false })),\n timeout_minutes: t.Optional(t.Number()),\n })\n })\n \n // 获取优化历史 - 必须在 /:jobId 之前定义,避免 \"history\" 被当作 jobId\n .get('/history', async ({ query }) => {\n try {\n const result = await cortexMemService.getOptimizationHistory({\n limit: query.limit ? parseInt(query.limit) : 20,\n offset: query.offset ? parseInt(query.offset) : 0,\n status: query.status,\n start_date: query.start_date,\n end_date: query.end_date,\n });\n return result;\n } catch (error) {\n console.error('获取优化历史失败:', error);\n return {\n success: false,\n error: {\n code: 'GET_HISTORY_FAILED',\n message: error instanceof Error ? error.message : '获取历史失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n query: t.Object({\n limit: t.Optional(t.String()),\n offset: t.Optional(t.String()),\n status: t.Optional(t.String()),\n start_date: t.Optional(t.String()),\n end_date: t.Optional(t.String()),\n })\n })\n \n // 获取优化统计 - 也要在 /:jobId 之前\n .get('/statistics', async () => {\n try {\n const result = await cortexMemService.getOptimizationStatistics();\n return result;\n } catch (error) {\n console.error('获取优化统计失败:', error);\n return {\n success: false,\n error: {\n code: 'GET_STATISTICS_FAILED',\n message: error instanceof Error ? error.message : '获取统计失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n })\n \n // 分析优化问题(预览)- 也要在 /:jobId 之前\n .post('/analyze', async ({ body }) => {\n try {\n const result = await cortexMemService.analyzeOptimization(body);\n return result;\n } catch (error) {\n console.error('分析优化问题失败:', error);\n return {\n success: false,\n error: {\n code: 'ANALYZE_FAILED',\n message: error instanceof Error ? error.message : '分析失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n body: t.Object({\n memory_type: t.Optional(t.String()),\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n similarity_threshold: t.Optional(t.Number({ default: 0.7 })),\n })\n })\n \n // 获取优化状态 - 动态路由必须放在静态路由之后\n .get('/:jobId', async ({ params }) => {\n try {\n const result = await cortexMemService.getOptimizationStatus(params.jobId);\n return result;\n } catch (error) {\n console.error('获取优化状态失败:', error);\n return {\n success: false,\n error: {\n code: 'GET_STATUS_FAILED',\n message: error instanceof Error ? error.message : '获取状态失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n params: t.Object({\n jobId: t.String()\n })\n })\n \n // 取消优化\n .post('/:jobId/cancel', async ({ params }) => {\n try {\n const result = await cortexMemService.cancelOptimization(params.jobId);\n return result;\n } catch (error) {\n console.error('取消优化失败:', error);\n return {\n success: false,\n error: {\n code: 'CANCEL_FAILED',\n message: error instanceof Error ? error.message : '取消优化失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n params: t.Object({\n jobId: t.String()\n })\n })\n \n // 清理旧的历史记录 - 也要在 /:jobId 之前\n .post('/cleanup', async ({ body }) => {\n try {\n const result = await cortexMemService.cleanupOptimizationHistory(body.max_age_days);\n return result;\n } catch (error) {\n console.error('清理历史记录失败:', error);\n return {\n success: false,\n error: {\n code: 'CLEANUP_FAILED',\n message: error instanceof Error ? error.message : '清理失败',\n },\n timestamp: new Date().toISOString(),\n };\n }\n }, {\n body: t.Object({\n max_age_days: t.Number({ default: 7 })\n })\n });" }, "complexity_metrics": { - "cyclomatic_complexity": 7.0, - "lines_of_code": 381, + "cyclomatic_complexity": 1.0, + "lines_of_code": 197, "number_of_classes": 0, - "number_of_functions": 19 + "number_of_functions": 6 }, "dependencies": [ { - "dependency_type": "import", + "dependency_type": "module", "is_external": true, "line_number": 1, - "name": "svelte/store", - "path": "svelte/store", + "name": "elysia", + "path": null, "version": null }, { - "dependency_type": "import", - "is_external": false, - "line_number": 2, - "name": "systemApi", - "path": "../api/client", + "dependency_type": "module", + "is_external": true, + "line_number": 1, + "name": "t", + "path": null, "version": null }, { - "dependency_type": "import", + "dependency_type": "service", "is_external": false, - "line_number": 3, - "name": "SystemStatus, PerformanceMetrics, SystemInfo, LogEntry", - "path": "../../server/api/types", + "line_number": 2, + "name": "cortexMemService", + "path": "../integrations/cortex-mem", "version": null } ], - "detailed_description": "This component implements three distinct Svelte stores to manage global state in a frontend application dashboard. The systemStore manages the state of backend system metrics, status, logs, and information through API calls via systemApi. It supports loading individual components or refreshing all data at once, with proper error handling and loading states. The appStore handles frontend application connectivity status by polling a health endpoint, allowing components to reactively respond to backend availability. The themeStore manages UI preferences such as dark mode and sidebar collapse state, persisting these settings to localStorage for user preference retention across sessions. Additionally, several derived stores (systemHealth, performanceSummary, recentLogs, errorLogs, warningLogs) provide computed, filtered views of the raw data for optimized consumption by UI components.", + "detailed_description": "This component defines a set of API endpoints using the Elysia framework to manage memory optimization tasks. It provides functionality to start an optimization job, analyze potential issues, retrieve historical records and statistics, check the status of a specific job, cancel running jobs, and clean up old history. All logic is delegated to an external service `cortexMemService`, making this a thin API layer. The routes are carefully ordered to prevent dynamic segments like `/:jobId` from shadowing static ones such as `/history`. Input validation is implemented via Elysia's type system (`t.Object`, etc.), ensuring robust handling of optional parameters with defaults. Error handling is centralized with structured error responses containing codes and timestamps.", "interfaces": [ { - "description": "Loads current system status from API and updates store state", - "interface_type": "method", - "name": "loadStatus", - "parameters": [], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Loads performance metrics from API and updates store state", - "interface_type": "method", - "name": "loadMetrics", - "parameters": [], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Loads system information from API and updates store state", - "interface_type": "method", - "name": "loadInfo", - "parameters": [], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Loads system logs with optional filtering parameters", - "interface_type": "method", - "name": "loadLogs", + "description": "Defines optional input parameters for optimization operations including memory type, user/agent/run/actor IDs, similarity threshold, dry-run mode, verbosity, strategy, aggressiveness, and timeout.", + "interface_type": "interface", + "name": "OptimizationRequest", "parameters": [ { - "description": "Optional filtering parameters for log retrieval", + "description": null, "is_optional": true, - "name": "params", - "param_type": "object" - } - ], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Loads resource usage data and returns result object", - "interface_type": "method", - "name": "loadResources", - "parameters": [], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Clears application cache and returns operation result", - "interface_type": "method", - "name": "clearCache", - "parameters": [], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Initiates service restart and returns operation result", - "interface_type": "method", - "name": "restartService", - "parameters": [], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Refreshes all system data concurrently", - "interface_type": "method", - "name": "refreshAll", - "parameters": [], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Checks backend health status and updates connection state", - "interface_type": "method", - "name": "checkConnection", - "parameters": [], - "return_type": "Promise", - "visibility": "public" - }, - { - "description": "Toggles dark mode and persists preference to localStorage", - "interface_type": "method", - "name": "toggleDarkMode", - "parameters": [], - "return_type": "void", - "visibility": "public" - }, - { - "description": "Toggles sidebar collapse state and persists to localStorage", - "interface_type": "method", - "name": "toggleSidebar", - "parameters": [], - "return_type": "void", - "visibility": "public" - }, - { - "description": "Loads persisted UI settings from localStorage", - "interface_type": "method", - "name": "loadSettings", - "parameters": [], - "return_type": "void", - "visibility": "public" + "name": "memory_type", + "param_type": "string" + }, + { + "description": null, + "is_optional": true, + "name": "user_id", + "param_type": "string" + }, + { + "description": null, + "is_optional": true, + "name": "agent_id", + "param_type": "string" + }, + { + "description": null, + "is_optional": true, + "name": "run_id", + "param_type": "string" + }, + { + "description": null, + "is_optional": true, + "name": "actor_id", + "param_type": "string" + }, + { + "description": null, + "is_optional": true, + "name": "similarity_threshold", + "param_type": "number" + }, + { + "description": null, + "is_optional": true, + "name": "dry_run", + "param_type": "boolean" + }, + { + "description": null, + "is_optional": true, + "name": "verbose", + "param_type": "boolean" + }, + { + "description": null, + "is_optional": true, + "name": "strategy", + "param_type": "string" + }, + { + "description": null, + "is_optional": true, + "name": "aggressive", + "param_type": "boolean" + }, + { + "description": null, + "is_optional": true, + "name": "timeout_minutes", + "param_type": "number" + } + ], + "return_type": null, + "visibility": "private" } ], "responsibilities": [ - "Manage system monitoring state including status, metrics, info, and logs", - "Handle application connectivity state and health checks", - "Persist and manage UI theme and layout preferences", - "Provide derived computed state for optimized UI rendering", - "Coordinate API interactions with state updates and error handling" + "Expose RESTful API endpoints for initiating and managing memory optimization processes", + "Validate incoming request payloads and query parameters using type-safe schemas", + "Delegate business logic execution to the cortex-mem-service integration layer", + "Provide structured error responses with appropriate error codes and messages", + "Ensure correct routing precedence by placing static paths before dynamic ones" ] }, { "code_dossier": { - "code_purpose": "widget", - "description": "A Svelte component for displaying and monitoring the real-time status of backend services including Cortex Memory Service, Qdrant, and LLM Service. It provides visual indicators for connection status, latency, model details, and supports manual refresh and auto-detection.", - "file_path": "cortex-mem-insights/src/lib/components/ServiceStatus.svelte", - "functions": [ - "detectIndividualServices", - "detectServicesAsync", - "handleRefresh", - "getStatusColor", - "getStatusLightColor", - "getStatusText" - ], + "code_purpose": "api", + "description": "Provides a comprehensive RESTful API for managing and querying memory records with CRUD operations, search, statistics, and batch processing.", + "file_path": "cortex-mem-insights/src/server/api/memory.ts", + "functions": [], "importance_score": 0.8, "interfaces": [ - "systemStatus", - "title", - "showRefreshButton", - "autoDetect", - "statusUpdate" + "MemoryResponse", + "ListResponse", + "SearchRequest", + "SearchResponse" ], - "name": "ServiceStatus.svelte", - "source_summary": "\n\n
\n\t
\n\t\t

{title}

\n\t\t{#if showRefreshButton}\n\t\t\t\n\t\t\t\t{#if isDetectingServices}\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t检测中...\n\t\t\t\t{:else}\n\t\t\t\t\t重新检查所有服务\n\t\t\t\t{/if}\n\t\t\t\n\t\t{/if}\n\t
\n\n\t
\n\t\t{#each Object.entries(localSystemStatus) as [service, data]}\n\t\t\t{#if data && typeof data === 'object' && data.status}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{service === 'cortexMemService'\n\t\t\t\t\t\t\t\t\t? 'Cortex Memory Service'\n\t\t\t\t\t\t\t\t\t: service === 'qdrant'\n\t\t\t\t\t\t\t\t\t\t? 'Qdrant 数据库'\n\t\t\t\t\t\t\t\t\t\t: 'LLM 服务'}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{getStatusText(data.status)}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t延迟: \n\t\t\t\t\t\t\t\t{#if data.status === 'detecting'}\n\t\t\t\t\t\t\t\t\t检测中...\n\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t{data.latency}ms\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t{#if data.provider}\n\t\t\t\t\t\t\t
提供商: {data.provider}
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t{#if data.model}\n\t\t\t\t\t\t\t
模型: {data.model}
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\n\t\t\t\t\t{#if data.lastCheck}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t最后检查: {data.lastCheck}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t{/if}\n\t\t{/each}\n\t
\n
\n\n" + "name": "memory.ts", + "source_summary": "import { Elysia, t } from 'elysia';\nimport { cortexMemService } from '../integrations/cortex-mem';\n\n// 类型定义\ninterface MemoryResponse {\n id: string;\n content: string;\n metadata: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type: string;\n hash: string;\n importance_score?: number;\n custom: Record;\n };\n created_at: string;\n updated_at: string;\n}\n\ninterface ListResponse {\n total: number;\n memories: MemoryResponse[];\n}\n\ninterface SearchRequest {\n query: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n}\n\ninterface SearchResponse {\n total: number;\n results: Array<{\n memory: MemoryResponse;\n score: number;\n }>;\n}\n\n// 内存API路由\nexport const memoryRoutes = new Elysia({ prefix: '/api/memories' })\n // 获取记忆列表\n .get('/', async ({ query }) => {\n try {\n const response = await cortexMemService.listMemories({\n user_id: query.user_id,\n agent_id: query.agent_id,\n run_id: query.run_id,\n actor_id: query.actor_id,\n memory_type: query.memory_type,\n limit: query.limit ? parseInt(query.limit) : undefined\n });\n \n return {\n total: response.total,\n memories: response.memories\n };\n } catch (error) {\n console.error('获取记忆列表失败:', error);\n throw error;\n }\n }, {\n query: t.Object({\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n memory_type: t.Optional(t.String()),\n limit: t.Optional(t.String())\n })\n })\n \n // 搜索记忆\n .post('/search', async ({ body }) => {\n try {\n const { query, ...params } = body;\n const response = await cortexMemService.searchMemories(query, params);\n return response;\n } catch (error) {\n console.error('搜索记忆失败:', error);\n throw error;\n }\n }, {\n body: t.Object({\n query: t.String(),\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n memory_type: t.Optional(t.String()),\n limit: t.Optional(t.Number()),\n similarity_threshold: t.Optional(t.Number())\n })\n })\n \n // 获取单个记忆\n .get('/:id', async ({ params }) => {\n try {\n const memory = await cortexMemService.getMemory(params.id);\n return memory;\n } catch (error) {\n console.error(`获取记忆 ${params.id} 失败:`, error);\n throw error;\n }\n }, {\n params: t.Object({\n id: t.String()\n })\n })\n \n // 创建记忆\n .post('/', async ({ body }) => {\n try {\n const response = await cortexMemService.createMemory(body);\n return response;\n } catch (error) {\n console.error('创建记忆失败:', error);\n throw error;\n }\n }, {\n body: t.Object({\n content: t.String(),\n user_id: t.Optional(t.String()),\n agent_id: t.Optional(t.String()),\n run_id: t.Optional(t.String()),\n actor_id: t.Optional(t.String()),\n role: t.Optional(t.String()),\n memory_type: t.Optional(t.String()),\n custom: t.Optional(t.Record(t.String(), t.Any()))\n })\n })\n \n // 更新记忆\n .put('/:id', async ({ params, body }) => {\n try {\n const response = await cortexMemService.updateMemory(params.id, body.content);\n return response;\n } catch (error) {\n console.error(`更新记忆 ${params.id} 失败:`, error);\n throw error;\n }\n }, {\n params: t.Object({\n id: t.String()\n }),\n body: t.Object({\n content: t.String()\n })\n })\n \n // 删除记忆\n .delete('/:id', async ({ params }) => {\n try {\n const response = await cortexMemService.deleteMemory(params.id);\n return response;\n } catch (error) {\n console.error(`删除记忆 ${params.id} 失败:`, error);\n throw error;\n }\n }, {\n params: t.Object({\n id: t.String()\n })\n })\n \n // 获取统计信息\n .get('/stats/summary', async () => {\n try {\n const memories = await cortexMemService.listMemories({});\n \n // 计算基本统计\n const total = memories.total;\n const types = memories.memories.reduce((acc, memory) => {\n const type = memory.metadata.memory_type;\n acc[type] = (acc[type] || 0) + 1;\n return acc;\n }, {} as Record);\n \n // 按用户分组\n const users = memories.memories.reduce((acc, memory) => {\n const userId = memory.metadata.user_id || 'unknown';\n acc[userId] = (acc[userId] || 0) + 1;\n return acc;\n }, {} as Record);\n \n // 最近记忆\n const recent = memories.memories\n .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())\n .slice(0, 10);\n \n return {\n total,\n types,\n users: Object.keys(users).length,\n user_distribution: users,\n recent_count: recent.length\n };\n } catch (error) {\n console.error('获取统计信息失败:', error);\n throw error;\n }\n })\n \n // 获取类型分布\n .get('/stats/types', async () => {\n try {\n const memories = await cortexMemService.listMemories({});\n \n const typeDistribution = memories.memories.reduce((acc, memory) => {\n const type = memory.metadata.memory_type;\n acc[type] = (acc[type] || 0) + 1;\n return acc;\n }, {} as Record);\n \n return {\n distribution: typeDistribution,\n total: memories.total\n };\n } catch (error) {\n console.error('获取类型分布失败:', error);\n throw error;\n }\n })\n \n // 批量操作\n .post('/batch/delete', async ({ body }) => {\n try {\n const results = await Promise.allSettled(\n body.ids.map((id: string) => cortexMemService.deleteMemory(id))\n );\n \n const succeeded = results.filter(r => r.status === 'fulfilled').length;\n const failed = results.filter(r => r.status === 'rejected').length;\n \n return {\n total: body.ids.length,\n succeeded,\n failed,\n results: results.map((r, i) => ({\n id: body.ids[i],\n status: r.status,\n error: r.status === 'rejected' ? (r as PromiseRejectedResult).reason.message : undefined\n }))\n };\n } catch (error) {\n console.error('批量删除失败:', error);\n throw error;\n }\n }, {\n body: t.Object({\n ids: t.Array(t.String())\n })\n });" }, "complexity_metrics": { - "cyclomatic_complexity": 33.0, - "lines_of_code": 393, + "cyclomatic_complexity": 1.0, + "lines_of_code": 260, "number_of_classes": 0, - "number_of_functions": 6 + "number_of_functions": 9 }, "dependencies": [ { - "dependency_type": "framework", + "dependency_type": "package", "is_external": true, "line_number": null, - "name": "svelte", - "path": "svelte", + "name": "elysia", + "path": null, "version": null }, { - "dependency_type": "module", - "is_external": true, - "line_number": 3, - "name": "svelte", - "path": "svelte", + "dependency_type": "service", + "is_external": false, + "line_number": 2, + "name": "cortex-memService", + "path": "../integrations/cortex-mem", "version": null } ], - "detailed_description": "This component is a comprehensive service status dashboard widget implemented in Svelte. It allows users to monitor the health and connectivity of three core services: Cortex Memory Service, Qdrant vector database, and LLM Service. The component uses client-side HTTP polling via fetch to independently verify each service's availability by hitting specific API endpoints. For Cortex Memory Service, it first tries the /api/memories endpoint and falls back to /health if needed. For Qdrant and LLM services, it queries dedicated status endpoints on the insights server. The component visually represents status using colored indicators and text, with animations during detection. It supports both automatic detection on mount (configurable) and manual refresh via a button. Status updates are dispatched as events for parent components to handle. The UI adapts based on service type, showing provider, model names, and latency where applicable. All state management is encapsulated within the component using reactive declarations and Svelte's onMount lifecycle.", + "detailed_description": "This component defines a complete API module for memory management using the Elysia framework. It exposes endpoints for basic CRUD operations (create, read, update, delete), advanced search capabilities, statistical analysis, and batch operations. The API interacts with an external memory service (cortexMemService) to perform actual data operations while handling request validation, error logging, and response formatting. Type interfaces define the structure of requests and responses, ensuring type safety across the API surface. The component serves as the primary interface between clients and the underlying memory storage system.", "interfaces": [ { - "description": "The current system status object to display. If not provided, defaults to connecting state.", - "interface_type": "property", - "name": "systemStatus", + "description": "Structure of a single memory record including content, metadata, and timestamps", + "interface_type": "type", + "name": "MemoryResponse", "parameters": [], - "return_type": "SystemStatus | null", - "visibility": "export" + "return_type": null, + "visibility": "exported" }, { - "description": "The title displayed at the top of the status widget", - "interface_type": "property", - "name": "title", + "description": "Response structure for listing memories with pagination info", + "interface_type": "type", + "name": "ListResponse", "parameters": [], - "return_type": "string", - "visibility": "export" + "return_type": null, + "visibility": "exported" }, { - "description": "Controls whether the manual refresh button is visible", - "interface_type": "property", - "name": "showRefreshButton", + "description": "Request body structure for memory search operations", + "interface_type": "type", + "name": "SearchRequest", "parameters": [], - "return_type": "boolean", - "visibility": "export" + "return_type": null, + "visibility": "exported" }, { - "description": "Determines if services should be automatically detected when component mounts", - "interface_type": "property", - "name": "autoDetect", + "description": "Response structure for memory search results with similarity scores", + "interface_type": "type", + "name": "SearchResponse", "parameters": [], - "return_type": "boolean", - "visibility": "export" + "return_type": null, + "visibility": "exported" }, { - "description": "Emitted when service status is updated after detection", - "interface_type": "event", - "name": "statusUpdate", - "parameters": [ - { - "description": "The updated system status object", - "is_optional": false, - "name": "systemStatus", - "param_type": "SystemStatus" - } - ], + "description": "Elysia router instance containing all memory-related API endpoints", + "interface_type": "variable", + "name": "memoryRoutes", + "parameters": [], "return_type": null, - "visibility": "dispatch" + "visibility": "exported" } ], "responsibilities": [ - "Monitor and display real-time connectivity status of Cortex Memory Service, Qdrant, and LLM services", - "Perform health checks by polling specific API endpoints and measuring response latency", - "Provide visual status indicators with color coding and animated states for different conditions", - "Support manual refresh and optional automatic service detection on component mount", - "Dispatch status update events for parent components to respond to changes" + "Exposes RESTful endpoints for memory CRUD operations", + "Provides advanced search functionality with filtering and similarity scoring", + "Generates statistical insights about memory data distribution", + "Handles batch operations for efficient memory management", + "Validates incoming requests and formats consistent API responses" ] }, { "code_dossier": { "code_purpose": "api", - "description": "API 客户端配置,提供记忆、优化和系统相关的 API 请求封装。", - "file_path": "cortex-mem-insights/src/lib/api/client.ts", + "description": "Provides system-level monitoring, health checks, performance metrics, and administrative control endpoints for the backend service.", + "file_path": "cortex-mem-insights/src/server/api/system.ts", "functions": [ - "request", - "memoryApi.list", - "memoryApi.search", - "memoryApi.get", - "memoryApi.create", - "memoryApi.update", - "memoryApi.delete", - "memoryApi.batchDelete", - "memoryApi.batchUpdate", - "memoryApi.statistics", - "memoryApi.export", - "optimizationApi.optimize", - "optimizationApi.getStatus", - "optimizationApi.history", - "optimizationApi.cancel", - "optimizationApi.analyze", - "optimizationApi.statistics", - "systemApi.health", - "systemApi.status", - "systemApi.metrics", - "systemApi.info", - "systemApi.logs", - "systemApi.resources", - "systemApi.clearCache", - "systemApi.restart", - "api.testConnection", - "api.getAllStatus" + "GET /status", + "GET /vector-store/status", + "GET /llm/status", + "GET /metrics", + "GET /info", + "GET /logs", + "GET /health", + "GET /resources", + "POST /clear-cache", + "POST /restart" ], "importance_score": 0.8, "interfaces": [ - "memoryApi", - "optimizationApi", - "systemApi", - "api" + "SystemStatus", + "PerformanceMetrics", + "SystemInfo", + "LogEntry" ], - "name": "client.ts", - "source_summary": "// API 客户端配置\n// 在开发模式下使用相对路径,由Vite代理到API服务器\n// 在生产模式下使用环境变量配置的URL\nconst API_BASE_URL = import.meta.env.VITE_API_URL || '';\n\n// 通用请求函数\nasync function request(\n endpoint: string,\n options: RequestInit = {}\n): Promise {\n const url = `${API_BASE_URL}${endpoint}`;\n \n const defaultOptions: RequestInit = {\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n credentials: 'include',\n };\n \n try {\n const response = await fetch(url, { ...defaultOptions, ...options });\n \n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n errorData.error?.message || \n errorData.message || \n `HTTP ${response.status}: ${response.statusText}`\n );\n }\n \n return await response.json();\n } catch (error) {\n console.error(`API request failed: ${endpoint}`, error);\n throw error;\n }\n}\n\n// 记忆相关API\nexport const memoryApi = {\n // 获取记忆列表\n list: (params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n page?: number;\n }) => {\n const queryParams = new URLSearchParams();\n if (params?.user_id) queryParams.append('user_id', params.user_id);\n if (params?.agent_id) queryParams.append('agent_id', params.agent_id);\n if (params?.run_id) queryParams.append('run_id', params.run_id);\n if (params?.actor_id) queryParams.append('actor_id', params.actor_id);\n if (params?.memory_type) queryParams.append('memory_type', params.memory_type);\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.page) queryParams.append('page', params.page.toString());\n \n return request(`/api/memories${queryParams.toString() ? `?${queryParams}` : ''}`);\n },\n \n // 搜索记忆\n search: (query: string, params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n }) => {\n return request('/api/memories/search', {\n method: 'POST',\n body: JSON.stringify({ query, ...params }),\n });\n },\n \n // 获取单个记忆\n get: (id: string) => {\n return request(`/api/memories/${id}`);\n },\n \n // 创建记忆\n create: (content: string, metadata?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type?: string;\n custom?: Record;\n }) => {\n return request('/api/memories', {\n method: 'POST',\n body: JSON.stringify({ content, ...metadata }),\n });\n },\n \n // 更新记忆\n update: (id: string, content: string) => {\n return request(`/api/memories/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ content }),\n });\n },\n \n // 删除记忆\n delete: (id: string) => {\n return request(`/api/memories/${id}`, {\n method: 'DELETE',\n });\n },\n \n // 批量删除\n batchDelete: (ids: string[]) => {\n return request('/api/memories/batch/delete', {\n method: 'POST',\n body: JSON.stringify({ ids }),\n });\n },\n\n // 批量更新\n batchUpdate: (updates: { id: string; content: string }[]) => {\n return request('/api/memories/batch/update', {\n method: 'POST',\n body: JSON.stringify({ updates }),\n });\n },\n \n // 获取统计信息\n statistics: () => {\n return request('/api/memories/stats/summary');\n },\n \n // 导出记忆\n export: (params: {\n format: 'json' | 'csv' | 'txt';\n ids?: string[];\n filters?: any;\n include_metadata?: boolean;\n include_scores?: boolean;\n }) => {\n return request('/api/memories/export', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n },\n};\n\n// 优化相关API\nexport const optimizationApi = {\n // 执行优化\n optimize: (params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n }) => {\n return request('/api/optimization', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n },\n \n // 获取优化状态\n getStatus: (jobId: string) => {\n return request(`/api/optimization/${jobId}`);\n },\n \n // 获取优化历史\n history: (params?: {\n limit?: number;\n offset?: number;\n status?: string;\n start_date?: string;\n end_date?: string;\n }) => {\n const queryParams = new URLSearchParams();\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.offset) queryParams.append('offset', params.offset.toString());\n if (params?.status) queryParams.append('status', params.status);\n if (params?.start_date) queryParams.append('start_date', params.start_date);\n if (params?.end_date) queryParams.append('end_date', params.end_date);\n \n return request(`/api/optimization/history${queryParams.toString() ? `?${queryParams}` : ''}`);\n },\n \n // 取消优化\n cancel: (jobId: string) => {\n return request(`/api/optimization/${jobId}/cancel`, {\n method: 'POST',\n });\n },\n \n // 分析优化问题(预览模式)\n analyze: (params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n }) => {\n return request('/api/optimization/analyze', {\n method: 'POST',\n body: JSON.stringify(params || {}),\n });\n },\n \n // 获取优化统计\n statistics: () => {\n return request('/api/optimization/statistics');\n },\n};\n\n// 系统相关API\nexport const systemApi = {\n // 健康检查\n health: () => {\n return request('/health');\n },\n \n // 系统状态\n status: () => {\n return request('/api/system/status');\n },\n \n // 性能指标\n metrics: () => {\n return request('/api/system/metrics');\n },\n \n // 系统信息\n info: () => {\n return request('/api/system/info');\n },\n \n // 实时日志\n logs: (params?: {\n limit?: number;\n level?: string;\n source?: string;\n }) => {\n const queryParams = new URLSearchParams();\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.level) queryParams.append('level', params.level);\n if (params?.source) queryParams.append('source', params.source);\n \n return request(`/api/system/logs${queryParams.toString() ? `?${queryParams}` : ''}`);\n },\n \n // 资源使用情况\n resources: () => {\n return request('/api/system/resources');\n },\n \n // 清理缓存\n clearCache: () => {\n return request('/api/system/clear-cache', {\n method: 'POST',\n });\n },\n \n // 重启服务\n restart: () => {\n return request('/api/system/restart', {\n method: 'POST',\n });\n },\n};\n\n// 通用API\nexport const api = {\n // 测试连接\n testConnection: async () => {\n try {\n const response = await request('/health');\n return {\n connected: true,\n response,\n };\n } catch (error) {\n return {\n connected: false,\n error: error instanceof Error ? error.message : 'Connection failed',\n };\n }\n },\n \n // 获取所有服务状态\n getAllStatus: async () => {\n try {\n const [health, systemStatus, metrics] = await Promise.all([\n systemApi.health(),\n systemApi.status(),\n systemApi.metrics(),\n ]);\n \n return {\n success: true,\n health,\n systemStatus,\n metrics,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get system status',\n };\n }\n },\n};\n\n// 导出所有API\nexport default {\n memory: memoryApi,\n optimization: optimizationApi,\n system: systemApi,\n api,\n};" + "name": "system.ts", + "source_summary": "import { Elysia, t } from 'elysia';\nimport { cors } from '@elysiajs/cors';\nimport { cortexMemService } from '../integrations/cortex-mem';\nimport os from 'os';\nimport process from 'process';\n\n// 系统状态接口\ninterface SystemStatus {\n status: 'healthy' | 'unhealthy';\n vector_store: boolean;\n llm_service: boolean;\n timestamp: string;\n}\n\n// 性能指标接口\ninterface PerformanceMetrics {\n cpu_usage: number;\n memory_usage: number;\n disk_usage: number;\n active_connections: number;\n request_count: number;\n error_rate: number;\n response_time_avg: number;\n timestamp: string;\n}\n\n// 系统信息接口\ninterface SystemInfo {\n version: string;\n uptime: string;\n platform: string;\n arch: string;\n node_version: string;\n memory_total: number;\n memory_used: number;\n cpu_count: number;\n hostname: string;\n}\n\n// 日志条目接口\ninterface LogEntry {\n timestamp: string;\n level: 'info' | 'warn' | 'error' | 'debug';\n message: string;\n source: string;\n metadata?: Record;\n}\n\n// 创建系统API路由\nexport const systemRoutes = new Elysia({ prefix: '/api/system' })\n .use(cors())\n \n // 获取系统状态\n .get('/status', async () => {\n try {\n // 获取真实的cortex-mem-service状态\n const healthCheck = await cortexMemService.healthCheck();\n const llmStatus = await cortexMemService.getLLMStatus();\n \n // 检查Qdrant状态(通过cortex-mem-service的健康检查)\n const vectorStoreStatus = healthCheck.vector_store;\n const llmServiceStatus = healthCheck.llm_service;\n\n const systemStatus = {\n status: vectorStoreStatus && llmServiceStatus ? 'healthy' : 'unhealthy',\n cortex_mem_service: true, // cortex-mem-service可用\n vector_store: vectorStoreStatus,\n llm_service: llmServiceStatus,\n llm_details: {\n completion_model: llmStatus.completion_model,\n embedding_model: llmStatus.embedding_model,\n overall_status: llmStatus.overall_status,\n },\n timestamp: new Date().toISOString(),\n };\n\n return {\n success: true,\n data: systemStatus,\n timestamp: new Date().toISOString(),\n };\n } catch (error) {\n console.error('获取系统状态失败 - cortex-mem-service不可用:', error);\n // 当cortex-mem-service不可用时,返回错误状态\n return {\n success: true, // 仍然返回success: true,但数据中标记服务不可用\n data: {\n status: 'unhealthy',\n cortex_mem_service: false,\n vector_store: false,\n llm_service: false,\n llm_details: {\n completion_model: {\n available: false,\n provider: 'unknown',\n model_name: 'unknown',\n error_message: error instanceof Error ? error.message : 'Cortex Memory Service不可用',\n last_check: new Date().toISOString(),\n },\n embedding_model: {\n available: false,\n provider: 'unknown',\n model_name: 'unknown',\n error_message: error instanceof Error ? error.message : 'Cortex Memory Service不可用',\n last_check: new Date().toISOString(),\n },\n overall_status: 'error',\n },\n timestamp: new Date().toISOString(),\n },\n timestamp: new Date().toISOString(),\n };\n }\n })\n \n // 获取向量存储状态\n .get('/vector-store/status', async () => {\n try {\n const healthCheck = await cortexMemService.healthCheck();\n \n return {\n success: true,\n data: {\n status: healthCheck.vector_store ? 'connected' : 'disconnected',\n available: healthCheck.vector_store,\n type: 'qdrant',\n last_check: healthCheck.timestamp,\n },\n timestamp: new Date().toISOString(),\n };\n } catch (error) {\n console.error('获取向量存储状态失败 - cortex-mem-service不可用:', error);\n // 当cortex-mem-service不可用时,向量存储也不可用\n return {\n success: false,\n error: {\n code: 'CORTEX_MEM_SERVICE_UNAVAILABLE',\n message: error instanceof Error ? error.message : 'Cortex Memory Service不可用',\n },\n timestamp: new Date().toISOString(),\n };\n }\n })\n \n // 获取LLM服务详细状态\n .get('/llm/status', async () => {\n try {\n const llmStatus = await cortexMemService.getLLMStatus();\n \n return {\n success: true,\n data: llmStatus,\n timestamp: new Date().toISOString(),\n };\n } catch (error) {\n console.error('获取LLM服务状态失败 - cortex-mem-service不可用:', error);\n // 当cortex-mem-service不可用时,LLM服务也不可用\n return {\n success: false,\n error: {\n code: 'CORTEX_MEM_SERVICE_UNAVAILABLE',\n message: error instanceof Error ? error.message : 'Cortex Memory Service不可用',\n },\n timestamp: new Date().toISOString(),\n };\n }\n })\n \n // 获取性能指标 - 返回真实的系统性能数据\n .get('/metrics', () => {\n // 计算CPU使用率\n const cpus = os.cpus();\n const cpuUsage = cpus.reduce((acc: number, cpu: any) => {\n const times = cpu.times as { user: number; nice: number; sys: number; idle: number; irq: number };\n const total = (Object.values(times) as number[]).reduce((a: number, b: number) => a + b, 0);\n const idle = times.idle;\n return acc + ((total - idle) / total) * 100;\n }, 0) / cpus.length;\n \n // 计算内存使用率\n const totalMem = os.totalmem();\n const freeMem = os.freemem();\n const usedMem = totalMem - freeMem;\n const memoryUsage = (usedMem / totalMem) * 100;\n \n return {\n success: true,\n data: {\n cpu_usage: parseFloat(cpuUsage.toFixed(2)),\n memory_usage: parseFloat(memoryUsage.toFixed(2)),\n disk_usage: 0, // 磁盘使用率需要额外的库来获取,暂时返回0\n active_connections: 0, // 活动连接数需要从应用层统计\n request_count: 0, // 请求计数需要从应用层统计\n error_rate: 0, // 错误率需要从应用层统计\n response_time_avg: 0, // 平均响应时间需要从应用层统计\n timestamp: new Date().toISOString(),\n },\n timestamp: new Date().toISOString(),\n };\n })\n \n // 获取系统信息 - 返回真实的系统信息\n .get('/info', () => {\n // 计算运行时间\n const uptimeSeconds = process.uptime();\n const days = Math.floor(uptimeSeconds / 86400);\n const hours = Math.floor((uptimeSeconds % 86400) / 3600);\n const minutes = Math.floor((uptimeSeconds % 3600) / 60);\n const uptime = `${days} days, ${hours} hours, ${minutes} minutes`;\n \n return {\n success: true,\n data: {\n version: '0.1.0',\n uptime,\n platform: os.platform(),\n arch: os.arch(),\n node_version: process.version,\n memory_total: Math.floor(os.totalmem() / 1024 / 1024), // MB\n memory_used: Math.floor((os.totalmem() - os.freemem()) / 1024 / 1024), // MB\n cpu_count: os.cpus().length,\n hostname: os.hostname(),\n },\n timestamp: new Date().toISOString(),\n };\n })\n \n // 获取实时日志 - 暂时返回空数组,实际应该从日志系统获取\n .get('/logs', ({ query }) => {\n const { limit = 50, level, source } = query as {\n limit?: number;\n level?: string;\n source?: string;\n };\n \n // TODO: 实现真实的日志获取逻辑\n // 目前返回空数组,因为没有实际的日志系统\n const logs: any[] = [];\n \n return {\n success: true,\n data: logs,\n total: logs.length,\n timestamp: new Date().toISOString(),\n };\n })\n \n // 健康检查 - 返回insights server自身的健康状态\n .get('/health', async () => {\n try {\n // 检查cortex-mem-service的健康状态\n const healthCheck = await cortexMemService.healthCheck();\n \n return {\n success: true,\n status: healthCheck.status === 'healthy' ? 'healthy' : 'unhealthy',\n timestamp: new Date().toISOString(),\n services: {\n cortex_mem_service: true,\n vector_store: healthCheck.vector_store,\n llm_service: healthCheck.llm_service,\n },\n };\n } catch (error) {\n console.error('健康检查失败 - cortex-mem-service不可用:', error);\n return {\n success: false,\n status: 'unhealthy',\n timestamp: new Date().toISOString(),\n services: {\n cortex_mem_service: false,\n vector_store: false,\n llm_service: false,\n },\n error: {\n code: 'CORTEX_MEM_SERVICE_UNAVAILABLE',\n message: error instanceof Error ? error.message : 'Cortex Memory Service不可用',\n },\n };\n }\n })\n \n // 获取资源使用情况 - 返回真实的资源使用数据\n .get('/resources', () => {\n const totalMem = os.totalmem();\n const freeMem = os.freemem();\n const usedMem = totalMem - freeMem;\n \n // 计算CPU使用率\n const cpus = os.cpus();\n const cpuUsage = cpus.reduce((acc: number, cpu: any) => {\n const times = cpu.times as { user: number; nice: number; sys: number; idle: number; irq: number };\n const total = (Object.values(times) as number[]).reduce((a: number, b: number) => a + b, 0);\n const idle = times.idle;\n return acc + ((total - idle) / total) * 100;\n }, 0) / cpus.length;\n \n return {\n success: true,\n data: {\n memory: {\n total: Math.floor(totalMem / 1024 / 1024), // MB\n used: Math.floor(usedMem / 1024 / 1024), // MB\n free: Math.floor(freeMem / 1024 / 1024), // MB\n percentage: (usedMem / totalMem) * 100,\n },\n cpu: {\n usage: parseFloat(cpuUsage.toFixed(2)),\n cores: cpus.length,\n },\n disk: {\n usage: 0, // 磁盘使用率需要额外的库来获取\n },\n network: {\n active_connections: 0, // 活动连接数需要从应用层统计\n },\n },\n timestamp: new Date().toISOString(),\n };\n })\n \n // 清理系统缓存\n .post('/clear-cache', () => {\n return {\n success: true,\n message: 'System cache cleared successfully',\n timestamp: new Date().toISOString(),\n };\n })\n \n // 重启服务\n .post('/restart', () => {\n return {\n success: true,\n message: 'Service restart initiated',\n timestamp: new Date().toISOString(),\n restart_time: new Date(Date.now() + 5000).toISOString(),\n };\n })\n \n // 错误处理\n .onError(({ code, error }) => {\n console.error('System API error:', error);\n \n const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';\n \n return {\n success: false,\n error: {\n code: code || 'INTERNAL_ERROR',\n message: errorMessage,\n },\n timestamp: new Date().toISOString(),\n };\n });" }, "complexity_metrics": { - "cyclomatic_complexity": 17.0, - "lines_of_code": 326, + "cyclomatic_complexity": 1.0, + "lines_of_code": 355, "number_of_classes": 0, - "number_of_functions": 25 + "number_of_functions": 12 }, "dependencies": [ { - "dependency_type": "environment variable", + "dependency_type": "module", "is_external": true, - "line_number": 2, - "name": "import.meta.env.VITE_API_URL", + "line_number": 1, + "name": "elysia", "path": null, "version": null }, { - "dependency_type": "browser API", + "dependency_type": "package", "is_external": true, - "line_number": 14, - "name": "fetch", - "path": null, + "line_number": 2, + "name": "@elysiajs/cors", + "path": null, + "version": null + }, + { + "dependency_type": "local", + "is_external": false, + "line_number": 3, + "name": "cortex-mem-service", + "path": "../integrations/cortex-mem", + "version": null + }, + { + "dependency_type": "builtin", + "is_external": false, + "line_number": 4, + "name": "os", + "path": null, + "version": null + }, + { + "dependency_type": "builtin", + "is_external": false, + "line_number": 5, + "name": "process", + "path": null, "version": null } ], - "detailed_description": "该组件是前端与后端服务通信的核心 API 客户端,封装了对记忆管理、系统优化及系统监控等功能的 HTTP 请求。通过统一的 `request` 函数处理请求发送、错误解析和异常日志记录,支持开发环境下的代理配置和生产环境的动态 URL 注入。各 API 模块(memory、optimization、system)按功能划分,提供细粒度的操作接口,如记忆的增删改查、搜索、批量操作、导出,优化任务的执行与状态查询,以及系统健康检查、指标获取、日志访问等。同时提供了便捷的工具方法如连接测试和状态聚合,增强了前端调用的健壮性和可维护性。", + "detailed_description": "This component defines a comprehensive set of API endpoints under the '/api/system' prefix for monitoring and managing the health, performance, and operational state of the backend server and its integrated services (Cortex Memory Service, LLM, and vector store). It provides real-time system status by aggregating health information from external dependencies, collects actual system-level metrics (CPU, memory) using Node.js built-in 'os' module, exposes detailed system information (version, uptime, platform), and offers administrative actions such as cache clearing and service restart. Error handling is centralized via Elysia's .onError hook, ensuring consistent error responses across all routes. The API responses follow a standardized structure with 'success', 'data/error', and 'timestamp' fields, enhancing client-side handling and debugging.", "interfaces": [ { - "description": "记忆管理API集合,用于操作用户记忆数据。", - "interface_type": "object", - "name": "memoryApi", + "description": "Represents the overall health status of the system, including vector store and LLM service availability", + "interface_type": "interface", + "name": "SystemStatus", "parameters": [], "return_type": "object", - "visibility": "export" + "visibility": "public" }, { - "description": "系统优化API集合,用于执行和监控记忆优化任务。", - "interface_type": "object", - "name": "optimizationApi", + "description": "Defines the structure for system performance data including CPU, memory, and request metrics", + "interface_type": "interface", + "name": "PerformanceMetrics", "parameters": [], "return_type": "object", - "visibility": "export" + "visibility": "public" }, { - "description": "系统信息与运维API集合,用于获取系统状态和执行管理命令。", - "interface_type": "object", - "name": "systemApi", + "description": "Contains detailed information about the system environment and configuration", + "interface_type": "interface", + "name": "SystemInfo", "parameters": [], "return_type": "object", - "visibility": "export" + "visibility": "public" }, { - "description": "通用工具API集合,提供连接测试和状态聚合功能。", - "interface_type": "object", - "name": "api", + "description": "Defines the structure for log entries with timestamp, level, message, and optional metadata", + "interface_type": "interface", + "name": "LogEntry", "parameters": [], "return_type": "object", - "visibility": "export" + "visibility": "public" } ], "responsibilities": [ - "封装统一的HTTP请求逻辑,处理认证、头部设置和错误解析", - "提供记忆管理相关的增删改查、搜索、统计和导出功能", - "支持系统级优化操作的发起、状态查询、历史获取与取消", - "暴露系统健康检查、性能指标、资源使用、日志等运维接口", - "提供连接测试与聚合状态查询等辅助诊断功能" + "Provide real-time health status of the system and its dependent services (Cortex Memory Service, LLM, vector store)", + "Expose system performance metrics including CPU and memory usage using Node.js OS module", + "Serve detailed system information such as version, uptime, platform, and hardware specs", + "Offer administrative control endpoints for cache clearing and service restart", + "Implement centralized error handling for all system API routes with consistent response formatting" ] }, { "code_dossier": { - "code_purpose": "page", - "description": "Svelte page component for system monitoring dashboard with real-time metrics, logs, and alerts", - "file_path": "cortex-mem-insights/src/routes/monitor/+page.svelte", + "code_purpose": "specificfeature", + "description": "Manages state and operations for memory optimization jobs including running optimizations, tracking job status, loading history, handling cancellation, and computing derived metrics.", + "file_path": "cortex-mem-insights/src/lib/stores/optimization.ts", "functions": [ - "loadPerformanceMetrics", - "measureHealthLatency", - "getQdrantVersion", - "calculateMemoryUsage", - "calculateCpuUsage", - "calculateNetworkStats", - "calculatePerformanceMetrics", - "generateRealtimeLogs", - "generateAlerts", - "updateMetrics", - "toggleAutoRefresh", - "getLevelColor", - "getTrendIcon", - "getTrendColor", - "acknowledgeAlert" + "runOptimization", + "getJobStatus", + "loadHistory", + "cancelOptimization", + "loadStatistics", + "setFilter", + "clearFilters", + "setPagination", + "clearCurrentJob", + "reset" ], "importance_score": 0.8, "interfaces": [ - "ServiceStatus", - "realtimeLogs", - "alerts", - "systemMetrics", - "performanceMetrics", - "autoRefresh" + "optimizationStore", + "optimizationStatsStore", + "optimizationStatus", + "recentOptimizations", + "optimizationMetrics" ], - "name": "+page.svelte", - "source_summary": "\n\n
\n\t\n\t
\n\t\t
\n\t\t\t

系统监控

\n\t\t\t

实时监控系统状态、性能指标和运行日志

\n\t\t
\n\t\t
\n\t\t\t\n\t\t\t\n\t\t\t\t立即刷新\n\t\t\t\n\t\t
\n\t
\n\n\t{#if isLoading}\n\t\t\n\t\t
\n\t\t\t{#each Array(3) as _, i}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each Array(3) as _, j}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\t{:else if error}\n\t\t\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t⚠️\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

加载失败

\n\t\t\t\t\t

{error}

\n\t\t\t\t\t location.reload()}\n\t\t\t\t\t\tclass=\"mt-2 px-4 py-2 bg-red-500 hover:bg-red-600 text-white rounded-lg text-sm font-medium\"\n\t\t\t\t\t>\n\t\t\t\t\t\t重新加载\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t\n\t{:else}\n\t\t\n\t\t
\n\t\t\t\n\t\t\t {\n\t\t\t\t\t// 服务状态由组件内部处理,这里不需要更新外部状态\n\t\t\t\t}}\n\t\t\t/>\n\n\t\t\t\n\t\t\t
\n\t\t\t\t

资源使用

\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t内存使用\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{systemMetrics.memoryUsage.percentage.toFixed(1)}%\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t 80\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-red-500'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: systemMetrics.memoryUsage.percentage > 60\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-yellow-500'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-green-500'\n\t\t\t\t\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\t\t\t\tstyle={`width: ${systemMetrics.memoryUsage.percentage}%`}\n\t\t\t\t\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{systemMetrics.memoryUsage.used.toFixed(1)} MB\n\t\t\t\t\t\t\t\t\t\t\t{systemMetrics.memoryUsage.total} MB\n\t\t\t\t\t\t\t\t\t\t
\t\t\t\t\t
\n\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\tCPU使用\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{systemMetrics.cpuUsage.percentage.toFixed(1)}%\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t 70\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-red-500'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: systemMetrics.cpuUsage.percentage > 40\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-yellow-500'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-green-500'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={`width: ${systemMetrics.cpuUsage.percentage}%`}\n\t\t\t\t\t\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
网络状态
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t活跃连接: {systemMetrics.network.activeConnections}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
吞吐量: {systemMetrics.network.throughput}
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\n\t\t\t\n\t\t\t
\n\t\t\t\t

性能指标

\n\n\t\t\t\t
\n\t\t\t\t\t{#each performanceMetrics as metric}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{metric.name}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{getTrendIcon(metric.trend)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{metric.value.toFixed(0)}{metric.unit}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t metric.threshold * 0.8\n\t\t\t\t\t\t\t\t\t\t\t? 'bg-red-500'\n\t\t\t\t\t\t\t\t\t\t\t: metric.value > metric.threshold * 0.6\n\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-yellow-500'\n\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-green-500'\n\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\tstyle={`width: ${Math.min(metric.value / metric.threshold, 1) * 100}%`}\n\t\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t阈值: {metric.threshold}{metric.unit}\n\t\t\t\t\t\t\t\t使用率: {((metric.value / metric.threshold) * 100).toFixed(1)}%\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

系统告警

\n\t\t\t\t\t\n\t\t\t\t\t\t{alerts.filter((a) => !a.acknowledged).length} 个未处理\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t{#each alerts as alert}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{alert.level === 'error'\n\t\t\t\t\t\t\t\t\t\t\t\t? '错误'\n\t\t\t\t\t\t\t\t\t\t\t\t: alert.level === 'warning'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? '警告'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: '信息'}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{#if !alert.acknowledged}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t未处理\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t{alert.message}\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t{alert.time}\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#if !alert.acknowledged}\n\t\t\t\t\t\t\t\t\t acknowledgeAlert(alert.id)}\n\t\t\t\t\t\t\t\t\t\tclass=\"ml-2 px-3 py-1 text-sm bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t确认\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

实时日志

\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t最后更新: {lastUpdate || '未知'}\n\t\t\t\t\t\t\n\t\t\t\t\t\t (realtimeLogs = [])}\n\t\t\t\t\t\t\tclass=\"px-3 py-1 text-sm bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t清空\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{#if realtimeLogs.length === 0}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t暂无日志\n\t\t\t\t\t\t
\n\t\t\t\t\t{:else}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each realtimeLogs as log}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{log.time}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{log.level === 'error' ? 'ERR' : log.level === 'warning' ? 'WARN' : 'INFO'}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{log.message}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t
\n\t\t\t

监控工具

\n\n\t\t\t
\n\t\t\t\t console.log('健康检查')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t❤️\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

健康检查

\n\t\t\t\t\t\t\t

全面检查系统健康状态

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('性能测试')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

性能测试

\n\t\t\t\t\t\t\t

运行性能基准测试

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('诊断工具')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t🔧\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

诊断工具

\n\t\t\t\t\t\t\t

系统问题诊断和修复

\n\t\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t{/if}\n\n" + "name": "optimization.ts", + "source_summary": "import { writable, derived } from 'svelte/store';\nimport { optimizationApi } from '../api/client';\nimport type { OptimizationResult, OptimizationHistory } from '../../server/api/types';\n\n// 优化状态\ninterface OptimizationState {\n currentJob: OptimizationResult | null;\n history: OptimizationHistory[];\n loading: boolean;\n error: string | null;\n filters: {\n status?: string;\n start_date?: string;\n end_date?: string;\n };\n pagination: {\n page: number;\n limit: number;\n total: number;\n };\n}\n\n// 初始状态\nconst initialState: OptimizationState = {\n currentJob: null,\n history: [],\n loading: false,\n error: null,\n filters: {\n status: undefined,\n start_date: undefined,\n end_date: undefined,\n },\n pagination: {\n page: 1,\n limit: 20,\n total: 0,\n },\n};\n\n// 创建store\nfunction createOptimizationStore() {\n const { subscribe, set, update } = writable(initialState);\n\n return {\n subscribe,\n \n // 执行优化\n runOptimization: async (params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await optimizationApi.optimize(params);\n \n update(state => ({\n ...state,\n currentJob: response.data,\n loading: false,\n }));\n \n return { success: true, jobId: response.data?.job_id };\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to run optimization',\n }));\n \n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to run optimization',\n };\n }\n },\n \n // 获取优化状态\n getJobStatus: async (jobId: string) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await optimizationApi.getStatus(jobId);\n \n update(state => ({\n ...state,\n currentJob: response.data,\n loading: false,\n }));\n \n return { success: true, job: response.data };\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to get job status',\n }));\n \n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get job status',\n };\n }\n },\n \n // 加载优化历史\n loadHistory: async (params?: {\n page?: number;\n limit?: number;\n status?: string;\n start_date?: string;\n end_date?: string;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await optimizationApi.history({\n limit: params?.limit || initialState.pagination.limit,\n offset: ((params?.page || 1) - 1) * (params?.limit || initialState.pagination.limit),\n status: params?.status,\n start_date: params?.start_date,\n end_date: params?.end_date,\n });\n \n update(state => ({\n ...state,\n history: response.data?.history || [],\n loading: false,\n filters: {\n ...state.filters,\n status: params?.status,\n start_date: params?.start_date,\n end_date: params?.end_date,\n },\n pagination: {\n ...state.pagination,\n page: params?.page || 1,\n limit: params?.limit || state.pagination.limit,\n total: response.data?.total || 0,\n },\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load optimization history',\n }));\n }\n },\n \n // 取消优化\n cancelOptimization: async (jobId: string) => {\n try {\n await optimizationApi.cancel(jobId);\n \n update(state => {\n if (state.currentJob?.job_id === jobId) {\n return {\n ...state,\n currentJob: {\n ...state.currentJob,\n status: 'failed',\n message: 'Optimization cancelled by user',\n },\n };\n }\n \n return {\n ...state,\n history: state.history.map(job => \n job.job_id === jobId \n ? { ...job, status: 'cancelled' }\n : job\n ),\n };\n });\n \n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to cancel optimization',\n };\n }\n },\n \n // 获取优化统计\n loadStatistics: async () => {\n try {\n const response = await optimizationApi.statistics();\n return { success: true, statistics: response.data };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to load optimization statistics',\n };\n }\n },\n \n // 设置过滤器\n setFilter: (filter: keyof OptimizationState['filters'], value: string | undefined) => {\n update(state => ({\n ...state,\n filters: {\n ...state.filters,\n [filter]: value,\n },\n }));\n },\n \n // 清除过滤器\n clearFilters: () => {\n update(state => ({\n ...state,\n filters: initialState.filters,\n }));\n },\n \n // 设置分页\n setPagination: (page: number, limit?: number) => {\n update(state => ({\n ...state,\n pagination: {\n ...state.pagination,\n page,\n limit: limit || state.pagination.limit,\n },\n }));\n },\n \n // 清除当前任务\n clearCurrentJob: () => {\n update(state => ({\n ...state,\n currentJob: null,\n }));\n },\n \n // 重置状态\n reset: () => {\n set(initialState);\n },\n };\n}\n\nexport const optimizationStore = createOptimizationStore();\n\n// 优化统计状态\ninterface OptimizationStatsState {\n statistics: {\n total_jobs: number;\n successful_jobs: number;\n failed_jobs: number;\n cancelled_jobs: number;\n total_memories_processed: number;\n total_memories_deduplicated: number;\n total_memories_merged: number;\n total_memories_enhanced: number;\n avg_duration: number;\n last_run: string | null;\n } | null;\n loading: boolean;\n error: string | null;\n}\n\nfunction createOptimizationStatsStore() {\n const { subscribe, set, update } = writable({\n statistics: null,\n loading: false,\n error: null,\n });\n\n return {\n subscribe,\n \n // 加载统计信息\n loadStatistics: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await optimizationApi.statistics();\n update(state => ({ ...state, statistics: response.data, loading: false }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load optimization statistics',\n }));\n }\n },\n \n // 清除状态\n clear: () => {\n set({ statistics: null, loading: false, error: null });\n },\n };\n}\n\nexport const optimizationStatsStore = createOptimizationStatsStore();\n\n// 导出派生store\nexport const optimizationStatus = derived(optimizationStore, ($optimization) => {\n if (!$optimization.currentJob) return null;\n \n return {\n jobId: $optimization.currentJob.job_id,\n status: $optimization.currentJob.status,\n progress: $optimization.currentJob.processed_memories / $optimization.currentJob.total_memories * 100,\n message: $optimization.currentJob.message,\n duration: $optimization.currentJob.duration,\n };\n});\n\nexport const recentOptimizations = derived(optimizationStore, ($optimization) => {\n return $optimization.history\n .sort((a, b) => new Date(b.start_time).getTime() - new Date(a.start_time).getTime())\n .slice(0, 10);\n});\n\nexport const optimizationMetrics = derived(optimizationStatsStore, ($stats) => {\n if (!$stats.statistics) return null;\n \n return {\n successRate: $stats.statistics.total_jobs > 0 \n ? ($stats.statistics.successful_jobs / $stats.statistics.total_jobs) * 100 \n : 0,\n avgMemoriesPerJob: $stats.statistics.total_jobs > 0\n ? $stats.statistics.total_memories_processed / $stats.statistics.total_jobs\n : 0,\n deduplicationRate: $stats.statistics.total_memories_processed > 0\n ? ($stats.statistics.total_memories_deduplicated / $stats.statistics.total_memories_processed) * 100\n : 0,\n mergeRate: $stats.statistics.total_memories_processed > 0\n ? ($stats.statistics.total_memories_merged / $stats.statistics.total_memories_processed) * 100\n : 0,\n enhancementRate: $stats.statistics.total_memories_processed > 0\n ? ($stats.statistics.total_memories_enhanced / $stats.statistics.total_memories_processed) * 100\n : 0,\n };\n});" }, "complexity_metrics": { - "cyclomatic_complexity": 33.0, - "lines_of_code": 790, + "cyclomatic_complexity": 4.0, + "lines_of_code": 347, "number_of_classes": 0, - "number_of_functions": 15 + "number_of_functions": 14 }, "dependencies": [ { - "dependency_type": "framework", + "dependency_type": "import", "is_external": true, - "line_number": null, - "name": "svelte", - "path": "svelte", + "line_number": 1, + "name": "writable", + "path": "svelte/store", "version": null }, { - "dependency_type": "service", + "dependency_type": "import", + "is_external": true, + "line_number": 1, + "name": "derived", + "path": "svelte/store", + "version": null + }, + { + "dependency_type": "import", "is_external": false, - "line_number": null, - "name": "api", - "path": "$lib/api/client", + "line_number": 2, + "name": "optimizationApi", + "path": "../api/client", "version": null }, { - "dependency_type": "component", + "dependency_type": "type_import", "is_external": false, - "line_number": null, - "name": "ServiceStatus", - "path": "$lib/components/ServiceStatus.svelte", + "line_number": 3, + "name": "OptimizationResult", + "path": "../../server/api/types", + "version": null + }, + { + "dependency_type": "type_import", + "is_external": false, + "line_number": 3, + "name": "OptimizationHistory", + "path": "../../server/api/types", "version": null } ], - "detailed_description": "This Svelte page component implements a comprehensive system monitoring dashboard that displays real-time system metrics, performance indicators, logs, and alerts. The component fetches data from the API to monitor memory usage, CPU utilization, network statistics, and various performance metrics. It features automatic refresh functionality, visual indicators for system health, and interactive elements for acknowledging alerts and clearing logs. The component integrates with the ServiceStatus subcomponent to display service health information and provides a user interface for monitoring the overall system status.", + "detailed_description": "This component implements a Svelte store for managing memory optimization workflows in a frontend application. It handles core operations such as initiating optimization jobs via `runOptimization`, retrieving job status, loading historical records with pagination and filtering support, and allowing job cancellation. The store maintains both current job state and optimization history, with comprehensive error handling and loading states. Two primary stores are created: `optimizationStore` for job management and `optimizationStatsStore` for statistics. Several derived stores (`optimizationStatus`, `recentOptimizations`, `optimizationMetrics`) provide computed views of the data for UI consumption. All operations are implemented asynchronously with proper state updates using Svelte's writable store pattern, ensuring reactivity throughout the application.", "interfaces": [ { - "description": null, - "interface_type": "component", - "name": "ServiceStatus", - "parameters": [], - "return_type": "void", - "visibility": "public" - }, - { - "description": null, - "interface_type": "variable", - "name": "realtimeLogs", + "description": "Main store for optimization job management with methods to control the optimization workflow", + "interface_type": "store", + "name": "optimizationStore", "parameters": [], - "return_type": "Array<{ time: string; level: string; message: string }>", + "return_type": "Readable & { runOptimization: Function; getJobStatus: Function; loadHistory: Function; cancelOptimization: Function; loadStatistics: Function; setFilter: Function; clearFilters: Function; setPagination: Function; clearCurrentJob: Function; reset: Function }", "visibility": "public" }, { - "description": null, - "interface_type": "variable", - "name": "alerts", + "description": "Store dedicated to optimization statistics with loading and clearing functionality", + "interface_type": "store", + "name": "optimizationStatsStore", "parameters": [], - "return_type": "Array<{ id: string; level: string; message: string; time: string; acknowledged: boolean }>", + "return_type": "Readable & { loadStatistics: Function; clear: Function }", "visibility": "public" }, { - "description": null, - "interface_type": "variable", - "name": "systemMetrics", + "description": "Derived store computing current optimization job status and progress percentage", + "interface_type": "derived", + "name": "optimizationStatus", "parameters": [], - "return_type": "{ memoryUsage: { used: number, total: number, percentage: number }, cpuUsage: { percentage: number }, network: { activeConnections: number, throughput: string } }", + "return_type": "Readable<{ jobId: string; status: string; progress: number; message: string; duration: number } | null>", "visibility": "public" }, { - "description": null, - "interface_type": "variable", - "name": "performanceMetrics", + "description": "Derived store providing sorted list of 10 most recent optimization jobs", + "interface_type": "derived", + "name": "recentOptimizations", "parameters": [], - "return_type": "Array<{ name: string; value: number; unit: string; trend: string; threshold: number }>", + "return_type": "Readable", "visibility": "public" }, { - "description": null, - "interface_type": "variable", - "name": "autoRefresh", + "description": "Derived store calculating key performance metrics from optimization statistics", + "interface_type": "derived", + "name": "optimizationMetrics", "parameters": [], - "return_type": "boolean", + "return_type": "Readable<{ successRate: number; avgMemoriesPerJob: number; deduplicationRate: number; mergeRate: number; enhancementRate: number } | null>", "visibility": "public" } ], "responsibilities": [ - "Managing the system monitoring dashboard lifecycle and state", - "Fetching and calculating system performance metrics from API data", - "Generating real-time logs and alerts based on system metrics", - "Providing UI controls for auto-refresh and manual updates", - "Displaying system status, resource usage, and performance indicators" + "Managing the state of active and historical memory optimization jobs", + "Providing reactive interfaces for running, monitoring, and canceling optimization tasks", + "Handling pagination, filtering, and sorting of optimization history", + "Maintaining and exposing derived metrics and statistics for optimization performance", + "Synchronizing frontend state with backend optimization API through error-resilient requests" ] }, { "code_dossier": { - "code_purpose": "page", - "description": "Dashboard page for monitoring and analyzing Cortex Memory system status, displaying statistics, system health, and recent memories.", - "file_path": "cortex-mem-insights/src/routes/+page.svelte", + "code_purpose": "widget", + "description": "Svelte stores for managing memory data state including list, detail, and statistics. Provides reactive state management for memory operations like loading, searching, filtering, pagination, deletion, and updates.", + "file_path": "cortex-mem-insights/src/lib/stores/memory.ts", "functions": [ - "loadBasicData", - "calculateQualityDistribution", - "calculateImportanceScore", - "fallbackToMockData", - "formatImportance", - "getImportanceColor", - "formatDate" + "createMemoryStore", + "createMemoryDetailStore", + "createMemoryStatsStore" ], "importance_score": 0.8, "interfaces": [ - "on:statusUpdate" + "memoryStore", + "memoryDetailStore", + "memoryStatsStore", + "memoryTypes", + "topUsers", + "topAgents" ], - "name": "+page.svelte", - "source_summary": "\n\n
\n\t\n\t
\n\t\t

仪表盘

\n\t\t

监控和分析 Cortex Memory 系统的运行状态

\n\t
\n\n\t{#if isLoading}\n\t\t\n\t\t
\n\t\t\t{#each Array(4) as _, i}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\t{:else}\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

总记忆数

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{stats.totalMemories.toLocaleString()}\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t📚\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t

\n\t\t\t\t\t高质量记忆: {stats.qualityDistribution.high}\n\t\t\t\t

\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

平均质量

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{(stats.averageQuality * 100).toFixed(1)}%\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

质量分布

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{stats.qualityDistribution.high}/{stats.qualityDistribution.medium}/{stats\n\t\t\t\t\t\t\t\t.qualityDistribution.low}\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t📊\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t

高/中/低质量记忆数量

\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t {\n\t\t\t\t\t\tsystemStatus = event.detail.systemStatus;\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

最近记忆

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t查看全部 →\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each recentMemories as memory}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{formatImportance(memory.importance)}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{memory.type}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\t{memory.content}\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\tID: {memory.id}\n\t\t\t\t\t\t\t\t\t\t\t{memory.createdAt}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t console.log('查看详情', memory.id)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t🔍\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t
\n\t\t\t

快速操作

\n\n\t\t\t
\n\t\t\t\t console.log('运行优化')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

运行优化

\n\t\t\t\t\t\t\t

清理重复和低质量记忆

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('导出数据')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t📥\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

导出数据

\n\t\t\t\t\t\t\t

导出记忆为JSON/CSV格式

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('查看报告')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t📊\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

生成报告

\n\t\t\t\t\t\t\t

生成系统运行分析报告

\n\t\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t{/if}\n\n\n\n" + "name": "memory.ts", + "source_summary": "import { writable, derived } from 'svelte/store';\nimport { memoryApi } from '../api/client';\nimport type { MemoryResponse, SearchResponse, ListResponse } from '../../server/api/types';\n\n// 记忆列表状态\ninterface MemoryListState {\n memories: MemoryResponse[];\n total: number;\n loading: boolean;\n error: string | null;\n filters: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n search_query?: string;\n };\n pagination: {\n page: number;\n limit: number;\n total_pages: number;\n };\n}\n\n// 初始状态\nconst initialState: MemoryListState = {\n memories: [],\n total: 0,\n loading: false,\n error: null,\n filters: {\n user_id: undefined,\n agent_id: undefined,\n run_id: undefined,\n actor_id: undefined,\n memory_type: undefined,\n search_query: undefined,\n },\n pagination: {\n page: 1,\n limit: 20,\n total_pages: 1,\n },\n};\n\n// 创建store\nfunction createMemoryStore() {\n const { subscribe, set, update } = writable(initialState);\n\n return {\n subscribe,\n \n // 加载记忆列表\n loadMemories: async (params?: {\n page?: number;\n limit?: number;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await memoryApi.list({\n ...params,\n limit: params?.limit || initialState.pagination.limit,\n }) as ListResponse;\n \n update(state => ({\n ...state,\n memories: response.memories,\n total: response.total,\n loading: false,\n filters: {\n ...state.filters,\n user_id: params?.user_id,\n agent_id: params?.agent_id,\n run_id: params?.run_id,\n actor_id: params?.actor_id,\n memory_type: params?.memory_type,\n },\n pagination: {\n ...state.pagination,\n page: params?.page || 1,\n limit: params?.limit || state.pagination.limit,\n total_pages: Math.ceil(response.total / (params?.limit || state.pagination.limit)),\n },\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load memories',\n }));\n }\n },\n \n // 搜索记忆\n searchMemories: async (query: string, params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await memoryApi.search(query, params) as SearchResponse;\n \n update(state => ({\n ...state,\n memories: response.results.map(r => r.memory),\n total: response.total,\n loading: false,\n filters: {\n ...state.filters,\n user_id: params?.user_id,\n agent_id: params?.agent_id,\n run_id: params?.run_id,\n actor_id: params?.actor_id,\n memory_type: params?.memory_type,\n search_query: query,\n },\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to search memories',\n }));\n }\n },\n \n // 清除搜索\n clearSearch: () => {\n update(state => ({\n ...state,\n filters: {\n ...state.filters,\n search_query: undefined,\n },\n }));\n },\n \n // 设置过滤器\n setFilter: (filter: keyof MemoryListState['filters'], value: string | undefined) => {\n update(state => ({\n ...state,\n filters: {\n ...state.filters,\n [filter]: value,\n },\n }));\n },\n \n // 清除所有过滤器\n clearFilters: () => {\n update(state => ({\n ...state,\n filters: initialState.filters,\n }));\n },\n \n // 设置分页\n setPagination: (page: number, limit?: number) => {\n update(state => ({\n ...state,\n pagination: {\n ...state.pagination,\n page,\n limit: limit || state.pagination.limit,\n },\n }));\n },\n \n // 删除记忆\n deleteMemory: async (id: string) => {\n try {\n await memoryApi.delete(id);\n \n update(state => ({\n ...state,\n memories: state.memories.filter(memory => memory.id !== id),\n total: state.total - 1,\n }));\n \n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to delete memory',\n };\n }\n },\n \n // 批量删除\n batchDelete: async (ids: string[]) => {\n try {\n await memoryApi.batchDelete(ids);\n \n update(state => ({\n ...state,\n memories: state.memories.filter(memory => !ids.includes(memory.id)),\n total: state.total - ids.length,\n }));\n \n return { success: true, deleted: ids.length };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to delete memories',\n };\n }\n },\n \n // 重置状态\n reset: () => {\n set(initialState);\n },\n };\n}\n\nexport const memoryStore = createMemoryStore();\n\n// 单个记忆状态\ninterface MemoryDetailState {\n memory: MemoryResponse | null;\n loading: boolean;\n error: string | null;\n}\n\nfunction createMemoryDetailStore() {\n const { subscribe, set, update } = writable({\n memory: null,\n loading: false,\n error: null,\n });\n\n return {\n subscribe,\n \n // 加载记忆详情\n loadMemory: async (id: string) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const memory = await memoryApi.get(id) as MemoryResponse;\n update(state => ({ ...state, memory, loading: false }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load memory',\n }));\n }\n },\n \n // 更新记忆\n updateMemory: async (id: string, content: string) => {\n try {\n await memoryApi.update(id, content);\n \n update(state => {\n if (state.memory?.id === id) {\n return {\n ...state,\n memory: {\n ...state.memory,\n content,\n updated_at: new Date().toISOString(),\n },\n };\n }\n return state;\n });\n \n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to update memory',\n };\n }\n },\n \n // 清除状态\n clear: () => {\n set({ memory: null, loading: false, error: null });\n },\n };\n}\n\nexport const memoryDetailStore = createMemoryDetailStore();\n\n// 记忆统计状态\ninterface MemoryStatsState {\n statistics: {\n total_memories: number;\n by_type: Record;\n by_user: Record;\n by_agent: Record;\n recent_activity: Array<{ date: string; count: number }>;\n } | null;\n loading: boolean;\n error: string | null;\n}\n\nfunction createMemoryStatsStore() {\n const { subscribe, set, update } = writable({\n statistics: null,\n loading: false,\n error: null,\n });\n\n return {\n subscribe,\n \n // 加载统计信息\n loadStatistics: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const statistics = await memoryApi.statistics();\n update(state => ({ ...state, statistics, loading: false }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load statistics',\n }));\n }\n },\n \n // 清除状态\n clear: () => {\n set({ statistics: null, loading: false, error: null });\n },\n };\n}\n\nexport const memoryStatsStore = createMemoryStatsStore();\n\n// 导出派生store\nexport const memoryTypes = derived(memoryStatsStore, ($stats) => {\n if (!$stats.statistics) return [];\n \n return Object.entries($stats.statistics.by_type)\n .map(([type, count]) => ({ type, count }))\n .sort((a, b) => b.count - a.count);\n});\n\nexport const topUsers = derived(memoryStatsStore, ($stats) => {\n if (!$stats.statistics) return [];\n \n return Object.entries($stats.statistics.by_user)\n .map(([user, count]) => ({ user, count }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 10);\n});\n\nexport const topAgents = derived(memoryStatsStore, ($stats) => {\n if (!$stats.statistics) return [];\n \n return Object.entries($stats.statistics.by_agent)\n .map(([agent, count]) => ({ agent, count }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 10);\n});" }, "complexity_metrics": { - "cyclomatic_complexity": 17.0, - "lines_of_code": 510, + "cyclomatic_complexity": 5.0, + "lines_of_code": 374, "number_of_classes": 0, - "number_of_functions": 7 + "number_of_functions": 3 }, "dependencies": [ { - "dependency_type": "framework", + "dependency_type": "import", "is_external": true, "line_number": 1, - "name": "svelte", - "path": null, + "name": "svelte/store", + "path": "svelte/store", "version": null }, { - "dependency_type": "internal", + "dependency_type": "import", "is_external": false, "line_number": 2, - "name": "$lib/api/client", - "path": "$lib/api/client", + "name": "memoryApi", + "path": "../api/client", "version": null }, { - "dependency_type": "internal", + "dependency_type": "import", "is_external": false, "line_number": 3, - "name": "$lib/components/ServiceStatus.svelte", - "path": "$lib/components/ServiceStatus.svelte", + "name": "MemoryResponse, SearchResponse, ListResponse", + "path": "../../server/api/types", "version": null } ], - "detailed_description": "This Svelte component serves as the main dashboard page for the Cortex Memory Insights application. It displays key metrics including total memories, average quality, and quality distribution. The page shows real-time system status for core services (Cortex Mem Service, Qdrant, LLM Service) via the ServiceStatus component and lists recent memories with importance scoring. Data is loaded on mount with error handling that falls back to mock data. The UI includes loading states, statistics cards, system health monitoring, recent memory list, and quick action buttons for optimization, export, and reporting. The component handles asynchronous data loading, error recovery, and provides visual feedback throughout.", + "detailed_description": "This component implements three Svelte stores (memoryStore, memoryDetailStore, memoryStatsStore) to manage the state of memory data in a reactive frontend application. The memoryStore handles the list of memories with support for filtering, pagination, searching, and batch operations. The memoryDetailStore manages the state of a single memory item for viewing and editing. The memoryStatsStore provides aggregate statistics about memories. Additionally, derived stores (memoryTypes, topUsers, topAgents) transform and expose statistics data for UI consumption. All operations are implemented asynchronously with proper error handling and loading states, making it suitable for integration with API calls through the injected memoryApi service.", "interfaces": [ { - "description": "Event handler for receiving system status updates from ServiceStatus component", - "interface_type": "event", - "name": "on:statusUpdate", - "parameters": [ - { - "description": "Event containing updated system status information", - "is_optional": false, - "name": "event", - "param_type": "CustomEvent" - } - ], - "return_type": null, - "visibility": "public" + "description": "Primary store for managing memory list state with operations for loading, searching, filtering, and deleting memories", + "interface_type": "store", + "name": "memoryStore", + "parameters": [], + "return_type": "Readable & { loadMemories, searchMemories, clearSearch, setFilter, clearFilters, setPagination, deleteMemory, batchDelete, reset }", + "visibility": "export" }, { - "description": "Loads basic memory data and initializes system status", - "interface_type": "function", - "name": "loadBasicData", + "description": "Store for managing individual memory detail state with operations for loading and updating a single memory", + "interface_type": "store", + "name": "memoryDetailStore", "parameters": [], - "return_type": "Promise", - "visibility": "private" + "return_type": "Readable & { loadMemory, updateMemory, clear }", + "visibility": "export" }, { - "description": "Calculates quality distribution statistics from memory data", - "interface_type": "function", - "name": "calculateQualityDistribution", - "parameters": [ - { - "description": "Array of memory objects to analyze", - "is_optional": false, - "name": "memories", - "param_type": "any[]" - } - ], - "return_type": "object", - "visibility": "private" - }, - { - "description": "Calculates importance score for a memory based on type, role, and metadata", - "interface_type": "function", - "name": "calculateImportanceScore", - "parameters": [ - { - "description": "Memory object to score", - "is_optional": false, - "name": "memory", - "param_type": "any" - } - ], - "return_type": "number", - "visibility": "private" - }, - { - "description": "Provides default/mock data when real data loading fails", - "interface_type": "function", - "name": "fallbackToMockData", + "description": "Store for managing memory statistics state with operation to load aggregate data", + "interface_type": "store", + "name": "memoryStatsStore", "parameters": [], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Formats numerical importance score as human-readable text", - "interface_type": "function", - "name": "formatImportance", - "parameters": [ - { - "description": "Numerical importance score", - "is_optional": false, - "name": "importance", - "param_type": "number" - } - ], - "return_type": "string", - "visibility": "private" - }, - { - "description": "Returns CSS classes for coloring importance badges based on score", - "interface_type": "function", - "name": "getImportanceColor", - "parameters": [ - { - "description": "Numerical importance score", - "is_optional": false, - "name": "importance", - "param_type": "number" - } - ], - "return_type": "string", - "visibility": "private" - }, - { - "description": "Formats ISO date string to localized Chinese format", - "interface_type": "function", - "name": "formatDate", - "parameters": [ - { - "description": "ISO date string to format", - "is_optional": false, - "name": "isoString", - "param_type": "string" - } - ], - "return_type": "string", - "visibility": "private" + "return_type": "Readable & { loadStatistics, clear }", + "visibility": "export" } ], "responsibilities": [ - "Orchestrating the dashboard UI layout and state management", - "Loading and processing memory statistics and system status data", - "Handling data loading errors with fallback to mock data", - "Calculating memory quality metrics and importance scores", - "Coordinating with ServiceStatus component for service health monitoring" + "Manage reactive state for memory list with filtering, pagination and search capabilities", + "Handle CRUD operations for memory entities through API integration", + "Maintain state for individual memory details and support real-time updates", + "Provide aggregated memory statistics and derived data for visualization", + "Implement proper error handling and loading states for all asynchronous operations" ] }, { "code_dossier": { - "code_purpose": "page", - "description": "Svelte page component for memory optimization interface in Cortex-Mem Insights system", - "file_path": "cortex-mem-insights/src/routes/optimization/+page.svelte", + "code_purpose": "widget", + "description": "Svelte stores for managing system status, application connectivity, and UI theme state in a frontend monitoring dashboard.", + "file_path": "cortex-mem-insights/src/lib/stores/system.ts", "functions": [ - "loadOptimizationData", - "startOptimization", - "startPolling", - "stopPolling", - "cancelOptimization", - "getEstimatedImpact", - "getStatusColor", - "getSeverityColor" + "createSystemStore", + "createAppStore", + "createThemeStore" ], "importance_score": 0.8, "interfaces": [ - "onMount", - "optimizationApi.history", - "optimizationApi.analyze", - "optimizationApi.optimize", - "optimizationApi.getStatus", - "optimizationApi.cancel" + "loadStatus", + "loadMetrics", + "loadInfo", + "loadLogs", + "loadResources", + "clearCache", + "restartService", + "refreshAll", + "reset", + "checkConnection", + "setConnected", + "setError", + "toggleDarkMode", + "toggleSidebar", + "loadSettings" ], - "name": "+page.svelte", - "source_summary": "\n\n
\n \n
\n

优化面板

\n

\n 检测和优化记忆数据,提升系统性能和信息密度\n

\n
\n\n \n {#if errorMessage}\n
\n
\n \n \n \n {errorMessage}\n \n
\n
\n {/if}\n\n {#if isLoading}\n \n
\n
\n
\n
\n
\n
\n {#each Array(2) as _, i}\n
\n
\n
\n {#each Array(3) as _, j}\n
\n {/each}\n
\n
\n {/each}\n
\n
\n {:else}\n \n
\n

优化控制

\n \n
\n \n
\n

优化策略

\n
\n {#each strategies as strategy}\n \n {/each}\n
\n
\n \n \n
\n

优化选项

\n
\n \n \n \n \n
\n
超时时间
\n
\n \n \n {timeoutMinutes} 分钟\n \n
\n
\n
\n
\n \n \n
\n

预估影响

\n
\n
\n 预计影响记忆:\n \n ~{getEstimatedImpact()} 条\n \n
\n
\n 预计节省空间:\n \n ~{(getEstimatedImpact() * 0.15).toFixed(1)}MB\n \n
\n
\n 预计提升质量:\n \n +{aggressiveMode ? '15' : '10'}%\n \n
\n
\n
\n {previewMode ? '预览模式不会实际修改数据' : '优化将永久修改记忆数据'}\n
\n
\n
\n \n \n
\n {#if isOptimizing}\n \n 取消优化\n \n {:else}\n \n {previewMode ? '分析问题' : '开始优化'}\n \n {/if}\n \n console.log('导出报告')}\n class=\"w-full px-4 py-3 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg font-medium transition-colors duration-200\"\n >\n 导出优化报告\n \n
\n
\n
\n
\n\n \n {#if isOptimizing}\n
\n

优化进度

\n \n
\n \n
\n
\n \n {optimizationStatus === 'analyzing' ? '分析问题中...' :\n optimizationStatus === 'executing' ? '执行优化中...' :\n optimizationStatus === 'completed' ? '优化完成' : '优化失败'}\n \n \n {optimizationProgress}%\n \n
\n
\n
\n
\n
\n \n \n
\n
\n
当前阶段
\n
\n {optimizationStatus === 'analyzing' ? '问题分析' :\n optimizationStatus === 'executing' ? '执行优化' :\n optimizationStatus === 'completed' ? '完成' : '失败'}\n
\n
\n \n
\n
已处理记忆
\n
\n {Math.floor(optimizationProgress * 1.5)} 条\n
\n
\n \n
\n
预计剩余时间
\n
\n {Math.max(0, Math.floor((100 - optimizationProgress) * 0.3))} 分钟\n
\n
\n
\n \n \n
\n
实时日志
\n
\n {#each Array(Math.floor(optimizationProgress / 10)) as _, i}\n
\n [{new Date(Date.now() - (10 - i) * 1000).toLocaleTimeString('zh-CN', {hour12: false})}] \n {optimizationStatus === 'analyzing' ? '分析记忆 #' + (i * 10 + 1) + '...' :\n '优化记忆 #' + (i * 10 + 1) + '...'}\n
\n {/each}\n
\n
\n
\n
\n {/if}\n\n \n
\n
\n

检测到的问题

\n console.log('重新检测')}\n class=\"px-4 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg text-sm font-medium\"\n >\n 重新检测\n \n
\n \n
\n {#each detectedIssues as issue}\n
\n
\n \n {issue.severity === 'high' ? '高' : issue.severity === 'medium' ? '中' : '低'}\n \n \n {issue.count}\n \n
\n
\n {issue.type}\n
\n
\n {issue.description}\n
\n
\n console.log('查看详情', issue.type)}\n class=\"w-full px-3 py-1 text-sm bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded\"\n >\n 查看详情\n \n
\n
\n {/each}\n
\n
\n\n \n
\n

优化历史

\n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {#each optimizationHistory as record}\n \n \n \n \n \n \n \n \n \n \n {/each}\n \n
\n 优化ID\n \n 策略\n \n 状态\n \n 开始时间\n \n 耗时\n \n 影响记忆\n \n 节省空间\n \n 操作\n
\n
\n {record.id}\n
\n
\n
\n {record.strategy}\n
\n
\n \n {record.status === 'completed' ? '完成' : \n record.status === 'running' ? '进行中' : '失败'}\n \n \n {record.startedAt}\n \n {record.duration}\n \n
\n {record.memoriesAffected}\n
\n
\n
\n {record.spaceSaved}\n
\n
\n
\n console.log('查看报告', record.id)}\n class=\"text-sm text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300\"\n >\n 报告\n \n {#if record.status === 'completed'}\n console.log('撤销', record.id)}\n class=\"text-sm text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300\"\n >\n 撤销\n \n {/if}\n
\n
\n
\n \n
\n
\n
\n 共 {optimizationHistory.length} 次优化记录\n
\n console.log('清空历史')}\n class=\"px-4 py-2 text-sm bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg\"\n >\n 清空历史记录\n \n
\n
\n
\n {/if}\n" + "name": "system.ts", + "source_summary": "import { writable, derived } from 'svelte/store';\nimport { systemApi } from '../api/client';\nimport type { SystemStatus, PerformanceMetrics, SystemInfo, LogEntry } from '../../server/api/types';\n\n// 系统状态\ninterface SystemState {\n status: SystemStatus | null;\n metrics: PerformanceMetrics | null;\n info: SystemInfo | null;\n logs: LogEntry[];\n loading: boolean;\n error: string | null;\n lastUpdated: string | null;\n}\n\n// 初始状态\nconst initialState: SystemState = {\n status: null,\n metrics: null,\n info: null,\n logs: [],\n loading: false,\n error: null,\n lastUpdated: null,\n};\n\n// 创建store\nfunction createSystemStore() {\n const { subscribe, set, update } = writable(initialState);\n\n return {\n subscribe,\n \n // 加载系统状态\n loadStatus: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.status();\n update(state => ({ \n ...state, \n status: response.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load system status',\n }));\n }\n },\n \n // 加载性能指标\n loadMetrics: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.metrics();\n update(state => ({ \n ...state, \n metrics: response.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load performance metrics',\n }));\n }\n },\n \n // 加载系统信息\n loadInfo: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.info();\n update(state => ({ \n ...state, \n info: response.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load system info',\n }));\n }\n },\n \n // 加载日志\n loadLogs: async (params?: {\n limit?: number;\n level?: string;\n source?: string;\n }) => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.logs(params);\n update(state => ({ \n ...state, \n logs: response.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to load logs',\n }));\n }\n },\n \n // 加载资源使用情况\n loadResources: async () => {\n try {\n const response = await systemApi.resources();\n return { success: true, resources: response.data };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to load resources',\n };\n }\n },\n \n // 清理缓存\n clearCache: async () => {\n try {\n await systemApi.clearCache();\n return { success: true, message: 'Cache cleared successfully' };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to clear cache',\n };\n }\n },\n \n // 重启服务\n restartService: async () => {\n try {\n await systemApi.restart();\n return { success: true, message: 'Service restart initiated' };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to restart service',\n };\n }\n },\n \n // 刷新所有数据\n refreshAll: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const [status, metrics, info, logs] = await Promise.all([\n systemApi.status(),\n systemApi.metrics(),\n systemApi.info(),\n systemApi.logs({ limit: 50 }),\n ]);\n \n update(state => ({\n ...state,\n status: status.data,\n metrics: metrics.data,\n info: info.data,\n logs: logs.data,\n loading: false,\n lastUpdated: new Date().toISOString(),\n }));\n } catch (error) {\n update(state => ({\n ...state,\n loading: false,\n error: error instanceof Error ? error.message : 'Failed to refresh system data',\n }));\n }\n },\n \n // 重置状态\n reset: () => {\n set(initialState);\n },\n };\n}\n\nexport const systemStore = createSystemStore();\n\n// 应用状态\ninterface AppState {\n connected: boolean;\n loading: boolean;\n error: string | null;\n lastConnectionCheck: string | null;\n}\n\nfunction createAppStore() {\n const { subscribe, set, update } = writable({\n connected: false,\n loading: false,\n error: null,\n lastConnectionCheck: null,\n });\n\n return {\n subscribe,\n \n // 检查连接\n checkConnection: async () => {\n update(state => ({ ...state, loading: true, error: null }));\n \n try {\n const response = await systemApi.health();\n \n update(state => ({\n ...state,\n connected: response.status === 'healthy',\n loading: false,\n lastConnectionCheck: new Date().toISOString(),\n }));\n \n return { success: true, connected: response.status === 'healthy' };\n } catch (error) {\n update(state => ({\n ...state,\n connected: false,\n loading: false,\n error: error instanceof Error ? error.message : 'Connection failed',\n lastConnectionCheck: new Date().toISOString(),\n }));\n \n return { success: false, connected: false };\n }\n },\n \n // 设置连接状态\n setConnected: (connected: boolean) => {\n update(state => ({ ...state, connected }));\n },\n \n // 设置错误\n setError: (error: string | null) => {\n update(state => ({ ...state, error }));\n },\n \n // 重置状态\n reset: () => {\n set({\n connected: false,\n loading: false,\n error: null,\n lastConnectionCheck: null,\n });\n },\n };\n}\n\nexport const appStore = createAppStore();\n\n// 导出派生store\nexport const systemHealth = derived(systemStore, ($system) => {\n if (!$system.status) return null;\n \n return {\n overall: $system.status.status === 'healthy',\n vectorStore: $system.status.vector_store,\n llmService: $system.status.llm_service,\n timestamp: $system.status.timestamp,\n };\n});\n\nexport const performanceSummary = derived(systemStore, ($system) => {\n if (!$system.metrics) return null;\n \n return {\n cpuUsage: $system.metrics.cpu_usage,\n memoryUsage: $system.metrics.memory_usage,\n diskUsage: $system.metrics.disk_usage,\n activeConnections: $system.metrics.active_connections,\n errorRate: $system.metrics.error_rate,\n responseTime: $system.metrics.response_time_avg,\n };\n});\n\nexport const recentLogs = derived(systemStore, ($system) => {\n return $system.logs\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 20);\n});\n\nexport const errorLogs = derived(systemStore, ($system) => {\n return $system.logs\n .filter(log => log.level === 'error')\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 10);\n});\n\nexport const warningLogs = derived(systemStore, ($system) => {\n return $system.logs\n .filter(log => log.level === 'warn')\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 10);\n});\n\n// 主题状态\ninterface ThemeState {\n darkMode: boolean;\n sidebarCollapsed: boolean;\n}\n\nfunction createThemeStore() {\n const { subscribe, set, update } = writable({\n darkMode: false,\n sidebarCollapsed: false,\n });\n\n return {\n subscribe,\n \n // 切换暗黑模式\n toggleDarkMode: () => {\n update(state => {\n const darkMode = !state.darkMode;\n \n // 保存到localStorage\n if (typeof window !== 'undefined') {\n localStorage.setItem('darkMode', darkMode.toString());\n }\n \n return { ...state, darkMode };\n });\n },\n \n // 切换侧边栏\n toggleSidebar: () => {\n update(state => {\n const sidebarCollapsed = !state.sidebarCollapsed;\n \n // 保存到localStorage\n if (typeof window !== 'undefined') {\n localStorage.setItem('sidebarCollapsed', sidebarCollapsed.toString());\n }\n \n return { ...state, sidebarCollapsed };\n });\n },\n \n // 从localStorage加载设置\n loadSettings: () => {\n if (typeof window === 'undefined') return;\n \n const darkMode = localStorage.getItem('darkMode') === 'true';\n const sidebarCollapsed = localStorage.getItem('sidebarCollapsed') === 'true';\n \n set({ darkMode, sidebarCollapsed });\n },\n \n // 重置设置\n reset: () => {\n set({ darkMode: false, sidebarCollapsed: false });\n \n if (typeof window !== 'undefined') {\n localStorage.removeItem('darkMode');\n localStorage.removeItem('sidebarCollapsed');\n }\n },\n };\n}\n\nexport const themeStore = createThemeStore();" }, "complexity_metrics": { - "cyclomatic_complexity": 28.0, - "lines_of_code": 629, + "cyclomatic_complexity": 7.0, + "lines_of_code": 381, "number_of_classes": 0, - "number_of_functions": 8 + "number_of_functions": 19 }, "dependencies": [ { - "dependency_type": "framework", + "dependency_type": "import", "is_external": true, "line_number": 1, - "name": "svelte", - "path": null, + "name": "svelte/store", + "path": "svelte/store", "version": null }, { - "dependency_type": "local", + "dependency_type": "import", "is_external": false, "line_number": 2, - "name": "$lib/api/client", - "path": "$lib/api/client", + "name": "systemApi", + "path": "../api/client", "version": null }, { - "dependency_type": "api", + "dependency_type": "import", "is_external": false, "line_number": 3, - "name": "optimizationApi", - "path": null, + "name": "SystemStatus, PerformanceMetrics, SystemInfo, LogEntry", + "path": "../../server/api/types", "version": null } ], - "detailed_description": "This Svelte component serves as the optimization dashboard for a memory management system. It provides a comprehensive UI for analyzing and optimizing memory data with multiple strategies including full optimization, deduplication, quality improvement, and relevance tuning. The component features real-time progress tracking, historical data visualization, and interactive controls for optimization parameters. It manages asynchronous operations through polling mechanisms and handles various optimization states (idle, analyzing, executing, completed, failed). The interface displays detected issues with severity levels, allows configuration of optimization options (preview mode, aggressive mode, timeout), and shows estimated impact metrics. The component integrates with a backend optimization API to perform actual operations and retrieve status updates.", + "detailed_description": "This component implements three distinct Svelte stores to manage global state in a frontend application dashboard. The systemStore manages the state of backend system metrics, status, logs, and information through API calls via systemApi. It supports loading individual components or refreshing all data at once, with proper error handling and loading states. The appStore handles frontend application connectivity status by polling a health endpoint, allowing components to reactively respond to backend availability. The themeStore manages UI preferences such as dark mode and sidebar collapse state, persisting these settings to localStorage for user preference retention across sessions. Additionally, several derived stores (systemHealth, performanceSummary, recentLogs, errorLogs, warningLogs) provide computed, filtered views of the raw data for optimized consumption by UI components.", "interfaces": [ { - "description": "Loads optimization history and detected issues from API", - "interface_type": "function", - "name": "loadOptimizationData", + "description": "Loads current system status from API and updates store state", + "interface_type": "method", + "name": "loadStatus", + "parameters": [], + "return_type": "Promise", + "visibility": "public" + }, + { + "description": "Loads performance metrics from API and updates store state", + "interface_type": "method", + "name": "loadMetrics", + "parameters": [], + "return_type": "Promise", + "visibility": "public" + }, + { + "description": "Loads system information from API and updates store state", + "interface_type": "method", + "name": "loadInfo", + "parameters": [], + "return_type": "Promise", + "visibility": "public" + }, + { + "description": "Loads system logs with optional filtering parameters", + "interface_type": "method", + "name": "loadLogs", "parameters": [ { - "description": "Whether to skip the analysis step when loading data", + "description": "Optional filtering parameters for log retrieval", "is_optional": true, - "name": "skipAnalyze", - "param_type": "boolean" + "name": "params", + "param_type": "object" } ], "return_type": "Promise", - "visibility": "private" + "visibility": "public" }, { - "description": "Initiates optimization process with selected strategy and options", - "interface_type": "function", - "name": "startOptimization", + "description": "Loads resource usage data and returns result object", + "interface_type": "method", + "name": "loadResources", "parameters": [], - "return_type": "Promise", - "visibility": "private" + "return_type": "Promise", + "visibility": "public" }, { - "description": "Starts periodic polling for optimization status updates", - "interface_type": "function", - "name": "startPolling", + "description": "Clears application cache and returns operation result", + "interface_type": "method", + "name": "clearCache", "parameters": [], - "return_type": "void", - "visibility": "private" + "return_type": "Promise", + "visibility": "public" }, { - "description": "Stops the polling interval for optimization status", - "interface_type": "function", - "name": "stopPolling", + "description": "Initiates service restart and returns operation result", + "interface_type": "method", + "name": "restartService", "parameters": [], - "return_type": "void", - "visibility": "private" + "return_type": "Promise", + "visibility": "public" }, { - "description": "Cancels the currently running optimization process", - "interface_type": "function", - "name": "cancelOptimization", + "description": "Refreshes all system data concurrently", + "interface_type": "method", + "name": "refreshAll", "parameters": [], "return_type": "Promise", - "visibility": "private" + "visibility": "public" }, { - "description": "Calculates estimated number of memories affected by optimization", - "interface_type": "function", - "name": "getEstimatedImpact", + "description": "Checks backend health status and updates connection state", + "interface_type": "method", + "name": "checkConnection", "parameters": [], - "return_type": "number", - "visibility": "private" + "return_type": "Promise", + "visibility": "public" }, { - "description": "Returns CSS classes for status badge coloring", - "interface_type": "function", - "name": "getStatusColor", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "status", - "param_type": "string" - } - ], - "return_type": "string", - "visibility": "private" + "description": "Toggles dark mode and persists preference to localStorage", + "interface_type": "method", + "name": "toggleDarkMode", + "parameters": [], + "return_type": "void", + "visibility": "public" }, { - "description": "Returns CSS classes for severity level coloring", - "interface_type": "function", - "name": "getSeverityColor", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "severity", - "param_type": "string" - } - ], - "return_type": "string", - "visibility": "private" + "description": "Toggles sidebar collapse state and persists to localStorage", + "interface_type": "method", + "name": "toggleSidebar", + "parameters": [], + "return_type": "void", + "visibility": "public" + }, + { + "description": "Loads persisted UI settings from localStorage", + "interface_type": "method", + "name": "loadSettings", + "parameters": [], + "return_type": "void", + "visibility": "public" } ], "responsibilities": [ - "Provides UI for memory optimization control with multiple strategy selection", - "Manages real-time optimization process with progress tracking and state management", - "Displays historical optimization records and detected memory issues", - "Handles asynchronous API calls to optimization service with error handling", - "Calculates and displays estimated impact metrics based on selected options" - ] - }, - { - "code_dossier": { - "code_purpose": "page", - "description": "Svelte page component for managing memories with search, filter, sort, paginate and batch operations.", - "file_path": "cortex-mem-insights/src/routes/memories/+page.svelte", + "Manage system monitoring state including status, metrics, info, and logs", + "Handle application connectivity state and health checks", + "Persist and manage UI theme and layout preferences", + "Provide derived computed state for optimized UI rendering", + "Coordinate API interactions with state updates and error handling" + ] + }, + { + "code_dossier": { + "code_purpose": "widget", + "description": "Svelte component implementing a responsive navigation bar with internationalization support, path highlighting, and mobile/desktop layout adaptation.", + "file_path": "cortex-mem-insights/src/lib/components/Navigation.svelte", + "functions": [], + "importance_score": 0.8, + "interfaces": [ + "currentPath: string" + ], + "name": "Navigation.svelte", + "source_summary": "\n\n\n\n\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 1.0, + "lines_of_code": 95, + "number_of_classes": 0, + "number_of_functions": 0 + }, + "dependencies": [ + { + "dependency_type": "import", + "is_external": false, + "line_number": 1, + "name": "$lib/i18n", + "path": "$lib/i18n", + "version": null + }, + { + "dependency_type": "import", + "is_external": false, + "line_number": 2, + "name": "LanguageSwitcher", + "path": "./LanguageSwitcher.svelte", + "version": null + } + ], + "detailed_description": "The Navigation.svelte component is a frontend UI widget responsible for rendering a responsive navigation menu in a Svelte-based application. It displays a horizontal navigation bar on desktop screens and a vertical layout on mobile devices (screen width < 640px, based on Tailwind's sm: breakpoint). The component receives the `currentPath` as a prop to determine which navigation item should be visually highlighted. Navigation items are defined statically within the component, each containing a path, internationalized label key, and an emoji icon. The labels are resolved via the `$t` function from the i18n system. The navigation includes a logo section with the app name, a list of primary navigation links, and a right-aligned section containing a LanguageSwitcher component and a placeholder for a user menu. Styling uses Tailwind CSS with dark mode support, and the active route is indicated with a colored bottom border (desktop) or left border (mobile). The component is designed to be reusable across different pages while maintaining consistent navigation state.", + "interfaces": [ + { + "description": "The current active route path used to highlight the corresponding navigation item", + "interface_type": "property", + "name": "currentPath", + "parameters": [], + "return_type": "string", + "visibility": "export" + } + ], + "responsibilities": [ + "Render responsive navigation menu with desktop and mobile layouts", + "Highlight the currently active route based on currentPath prop", + "Display internationalized navigation labels using i18n system", + "Integrate LanguageSwitcher component for language selection", + "Provide consistent branding with logo and app name display" + ] + }, + { + "code_dossier": { + "code_purpose": "widget", + "description": "A Svelte component that provides a dropdown UI for switching application language, supporting English, Chinese, and Japanese with real-time i18n context updates.", + "file_path": "cortex-mem-insights/src/lib/components/LanguageSwitcher.svelte", "functions": [ - "loadMemories", - "handleSearch", - "getTypeColor", - "getTypeLabel", - "formatImportance", - "formatDate", - "getImportanceColor", - "toggleSort", - "getSortIcon", - "goToPage", - "nextPage", - "prevPage", - "showFullContent", - "hideContentModal", - "toggleSelectMemory", - "selectAll", - "deselectAll", - "batchExport", - "batchOptimize", - "batchDelete" + "toggleDropdown", + "selectLanguage", + "handleClickOutside" ], "importance_score": 0.8, "interfaces": [ - "Memory interface for memory data structure", - "onMount lifecycle function", - "Svelte reactivity declarations" + "selectLanguage(lang: 'en' | 'zh' | 'ja')", + "toggleDropdown()", + "handleClickOutside(event: MouseEvent)", + "onDestroy cleanup handler" ], - "name": "+page.svelte", - "source_summary": "\n\n
\n\t\n\t
\n\t\t

记忆浏览器

\n\t\t

浏览、搜索和管理所有记忆记录

\n\t
\n\n\t\n\t{#if error}\n\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t⚠️\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

加载失败

\n\t\t\t\t\t
\n\t\t\t\t\t\t{error}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t重试\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t\n\t
\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t🔍\n\t\t\t\t\t
\n\t\t\t\t\t {\n\t\t\t\t\t\t\tif (e.key === 'Enter') {\n\t\t\t\t\t\t\t\thandleSearch();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{#each memoryTypes as type}\n\t\t\t\t\t\t\n\t\t\t\t\t{/each}\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t搜索\n\t\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tsearchQuery = '';\n\t\t\t\t\t\tselectedType = 'all';\n\t\t\t\t\t\tsortBy = 'createdAt';\n\t\t\t\t\t\tsortOrder = 'desc';\n\t\t\t\t\t\tloadMemories();\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t重置\n\t\t\t\t\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t\t共 {filteredMemories.length}\n\t\t\t\t条记忆, 显示第\n\t\t\t\t{(currentPage - 1) * pageSize + 1}\n\t\t\t\t到\n\t\t\t\t{Math.min(currentPage * pageSize, filteredMemories.length)} 条\n\t\t\t\n\t\t\t
\n\t\t\t\t排序:\n\t\t\t\t
\n\t\t\t\t\t toggleSort('createdAt')}\n\t\t\t\t\t>\n\t\t\t\t\t\t创建时间 {createdAtSortIcon}\n\t\t\t\t\t\n\t\t\t\t\t toggleSort('importance')}\n\t\t\t\t\t>\n\t\t\t\t\t\t重要性 {importanceSortIcon}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t
\n\n\t\n\t{#if showBatchOperations}\n\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t已选择 {selectedMemories.size} 条记忆\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t取消选择\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t📤 批量导出\n\t\t\t\t\t\n\n\t\t\t\t\t\n\t\t\t\t\t\t⚡ 批量优化\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t🗑️ 批量删除\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t\n\t{/if}\n\n\t\n\t
\n\t\t{#if isLoading}\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{#each Array(5) as _, i}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t
\n\t\t{:else if filteredMemories.length === 0}\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t📭\n\t\t\t\t
\n\t\t\t\t

未找到记忆记录

\n\t\t\t\t

\n\t\t\t\t\t{searchQuery || selectedType !== 'all' ? '尝试调整搜索条件' : '系统暂无记忆记录'}\n\t\t\t\t

\n\t\t\t\t{#if searchQuery || selectedType !== 'all'}\n\t\t\t\t\t {\n\t\t\t\t\t\t\tsearchQuery = '';\n\t\t\t\t\t\t\tselectedType = 'all';\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t清除筛选条件\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t
\n\t\t{:else if paginatedMemories.length === 0}\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t📄\n\t\t\t\t
\n\t\t\t\t

当前页无数据

\n\t\t\t\t

\n\t\t\t\t\t第 {currentPage} 页暂无数据,请检查页码或调整筛选条件\n\t\t\t\t

\n\t\t\t\t goToPage(1)}\n\t\t\t\t>\n\t\t\t\t\t返回第一页\n\t\t\t\t\n\t\t\t\n\t\t{:else}\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\tif (e.currentTarget.checked) {\n\t\t\t\t\t\t\t\t\t\t\tselectAll();\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tdeselectAll();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tID\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t内容\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t类型\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t重要性\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t用户/Agent\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t创建时间\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{#each paginatedMemories as memory}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{formatDate(memory.createdAt)}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\t\t\t\t toggleSelectMemory(memory.id)}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{memory.id}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t showFullContent(memory.content, memory.id)}\n\t\t\t\t\t\t\t\t\t\t\ton:keydown={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\tif (e.key === 'Enter' || e.key === ' ') {\n\t\t\t\t\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\t\t\tshowFullContent(memory.content, memory.id);\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\ttitle=\"点击查看完整内容\"\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{memory.content}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{#if memory.content.length > 100}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t点击查看完整内容 ({memory.content.length} 字符)\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{getTypeLabel(memory.type)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{formatImportance(memory.importance)}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{#if memory.userId}\n\t\t\t\t\t\t\t\t\t\t\t
{memory.userId}
\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{#if memory.agentId}\n\t\t\t\t\t\t\t\t\t\t\t
Agent: {memory.agentId}
\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if totalPages > 1}\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t显示第 {(currentPage - 1) * pageSize + 1} 到\n\t\t\t\t\t\t\t{Math.min(currentPage * pageSize, filteredMemories.length)}\n\t\t\t\t\t\t\t条, 共 {filteredMemories.length} 条,第\n\t\t\t\t\t\t\t{currentPage}\n\t\t\t\t\t\t\t/ {totalPages} 页\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t上一页\n\t\t\t\t\t\t\t\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each Array.from({ length: Math.min(5, totalPages) }, (_, i) => {\n\t\t\t\t\t\t\t\tconst startPage = Math.max(1, currentPage - 2);\n\t\t\t\t\t\t\t\tconst endPage = Math.min(totalPages, startPage + 4);\n\t\t\t\t\t\t\t\treturn startPage + i;\n\t\t\t\t\t\t\t}) as page}\n\t\t\t\t\t\t\t\t{#if page <= totalPages}\n\t\t\t\t\t\t\t\t\t goToPage(page)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{page}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{/each}\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t下一页\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\t\t\t{/if}\n\t\t{/if}\n\t\n\n\t\n\t{#if showContentModal}\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t

记忆内容详情

\n\t\t\t\t\t\t

ID: {selectedMemoryId}

\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t×\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{selectedContent}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t关闭\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\n\t\t\n\t{/if}\n\n\n\n" + "name": "LanguageSwitcher.svelte", + "source_summary": "\n\n
\n \n \n {#if $language === 'en'}\n 🇺🇸\n {:else if $language === 'zh'}\n 🇨🇳\n {:else if $language === 'ja'}\n 🇯🇵\n {/if}\n \n {$t('common.language')}\n \n \n \n \n \n {#if showDropdown}\n
\n {#each languageOptions as option}\n selectLanguage(option.value)}\n >\n \n {#if option.value === 'en'}\n 🇺🇸\n {:else if option.value === 'zh'}\n 🇨🇳\n {:else if option.value === 'ja'}\n 🇯🇵\n {/if}\n \n {option.label}\n {#if $language === option.value}\n \n \n \n {/if}\n \n {/each}\n
\n {/if}\n
\n\n" }, "complexity_metrics": { - "cyclomatic_complexity": 56.0, - "lines_of_code": 937, + "cyclomatic_complexity": 12.0, + "lines_of_code": 103, "number_of_classes": 0, - "number_of_functions": 25 + "number_of_functions": 4 }, "dependencies": [ { - "dependency_type": "module", - "is_external": true, + "dependency_type": "import", + "is_external": false, "line_number": 1, + "name": "$lib/i18n", + "path": "$lib/i18n", + "version": null + }, + { + "dependency_type": "import", + "is_external": true, + "line_number": 12, "name": "svelte", "path": "svelte", "version": null }, { - "dependency_type": "module", - "is_external": false, - "line_number": 2, - "name": "$lib/api/client", - "path": "$lib/api/client", + "dependency_type": "global", + "is_external": true, + "line_number": 26, + "name": "window", + "path": null, "version": null } ], - "detailed_description": "This component implements a comprehensive memory management interface in a Svelte application. It provides functionality to view, search, filter, sort, and paginate through memory records. The component handles data loading from an API, manages various UI states (loading, error, selection), and supports batch operations like export, optimize, and delete. It includes responsive design elements and accessibility features. The component processes memory data by transforming API responses, handling character encoding issues, and normalizing importance scores. It implements client-side filtering, sorting and pagination logic, along with a content modal for viewing complete memory entries.", + "detailed_description": "The LanguageSwitcher component is a frontend UI widget built with Svelte and TypeScript that enables users to switch between multiple languages (English, Chinese, Japanese) in the application. It displays the current language as a flag emoji in a button and provides a dropdown menu with all available language options. Selecting a language updates the global i18n store via setLanguage and reflects changes immediately through the $t store for translations. The component handles user interactions including toggling the dropdown and closing it when clicking outside. It properly manages event listeners by adding a global click handler on mount and removing it on destruction to prevent memory leaks. The UI is responsive with conditional rendering and visual feedback for the selected language, including a checkmark indicator and highlighted menu item.", "interfaces": [ { - "description": "Data structure for memory records", - "interface_type": "interface", - "name": "Memory", + "description": "Updates the global language setting and closes the dropdown", + "interface_type": "function", + "name": "selectLanguage", "parameters": [ { - "description": null, - "is_optional": false, - "name": "id", - "param_type": "string" - }, - { - "description": null, - "is_optional": false, - "name": "content", - "param_type": "string" - }, - { - "description": null, - "is_optional": false, - "name": "type", - "param_type": "string" - }, - { - "description": null, - "is_optional": false, - "name": "importance", - "param_type": "number" - }, - { - "description": null, - "is_optional": true, - "name": "userId", - "param_type": "string" - }, - { - "description": null, - "is_optional": true, - "name": "agentId", - "param_type": "string" - }, - { - "description": null, - "is_optional": false, - "name": "createdAt", - "param_type": "string" - }, - { - "description": null, + "description": "The language code to switch to", "is_optional": false, - "name": "updatedAt", - "param_type": "string" + "name": "lang", + "param_type": "'en' | 'zh' | 'ja'" } ], - "return_type": null, + "return_type": "void", "visibility": "private" }, { - "description": "Svelte lifecycle function", + "description": "Toggles the visibility of the language selection dropdown", "interface_type": "function", - "name": "onMount", + "name": "toggleDropdown", + "parameters": [], + "return_type": "void", + "visibility": "private" + }, + { + "description": "Closes the dropdown if the click occurred outside the component", + "interface_type": "function", + "name": "handleClickOutside", "parameters": [ { - "description": null, + "description": "The mouse click event to evaluate", "is_optional": false, - "name": "callback", - "param_type": "() => Promise" + "name": "event", + "param_type": "MouseEvent" } ], "return_type": "void", "visibility": "private" }, { - "description": "Load memories from API and transform response", - "interface_type": "function", - "name": "loadMemories", + "description": "Cleanup handler that removes the global click event listener", + "interface_type": "lifecycle", + "name": "onDestroy", "parameters": [], - "return_type": "Promise", + "return_type": "void", "visibility": "private" + } + ], + "responsibilities": [ + "Provide UI for language selection with flag emojis and labels", + "Manage dropdown visibility state and toggle behavior", + "Handle external clicks to close dropdown menu", + "Update global language setting and notify i18n system", + "Clean up event listeners on component destruction" + ] + }, + { + "code_dossier": { + "code_purpose": "widget", + "description": "A Svelte component that displays detailed information about a memory issue in a modal dialog, including affected memories and their metadata.", + "file_path": "cortex-mem-insights/src/lib/components/IssueDetailModal.svelte", + "functions": [ + "loadMemoryDetails", + "handleBackdropClick", + "getSeverityColor", + "formatDate", + "getMemoryTypeLabel" + ], + "importance_score": 0.8, + "interfaces": [ + "issue", + "onClose" + ], + "name": "IssueDetailModal.svelte", + "source_summary": "\n\n{#if issue}\n \n e.key === 'Escape' && onClose()}\n role=\"button\"\n tabindex=\"0\"\n >\n \n \n \n
\n
\n

\n {issue.type} - 详细信息\n

\n \n {issue.severity === 'high' ? '高' : issue.severity === 'medium' ? '中' : '低'}\n \n
\n \n \n \n \n \n
\n\n \n
\n
问题描述
\n
{issue.description}
\n
\n 影响记忆数量: {issue.count} 条\n
\n
\n\n \n
\n {#if isLoading}\n
\n
\n 加载记忆详情...\n
\n {:else if error}\n
\n
\n \n \n \n {error}\n
\n
\n {:else if memories.length === 0}\n
\n 暂无记忆详情\n
\n {:else}\n
\n {#each memories as memory, index}\n
\n \n
\n
\n #{index + 1}\n \n {getMemoryTypeLabel(memory.metadata?.memory_type || 'Unknown')}\n \n
\n
\n ID: {memory.id}\n
\n
\n\n \n
\n
内容
\n
\n {memory.content}\n
\n
\n\n \n
\n {#if memory.metadata?.user_id}\n
\n 用户ID:\n {memory.metadata.user_id}\n
\n {/if}\n {#if memory.metadata?.agent_id}\n
\n 代理ID:\n {memory.metadata.agent_id}\n
\n {/if}\n {#if memory.created_at}\n
\n 创建时间:\n {formatDate(memory.created_at)}\n
\n {/if}\n {#if memory.updated_at}\n
\n 更新时间:\n {formatDate(memory.updated_at)}\n
\n {/if}\n
\n
\n {/each}\n
\n {/if}\n
\n\n \n
\n \n 关闭\n \n
\n \n \n{/if}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 16.0, + "lines_of_code": 221, + "number_of_classes": 0, + "number_of_functions": 5 + }, + "dependencies": [ + { + "dependency_type": "framework", + "is_external": true, + "line_number": 1, + "name": "svelte", + "path": "svelte", + "version": null }, { - "description": "Handle search functionality", - "interface_type": "function", - "name": "handleSearch", + "dependency_type": "api", + "is_external": false, + "line_number": 2, + "name": "memoryApi", + "path": "$lib/api/client", + "version": null + } + ], + "detailed_description": "This component renders a modal dialog that shows detailed information about a detected memory issue. It displays the issue type, severity (color-coded), description, and count of affected memories. When an issue is provided, it automatically loads the full details of each affected memory by calling the memoryApi. The UI includes a loading state, error handling, and a responsive layout. Users can close the modal by clicking the backdrop, the close button, or pressing Escape. Memory details are displayed in a scrollable list with formatted metadata and timestamps.", + "interfaces": [ + { + "description": "The memory issue object to display, containing type, count, severity, description, and affected memory IDs", + "interface_type": "property", + "name": "issue", "parameters": [], - "return_type": "Promise", - "visibility": "private" + "return_type": "object", + "visibility": "export" }, { - "description": "Get CSS classes for memory type color", - "interface_type": "function", - "name": "getTypeColor", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "type", - "param_type": "string" - } - ], - "return_type": "string", - "visibility": "private" + "description": "Callback function triggered when the user closes the modal", + "interface_type": "property", + "name": "onClose", + "parameters": [], + "return_type": "void", + "visibility": "export" }, { - "description": "Get display label for memory type", + "description": "Loads detailed information for all affected memories from the backend API", "interface_type": "function", - "name": "getTypeLabel", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "type", - "param_type": "string" - } - ], - "return_type": "string", + "name": "loadMemoryDetails", + "parameters": [], + "return_type": "Promise", "visibility": "private" }, { - "description": "Format importance score as percentage", + "description": "Handles click events on the modal backdrop to close the dialog", "interface_type": "function", - "name": "formatImportance", + "name": "handleBackdropClick", "parameters": [ { "description": null, "is_optional": false, - "name": "importance", - "param_type": "number" + "name": "e", + "param_type": "MouseEvent" } ], - "return_type": "string", + "return_type": "void", "visibility": "private" }, { - "description": "Format ISO date string to localized format", + "description": "Returns Tailwind CSS classes for coloring severity badges based on level", "interface_type": "function", - "name": "formatDate", + "name": "getSeverityColor", "parameters": [ { "description": null, "is_optional": false, - "name": "isoString", + "name": "severity", "param_type": "string" } ], @@ -4614,5954 +4576,3145 @@ Code analysis results from preprocessing phase, including definitions of functio "visibility": "private" }, { - "description": "Get text color class based on importance score", - "interface_type": "function", - "name": "getImportanceColor", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "importance", - "param_type": "number" - } - ], - "return_type": "string", - "visibility": "private" - }, - { - "description": "Toggle sort column and order", + "description": "Maps English memory type names to Chinese display labels", "interface_type": "function", - "name": "toggleSort", + "name": "getMemoryTypeLabel", "parameters": [ { "description": null, "is_optional": false, - "name": "column", + "name": "type", "param_type": "string" } ], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Get sort icon based on current sort state", - "interface_type": "function", - "name": "getSortIcon", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "column", - "param_type": "string" - } - ], - "return_type": "string", - "visibility": "private" - }, - { - "description": "Navigate to specific page", - "interface_type": "function", - "name": "goToPage", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "page", - "param_type": "number" - } - ], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Navigate to next page", - "interface_type": "function", - "name": "nextPage", - "parameters": [], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Navigate to previous page", - "interface_type": "function", - "name": "prevPage", - "parameters": [], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Show modal with full memory content", - "interface_type": "function", - "name": "showFullContent", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "content", - "param_type": "string" - }, - { - "description": null, - "is_optional": false, - "name": "memoryId", - "param_type": "string" - } - ], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Hide the content modal", - "interface_type": "function", - "name": "hideContentModal", - "parameters": [], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Toggle selection of a memory", - "interface_type": "function", - "name": "toggleSelectMemory", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "memoryId", - "param_type": "string" - } - ], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Select all memories on current page", - "interface_type": "function", - "name": "selectAll", - "parameters": [], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Deselect all memories", - "interface_type": "function", - "name": "deselectAll", - "parameters": [], - "return_type": "void", - "visibility": "private" - }, - { - "description": "Export selected memories to JSON file", - "interface_type": "function", - "name": "batchExport", - "parameters": [], - "return_type": "Promise", - "visibility": "private" - }, - { - "description": "Optimize selected memories", - "interface_type": "function", - "name": "batchOptimize", - "parameters": [], - "return_type": "Promise", - "visibility": "private" - }, - { - "description": "Delete selected memories", - "interface_type": "function", - "name": "batchDelete", - "parameters": [], - "return_type": "Promise", + "return_type": "string", "visibility": "private" } ], "responsibilities": [ - "Handle memory data loading and API communication", - "Manage UI state for search, filter, sort, and pagination", - "Provide batch operations on selected memories (export, optimize, delete)", - "Display memory data in a tabular format with proper formatting", - "Handle user interactions and maintain responsive UI" + "Display detailed information about a memory issue in a modal interface", + "Fetch and render detailed data for affected memories via API calls", + "Handle user interactions including closing the modal and keyboard navigation", + "Format and present memory metadata with localized labels and timestamps", + "Manage loading, error, and empty states for UX stability" ] }, { "code_dossier": { - "code_purpose": "router", - "description": "SvelteKit layout component that defines the common UI structure for all pages in the application, including navigation bar, main content area, and footer. Manages active route highlighting based on current URL path.", - "file_path": "cortex-mem-insights/src/routes/+layout.svelte", - "functions": [], + "code_purpose": "widget", + "description": "Svelte component for displaying and detecting the real-time status of backend services including Cortex Memory Service, Qdrant, and LLM services.", + "file_path": "cortex-mem-insights/src/lib/components/ServiceStatus.svelte", + "functions": [ + "detectIndividualServices", + "detectServicesAsync", + "handleRefresh", + "getStatusColor", + "getStatusLightColor", + "getStatusText" + ], "importance_score": 0.8, "interfaces": [ - "currentPath: derived store", - "navItems: navigation configuration" + "systemStatus", + "title", + "showRefreshButton", + "autoDetect", + "statusUpdate" ], - "name": "+layout.svelte", - "source_summary": "\n\n
\n\t\n\t\n\n\t\n\t
\n\t\t\n\t
\n\n\t\n\t
\n\t\t
\n\t\t\t
\n\t\t\t\t
© 2025 Cortex Memory Insights
\n\t\t\t
\n\t\t
\n\t
\n
\n\n\n" + "name": "ServiceStatus.svelte", + "source_summary": "\n\n
\n\t
\n\t\t

{title}

\n\t\t{#if showRefreshButton}\n\t\t\t\n\t\t\t\t{#if isDetectingServices}\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t检测中...\n\t\t\t\t{:else}\n\t\t\t\t\t重新检查所有服务\n\t\t\t\t{/if}\n\t\t\t\n\t\t{/if}\n\t
\n\n\t
\n\t\t{#if localSystemStatus}\n\t\t\t{#each Object.entries(localSystemStatus) as [service, data]}\n\t\t\t\t{#if data && typeof data === 'object' && data.status}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{service === 'cortexMemService'\n\t\t\t\t\t\t\t\t\t\t? 'Cortex Memory Service'\n\t\t\t\t\t\t\t\t\t\t: service === 'qdrant'\n\t\t\t\t\t\t\t\t\t\t\t? 'Qdrant 数据库'\n\t\t\t\t\t\t\t\t\t\t\t: 'LLM 服务'}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{getStatusText(data.status)}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t延迟: \n\t\t\t\t\t\t\t\t\t{#if data.status === 'detecting'}\n\t\t\t\t\t\t\t\t\t\t检测中...\n\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t{data.latency}ms\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t{#if data.lastCheck}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t最后检查: {data.lastCheck}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t{/each}\n\t\t{/if}\n\t
\n
\n\n\n" }, "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 73, + "cyclomatic_complexity": 37.0, + "lines_of_code": 472, "number_of_classes": 0, - "number_of_functions": 0 + "number_of_functions": 6 }, "dependencies": [ { - "dependency_type": "style", - "is_external": false, - "line_number": 2, - "name": "app.css", - "path": "cortex-mem-insights/src/app.css", + "dependency_type": "import", + "is_external": true, + "line_number": null, + "name": "svelte", + "path": "svelte", "version": null }, { - "dependency_type": "framework", + "dependency_type": "import", "is_external": true, "line_number": 3, - "name": "$app/stores", - "path": "$app/stores", + "name": "svelte", + "path": "svelte", "version": null } ], - "detailed_description": "This SvelteKit layout component serves as the shell for all pages in the Cortex Memory Insights application. It imports the global CSS and the SvelteKit page store to access routing information. The component defines a navigation menu with five items: Dashboard, Memory Browser, Analytics, Optimization Panel, and System Monitor, each with corresponding icons and routes. Using Svelte's reactive declaration syntax ($:), it derives the current path from $page.url.pathname, with null-safe handling for undefined URLs. The UI is built with Tailwind CSS, featuring a responsive design with dark mode support. The navigation highlights the active route by comparing the current path with each menu item's href. The main content area uses a to render child page content, following Svelte's component composition pattern. The layout also includes a header with the application logo and name, and a footer with copyright information. The styling is clean and modern, with proper spacing, shadows, and transition effects for interactive elements.", + "detailed_description": "This Svelte component provides a UI widget to visualize and actively check the connectivity and performance status of three core backend services: Cortex Memory Service, Qdrant vector database, and LLM service. It supports both automatic initial detection on mount and manual refresh via user interaction. The component fetches status through three distinct API endpoints (/api/system/status, /api/system/vector-store/status, /api/system/llm/status) with individual timeout controls. Status is displayed with colored indicators, latency metrics, and timestamps. When the main Cortex Memory Service is unreachable, dependent services (Qdrant and LLM) are automatically marked as disconnected. The component emits a 'statusUpdate' event whenever new status data is obtained, enabling parent components to react to service state changes. Visual feedback includes loading spinners during detection and pulsing animations for 'detecting' states.", "interfaces": [ { - "description": "Reactive derived store that extracts the current pathname from SvelteKit's page store, with fallback to root path", - "interface_type": "derived_store", - "name": "currentPath", + "description": "Input property for pre-fetched system status; when null, component performs its own detection", + "interface_type": "property", + "name": "systemStatus", + "parameters": [], + "return_type": "SystemStatus | null", + "visibility": "export" + }, + { + "description": "Display title for the status panel; defaults to '服务状态'", + "interface_type": "property", + "name": "title", "parameters": [], "return_type": "string", - "visibility": "private" + "visibility": "export" }, { - "description": "Configuration array defining the navigation menu items with name, route, and icon", - "interface_type": "constant", - "name": "navItems", + "description": "Controls visibility of the manual refresh button; defaults to true", + "interface_type": "property", + "name": "showRefreshButton", "parameters": [], - "return_type": "NavItem[]", - "visibility": "private" + "return_type": "boolean", + "visibility": "export" + }, + { + "description": "Determines whether to automatically detect services on component mount; defaults to true", + "interface_type": "property", + "name": "autoDetect", + "parameters": [], + "return_type": "boolean", + "visibility": "export" + }, + { + "description": "Event dispatched when service status is updated after detection", + "interface_type": "event", + "name": "statusUpdate", + "parameters": [ + { + "description": "The updated system status object", + "is_optional": false, + "name": "systemStatus", + "param_type": "SystemStatus" + } + ], + "return_type": null, + "visibility": "dispatch" } ], "responsibilities": [ - "Provides consistent application layout structure across all routes", - "Manages navigation menu with active route highlighting", - "Handles responsive design and dark mode styling", - "Derives current route path from SvelteKit page store", - "Serves as container for all page-level content through slot mechanism" + "Display real-time status of Cortex Memory Service, Qdrant, and LLM services with visual indicators", + "Actively detect service connectivity and performance metrics via API calls with timeout handling", + "Manage dependent service states (Qdrant and LLM become disconnected if Cortex Memory Service is down)", + "Provide manual refresh capability with loading state feedback", + "Notify parent components of status changes through event dispatching" ] }, { "code_dossier": { - "code_purpose": "page", - "description": "Analytics dashboard page for memory data visualization and statistics", - "file_path": "cortex-mem-insights/src/routes/analytics/+page.svelte", + "code_purpose": "api", + "description": "API 客户端配置,提供记忆、优化和系统相关的API接口封装。", + "file_path": "cortex-mem-insights/src/lib/api/client.ts", "functions": [ - "loadAnalyticsData", - "loadDefaultData", - "generateChartData", - "calculateAverageQuality", - "calculateImportanceScore", - "calculateActiveUsers", - "calculateTypeDistribution", - "calculateQualityDistribution", - "calculateTimeTrends", - "calculateUserStats", - "getPercentageColor" + "request", + "memoryApi.list", + "memoryApi.search", + "memoryApi.get", + "memoryApi.create", + "memoryApi.update", + "memoryApi.delete", + "memoryApi.batchDelete", + "memoryApi.batchUpdate", + "memoryApi.export", + "optimizationApi.optimize", + "optimizationApi.getStatus", + "optimizationApi.history", + "optimizationApi.cancel", + "optimizationApi.analyze", + "optimizationApi.cleanup", + "optimizationApi.statistics", + "systemApi.health", + "systemApi.status", + "systemApi.metrics", + "systemApi.info", + "systemApi.logs", + "systemApi.resources", + "systemApi.clearCache", + "systemApi.restart", + "api.testConnection", + "api.getAllStatus" ], "importance_score": 0.8, "interfaces": [ - "onMount", - "Line", - "ChartJS", - "CategoryScale", - "LinearScale", - "PointElement", - "LineElement", - "Title", - "Tooltip", - "Legend", - "Filler", - "api.memory.list" + "memoryApi", + "optimizationApi", + "systemApi", + "api" ], - "name": "+page.svelte", - "source_summary": "\n\n
\n\t\n\t
\n\t\t

统计分析

\n\t\t

深入分析记忆数据的分布、质量和趋势

\n\t
\n\n\t{#if isLoading}\n\t\t\n\t\t
\n\t\t\t{#each Array(4) as _, i}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\t{:else if error}\n\t\t\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t⚠️\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

加载失败

\n\t\t\t\t\t

{error}

\n\t\t\t\t
\n\t\t\t
\n\t\t\n\t{:else}\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t

总记忆数

\n\t\t\t\t

\n\t\t\t\t\t{summaryStats.totalMemories.toLocaleString()}\n\t\t\t\t

\n\t\t\t\t

当前总数

\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t

平均质量

\n\t\t\t\t

\n\t\t\t\t\t{(summaryStats.averageQuality * 100).toFixed(1)}%\n\t\t\t\t

\n\t\t\t\t

基于重要性评分

\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t

活跃用户

\n\t\t\t\t

\n\t\t\t\t\t{summaryStats.activeUsers}\n\t\t\t\t

\n\t\t\t\t

有记忆的用户

\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t

优化次数

\n\t\t\t\t

\n\t\t\t\t\t{summaryStats.optimizationCount}\n\t\t\t\t

\n\t\t\t\t

历史优化记录

\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t

记忆类型分布

\n\n\t\t\t\t
\n\t\t\t\t\t{#each typeDistribution as item}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{item.type}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{item.percentage}%\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{item.count} 条记录\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t占比 {item.percentage}%\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t总计: {summaryStats.totalMemories} 条记忆\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t

质量评分分布

\n\n\t\t\t\t
\n\t\t\t\t\t{#each qualityDistribution as item}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{item.range}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{item.count} 条\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

新增记忆趋势

\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t最近7天\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t最近30天\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t{#if chartData && timeTrends.length > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t{:else if timeTrends.length > 0}\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each timeTrends as trend, i}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{trend.date}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{trend.count}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t{:else}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
📊
\n\t\t\t\t\t\t\t

暂无数据

\n\t\t\t\t\t\t\t

等待记忆数据加载...

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t日均新增: 54.8 条\n\t\t\t\t\t\t峰值: 68 条 (12月13日)\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\n\t\t\t\n\t\t\t
\n\t\t\t\t

用户维度统计

\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t用户ID\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t记忆数量\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t平均质量\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t占比\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t趋势\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each userStats as user}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{user.userId}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t 0 ? (user.memoryCount / summaryStats.totalMemories) * 100 : 0}%`}\n\t\t\t\t\t\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{user.memoryCount}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t= 0.8\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: user.avgImportance >= 0.7\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300'\n\t\t\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{(user.avgImportance * 100).toFixed(1)}%\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{summaryStats.totalMemories > 0\n\t\t\t\t\t\t\t\t\t\t\t? ((user.memoryCount / summaryStats.totalMemories) * 100).toFixed(1)\n\t\t\t\t\t\t\t\t\t\t\t: '0.0'}%\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t数据不足\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t前{userStats.length}用户占总记忆的 {summaryStats.totalMemories > 0\n\t\t\t\t\t\t\t\t? (\n\t\t\t\t\t\t\t\t\t\t(userStats.reduce((sum, user) => sum + user.memoryCount, 0) /\n\t\t\t\t\t\t\t\t\t\t\tsummaryStats.totalMemories) *\n\t\t\t\t\t\t\t\t\t\t100\n\t\t\t\t\t\t\t\t\t).toFixed(1)\n\t\t\t\t\t\t\t\t: '0.0'}%\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t\n\n\t\t\n\t\t
\n\t\t\t

分析工具

\n\n\t\t\t
\n\t\t\t\t console.log('生成质量报告')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t📈\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

质量分析报告

\n\t\t\t\t\t\t\t

生成详细的质量分析

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('趋势预测')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t🔮\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

趋势预测

\n\t\t\t\t\t\t\t

预测未来增长趋势

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('对比分析')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t⚖️\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

对比分析

\n\t\t\t\t\t\t\t

不同时间段对比

\n\t\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t{/if}\n\n" + "name": "client.ts", + "source_summary": "// API 客户端配置\n// 在开发模式下使用相对路径,由Vite代理到API服务器\n// 在生产模式下使用环境变量配置的URL\nconst API_BASE_URL = import.meta.env.VITE_API_URL || '';\n\n// 通用请求函数\nasync function request(\n endpoint: string,\n options: RequestInit = {}\n): Promise {\n const url = `${API_BASE_URL}${endpoint}`;\n \n const defaultOptions: RequestInit = {\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n credentials: 'include',\n };\n \n try {\n const response = await fetch(url, { ...defaultOptions, ...options });\n \n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n errorData.error?.message || \n errorData.message || \n `HTTP ${response.status}: ${response.statusText}`\n );\n }\n \n return await response.json();\n } catch (error) {\n console.error(`API request failed: ${endpoint}`, error);\n throw error;\n }\n}\n\n// 记忆相关API\nexport const memoryApi = {\n // 获取记忆列表\n list: (params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n page?: number;\n }) => {\n const queryParams = new URLSearchParams();\n if (params?.user_id) queryParams.append('user_id', params.user_id);\n if (params?.agent_id) queryParams.append('agent_id', params.agent_id);\n if (params?.run_id) queryParams.append('run_id', params.run_id);\n if (params?.actor_id) queryParams.append('actor_id', params.actor_id);\n if (params?.memory_type) queryParams.append('memory_type', params.memory_type);\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.page) queryParams.append('page', params.page.toString());\n \n return request(`/api/memories${queryParams.toString() ? `?${queryParams}` : ''}`);\n },\n \n // 搜索记忆\n search: (query: string, params?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n memory_type?: string;\n limit?: number;\n similarity_threshold?: number;\n }) => {\n return request('/api/memories/search', {\n method: 'POST',\n body: JSON.stringify({ query, ...params }),\n });\n },\n \n // 获取单个记忆\n get: (id: string) => {\n return request(`/api/memories/${id}`);\n },\n \n // 创建记忆\n create: (content: string, metadata?: {\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n role?: string;\n memory_type?: string;\n custom?: Record;\n }) => {\n return request('/api/memories', {\n method: 'POST',\n body: JSON.stringify({ content, ...metadata }),\n });\n },\n \n // 更新记忆\n update: (id: string, content: string) => {\n return request(`/api/memories/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ content }),\n });\n },\n \n // 删除记忆\n delete: (id: string) => {\n return request(`/api/memories/${id}`, {\n method: 'DELETE',\n });\n },\n \n // 批量删除\n batchDelete: (ids: string[]) => {\n return request('/api/memories/batch/delete', {\n method: 'POST',\n body: JSON.stringify({ ids }),\n });\n },\n\n // 批量更新\n batchUpdate: (updates: { id: string; content: string }[]) => {\n return request('/api/memories/batch/update', {\n method: 'POST',\n body: JSON.stringify({ updates }),\n });\n },\n \n // 导出记忆\n export: (params: {\n format: 'json' | 'csv' | 'txt';\n ids?: string[];\n filters?: any;\n include_metadata?: boolean;\n include_scores?: boolean;\n }) => {\n return request('/api/memories/export', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n },\n};\n\n// 优化相关API\nexport const optimizationApi = {\n // 执行优化\n optimize: (params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n dry_run?: boolean;\n verbose?: boolean;\n }) => {\n return request('/api/optimization', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n },\n \n // 获取优化状态\n getStatus: (jobId: string) => {\n return request(`/api/optimization/${jobId}`);\n },\n \n // 获取优化历史\n history: (params?: {\n limit?: number;\n offset?: number;\n status?: string;\n start_date?: string;\n end_date?: string;\n }) => {\n const queryParams = new URLSearchParams();\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.offset) queryParams.append('offset', params.offset.toString());\n if (params?.status) queryParams.append('status', params.status);\n if (params?.start_date) queryParams.append('start_date', params.start_date);\n if (params?.end_date) queryParams.append('end_date', params.end_date);\n \n return request(`/api/optimization/history${queryParams.toString() ? `?${queryParams}` : ''}`);\n },\n \n // 取消优化\n cancel: (jobId: string) => {\n return request(`/api/optimization/${jobId}/cancel`, {\n method: 'POST',\n });\n },\n \n // 分析优化问题(预览模式)\n analyze: (params?: {\n memory_type?: string;\n user_id?: string;\n agent_id?: string;\n run_id?: string;\n actor_id?: string;\n similarity_threshold?: number;\n }) => {\n return request('/api/optimization/analyze', {\n method: 'POST',\n body: JSON.stringify(params || {}),\n });\n },\n \n // 清理请求\n cleanup: (params?: {\n max_age_days?: number;\n }) => {\n return request('/api/optimization/cleanup', {\n method: 'POST',\n body: JSON.stringify(params || {}),\n });\n },\n \n // 获取优化统计\n statistics: () => {\n return request('/api/optimization/statistics');\n },\n};\n\n// 系统相关API\nexport const systemApi = {\n // 健康检查\n health: () => {\n return request('/health');\n },\n \n // 系统状态\n status: () => {\n return request('/api/system/status');\n },\n \n // 性能指标\n metrics: () => {\n return request('/api/system/metrics');\n },\n \n // 系统信息\n info: () => {\n return request('/api/system/info');\n },\n \n // 实时日志\n logs: (params?: {\n limit?: number;\n level?: string;\n source?: string;\n }) => {\n const queryParams = new URLSearchParams();\n if (params?.limit) queryParams.append('limit', params.limit.toString());\n if (params?.level) queryParams.append('level', params.level);\n if (params?.source) queryParams.append('source', params.source);\n \n return request(`/api/system/logs${queryParams.toString() ? `?${queryParams}` : ''}`);\n },\n \n // 资源使用情况\n resources: () => {\n return request('/api/system/resources');\n },\n \n // 清理缓存\n clearCache: () => {\n return request('/api/system/clear-cache', {\n method: 'POST',\n });\n },\n \n // 重启服务\n restart: () => {\n return request('/api/system/restart', {\n method: 'POST',\n });\n },\n};\n\n// 通用API\nexport const api = {\n // 测试连接\n testConnection: async () => {\n try {\n const response = await request('/health');\n return {\n connected: true,\n response,\n };\n } catch (error) {\n return {\n connected: false,\n error: error instanceof Error ? error.message : 'Connection failed',\n };\n }\n },\n \n // 获取所有服务状态\n getAllStatus: async () => {\n try {\n const [health, systemStatus, metrics] = await Promise.all([\n systemApi.health(),\n systemApi.status(),\n systemApi.metrics(),\n ]);\n \n return {\n success: true,\n health,\n systemStatus,\n metrics,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get system status',\n };\n }\n },\n};\n\n// 导出所有API\nexport default {\n memory: memoryApi,\n optimization: optimizationApi,\n system: systemApi,\n api,\n};" }, "complexity_metrics": { - "cyclomatic_complexity": 24.0, - "lines_of_code": 744, + "cyclomatic_complexity": 17.0, + "lines_of_code": 331, "number_of_classes": 0, - "number_of_functions": 11 + "number_of_functions": 27 }, "dependencies": [ { - "dependency_type": "framework", - "is_external": true, - "line_number": 1, - "name": "svelte", - "path": null, - "version": null - }, - { - "dependency_type": "library", + "dependency_type": "environment variable", "is_external": true, - "line_number": 4, - "name": "chart.js", + "line_number": 2, + "name": "import.meta.env.VITE_API_URL", "path": null, "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": 12, - "name": "$lib/api/client", - "path": "$lib/api/client", - "version": null } ], - "detailed_description": "This Svelte component implements an analytics dashboard page that visualizes memory data statistics. It loads memory data via API, calculates various analytics including type distribution, quality distribution, time trends, and user statistics, then displays them through charts and summary cards. The component handles loading states, error states, and provides fallback visualization when charts cannot render. It uses Chart.js for data visualization with custom styling and tooltips.", + "detailed_description": "该组件是一个前端API客户端,封装了与后端服务通信的所有HTTP请求。主要功能包括:1) 提供通用的`request`函数用于处理fetch请求,自动设置鉴权头和错误处理;2) 按业务域组织了memory、optimization、system三类API模块,分别对应记忆管理、优化操作和系统监控;3) 支持查询参数构建、POST/PUT/DELETE等方法调用、批量操作和导出功能;4) 包含连接测试和状态聚合等辅助API。所有请求均基于相对路径并通过Vite代理,在生产环境可配置API基础URL。", "interfaces": [ { - "description": null, - "interface_type": "lifecycle", - "name": "onMount", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "callback", - "param_type": "() => Promise" - } - ], - "return_type": "void", - "visibility": "public" + "description": "记忆管理相关的API集合", + "interface_type": "object", + "name": "memoryApi", + "parameters": [], + "return_type": "object", + "visibility": "export" }, { - "description": null, - "interface_type": "function", - "name": "loadAnalyticsData", + "description": "系统优化相关的API集合", + "interface_type": "object", + "name": "optimizationApi", "parameters": [], - "return_type": "Promise", - "visibility": "private" + "return_type": "object", + "visibility": "export" }, { - "description": null, + "description": "系统监控与运维相关的API集合", + "interface_type": "object", + "name": "systemApi", + "parameters": [], + "return_type": "object", + "visibility": "export" + }, + { + "description": "通用辅助API集合", + "interface_type": "object", + "name": "api", + "parameters": [], + "return_type": "object", + "visibility": "export" + }, + { + "description": "通用请求处理函数,封装fetch逻辑", "interface_type": "function", - "name": "calculateImportanceScore", + "name": "request", "parameters": [ { "description": null, "is_optional": false, - "name": "memory", - "param_type": "any" + "name": "endpoint", + "param_type": "string" + }, + { + "description": null, + "is_optional": true, + "name": "options", + "param_type": "RequestInit" } ], - "return_type": "number", + "return_type": "Promise", "visibility": "private" } ], "responsibilities": [ - "Manage analytics data loading and error handling", - "Calculate memory statistics and distributions", - "Render data visualization charts and summary cards", - "Handle loading and error states with appropriate UI feedback", - "Provide interactive analytics dashboard with multiple visualization types" + "封装与后端API的HTTP通信逻辑,提供类型安全的请求方法", + "组织和管理记忆数据的增删改查及搜索、批量操作、导出等功能", + "提供系统优化任务的提交、状态查询、历史记录、分析预览等接口", + "暴露系统健康检查、状态监控、性能指标、日志获取等运维相关API", + "实现连接测试和系统状态聚合等前端友好的辅助功能" ] }, { "code_dossier": { - "code_purpose": "controller", - "description": "Handles terminal UI input events (keyboard and mouse) and routes them to appropriate application actions. Translates raw input into meaningful user commands.", - "file_path": "examples/cortex-mem-tars/src/events.rs", + "code_purpose": "page", + "description": "Svelte page component for system monitoring dashboard with real-time metrics, alerts, and logs visualization.", + "file_path": "cortex-mem-insights/src/routes/monitor/+page.svelte", "functions": [ - "handle_key_event", - "handle_mouse_event", - "process_user_input" + "loadPerformanceMetrics", + "measureHealthLatency", + "getQdrantVersion", + "calculateMemoryUsage", + "calculateCpuUsage", + "calculateNetworkStats", + "calculatePerformanceMetrics", + "generateRealtimeLogs", + "generateAlerts", + "updateMetrics", + "toggleAutoRefresh", + "getLevelColor", + "getTrendIcon", + "getTrendColor", + "acknowledgeAlert" ], "importance_score": 0.8, "interfaces": [ - "handle_key_event", - "process_user_input" + "onMount", + "onDestroy", + "t", + "api.memory.list", + "api.memory.search" ], - "name": "events.rs", - "source_summary": "use crate::app::{App, FocusArea};\nuse crossterm::event::{Event, KeyCode, KeyEventKind, MouseButton, MouseEvent, MouseEventKind};\n\npub fn handle_key_event(event: Event, app: &mut App) -> Option {\n // 处理鼠标事件\n if let Event::Mouse(mouse) = event {\n return handle_mouse_event(mouse, app);\n }\n\n // Some(input)表示需要处理的输入,None表示不需要处理\n if let Event::Key(key) = event {\n if key.kind == KeyEventKind::Press {\n match key.code {\n KeyCode::Enter => {\n if app.focus_area == FocusArea::Input && !app.current_input.trim().is_empty() {\n let input = app.current_input.clone();\n app.current_input.clear();\n app.reset_cursor_to_end();\n app.is_processing = true;\n Some(input) // 返回输入内容给上层处理\n } else {\n None\n }\n }\n KeyCode::Char(c) => {\n if !app.is_processing\n && !app.is_shutting_down\n && app.focus_area == FocusArea::Input\n {\n app.insert_char_at_cursor(c);\n }\n None\n }\n KeyCode::Backspace => {\n if !app.is_processing\n && !app.is_shutting_down\n && app.focus_area == FocusArea::Input\n {\n app.delete_char_at_cursor();\n }\n None\n }\n KeyCode::Left => {\n if !app.is_processing\n && !app.is_shutting_down\n && app.focus_area == FocusArea::Input\n {\n app.move_cursor_left();\n }\n None\n }\n KeyCode::Right => {\n if !app.is_processing\n && !app.is_shutting_down\n && app.focus_area == FocusArea::Input\n {\n app.move_cursor_right();\n }\n None\n }\n KeyCode::Up => {\n // 上键:向后滚动(查看更新内容)\n match app.focus_area {\n FocusArea::Logs => {\n app.scroll_logs_backward();\n }\n FocusArea::Conversation => {\n app.scroll_conversations_backward();\n }\n FocusArea::Input => {}\n }\n None\n }\n KeyCode::Down => {\n // 下键:向前滚动(查看更早内容)\n match app.focus_area {\n FocusArea::Logs => {\n app.scroll_logs_forward();\n }\n FocusArea::Conversation => {\n app.scroll_conversations_forward();\n }\n FocusArea::Input => {}\n }\n None\n }\n KeyCode::Tab => {\n // 切换焦点\n let _old_focus = app.focus_area;\n app.next_focus();\n None\n }\n KeyCode::Home => {\n match app.focus_area {\n FocusArea::Logs => {\n // 滚动到最旧的日志(设置一个较大的偏移量)\n app.log_scroll_offset = app.logs.len().saturating_sub(1);\n app.user_scrolled_logs = true;\n }\n FocusArea::Conversation => {\n // 滚动到最旧的对话(设置一个较大的偏移量)\n let total_lines = app.conversations.len() * 3;\n app.conversation_scroll_offset = total_lines.saturating_sub(1);\n app.user_scrolled_conversations = true;\n }\n FocusArea::Input => {\n // 将光标移动到输入框开头\n app.cursor_position = 0;\n }\n }\n None\n }\n KeyCode::End => {\n match app.focus_area {\n FocusArea::Logs => {\n // 滚动到最新的日志\n app.scroll_logs_to_bottom();\n }\n FocusArea::Conversation => {\n // 滚动到最新的对话\n app.scroll_conversations_to_bottom();\n }\n FocusArea::Input => {\n // 将光标移动到输入框末尾\n app.reset_cursor_to_end();\n }\n }\n None\n }\n KeyCode::Esc => {\n app.should_quit = true;\n app.is_shutting_down = true;\n Some(\"/quit\".to_string()) // 模拟quit命令\n }\n _ => None,\n }\n } else {\n None\n }\n } else {\n None\n }\n}\n\n/// 处理鼠标事件\nfn handle_mouse_event(mouse: MouseEvent, app: &mut App) -> Option {\n match mouse.kind {\n MouseEventKind::Down(MouseButton::Left) => {\n // 左键点击时更新焦点区域\n // 这里可以根据鼠标位置判断点击了哪个区域\n // 简化处理:如果鼠标在左边区域,设置为输入或对话焦点;如果在右边区域,设置为日志焦点\n // 由于我们没有详细的坐标信息,这里只是简化处理\n None\n }\n MouseEventKind::ScrollUp => {\n // 鼠标向上滚动\n match app.focus_area {\n FocusArea::Logs => {\n app.scroll_logs_backward();\n }\n FocusArea::Conversation => {\n app.scroll_conversations_backward();\n }\n FocusArea::Input => {}\n }\n None\n }\n MouseEventKind::ScrollDown => {\n // 鼠标向下滚动\n match app.focus_area {\n FocusArea::Logs => {\n app.scroll_logs_forward();\n }\n FocusArea::Conversation => {\n app.scroll_conversations_forward();\n }\n FocusArea::Input => {}\n }\n None\n }\n MouseEventKind::Drag(MouseButton::Left) => {\n // 鼠标左键拖拽 - 这里我们不需要特别处理,终端默认支持文本选择\n None\n }\n _ => None,\n }\n}\n\npub fn process_user_input(input: String, app: &mut App) -> bool {\n // true表示是quit命令,false表示普通输入\n // 检查是否为退出命令\n let is_quit = input.trim() == \"/quit\";\n if is_quit {\n app.should_quit = true;\n }\n is_quit\n}\n" + "name": "+page.svelte", + "source_summary": "\n\n
\n\t\n\t
\n\t\t
\n\t\t\t

{$t('monitor.title')}

\n\t\t\t

{$t('monitor.description')}

\n\t\t
\n\t\t
\n\t\t\t\n\t\t\t\n\t\t\t\t{$t('monitor.refreshNow')}\n\t\t\t\n\t\t
\n\t
\n\n\t{#if isLoading}\n\t\t\n\t\t
\n\t\t\t{#each Array(3) as _, i}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each Array(3) as _, j}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\t{:else if error}\n\t\t\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t⚠️\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t('common.error')}

\n\t\t\t\t\t

{error}

\n\t\t\t\t\t location.reload()}\n\t\t\t\t\t\tclass=\"mt-2 px-4 py-2 bg-red-500 hover:bg-red-600 text-white rounded-lg text-sm font-medium\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{$t('common.refresh')}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t\n\t{:else}\n\t\t\n\t\t
\n\t\t\t\n\t\t\t {\n\t\t\t\t\t// 服务状态由组件内部处理,这里不需要更新外部状态\n\t\t\t\t}}\n\t\t\t/>\n\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t('monitor.resourceUsage')}

\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t('monitor.memoryUsage')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{systemMetrics.memoryUsage.percentage.toFixed(1)}%\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t 80\n\t\t\t\t\t\t\t\t\t\t? 'bg-red-500'\n\t\t\t\t\t\t\t\t\t\t: systemMetrics.memoryUsage.percentage > 60\n\t\t\t\t\t\t\t\t\t\t\t? 'bg-yellow-500'\n\t\t\t\t\t\t\t\t\t\t\t: 'bg-green-500'\n\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\tstyle={`width: ${systemMetrics.memoryUsage.percentage}%`}\n\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{systemMetrics.memoryUsage.used.toFixed(1)} MB\n\t\t\t\t\t\t\t{systemMetrics.memoryUsage.total} MB\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t('monitor.cpuUsage')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{systemMetrics.cpuUsage.percentage.toFixed(1)}%\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t 70\n\t\t\t\t\t\t\t\t\t\t? 'bg-red-500'\n\t\t\t\t\t\t\t\t\t\t: systemMetrics.cpuUsage.percentage > 40\n\t\t\t\t\t\t\t\t\t\t\t? 'bg-yellow-500'\n\t\t\t\t\t\t\t\t\t\t\t: 'bg-green-500'\n\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\tstyle={`width: ${systemMetrics.cpuUsage.percentage}%`}\n\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
{$t('monitor.networkStatus')}
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{$t('monitor.activeConnections')}: {systemMetrics.network.activeConnections}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{$t('monitor.throughput')}: {systemMetrics.network.throughput}
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t('monitor.performanceMetrics')}

\n\n\t\t\t\t
\n\t\t\t\t\t{#each performanceMetrics as metric}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{metric.name === 'API响应时间' ? $t('monitor.apiResponseTime') : \n\t\t\t\t\t\t\t\t\t metric.name === '搜索延迟' ? $t('monitor.searchLatency') :\n\t\t\t\t\t\t\t\t\t metric.name === '健康检查' ? $t('monitor.healthCheck') :\n\t\t\t\t\t\t\t\t\t metric.name === '向量查询' ? $t('monitor.vectorQuery') : metric.name}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{getTrendIcon(metric.trend)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{metric.value.toFixed(0)}{metric.unit}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t metric.threshold * 0.8\n\t\t\t\t\t\t\t\t\t\t\t? 'bg-red-500'\n\t\t\t\t\t\t\t\t\t\t\t: metric.value > metric.threshold * 0.6\n\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-yellow-500'\n\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-green-500'\n\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\tstyle={`width: ${Math.min(metric.value / metric.threshold, 1) * 100}%`}\n\t\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{$t('monitor.threshold')}: {metric.threshold}{metric.unit}\n\t\t\t\t\t\t\t\t{$t('monitor.usageRate')}: {((metric.value / metric.threshold) * 100).toFixed(1)}%\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t('monitor.systemAlerts')}

\n\t\t\t\t\t\n\t\t\t\t\t\t{alerts.filter((a) => !a.acknowledged).length} {$t('monitor.unprocessed')}\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t{#each alerts as alert}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{alert.level === 'error'\n\t\t\t\t\t\t\t\t\t\t\t\t? $t('monitor.error')\n\t\t\t\t\t\t\t\t\t\t\t\t: alert.level === 'warning'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? $t('monitor.warning')\n\t\t\t\t\t\t\t\t\t\t\t\t\t: $t('monitor.info')}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{#if !alert.acknowledged}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{$t('monitor.unprocessed')}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t{alert.message}\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t{alert.time}\n\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t('monitor.realtimeLogs')}

\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t('monitor.lastUpdated')}: {lastUpdate || $t('common.unknown')}\n\t\t\t\t\t\t\n\t\t\t\t\t\t (realtimeLogs = [])}\n\t\t\t\t\t\t\tclass=\"px-3 py-1 text-sm bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{$t('monitor.clear')}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{#if realtimeLogs.length === 0}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t('monitor.noLogs')}\n\t\t\t\t\t\t
\n\t\t\t\t\t{:else}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each realtimeLogs as log}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{log.time}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{log.level === 'error' ? 'ERR' : log.level === 'warning' ? 'WARN' : 'INFO'}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{log.message}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t
\n\t\t\t

{$t('monitor.monitoringTools')}

\n\n\t\t\t
\n\t\t\t\t console.log('健康检查')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t❤️\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{$t('monitor.healthCheckTool')}

\n\t\t\t\t\t\t\t

{$t('monitor.comprehensiveHealthCheck')}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('性能测试')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{$t('monitor.performanceTest')}

\n\t\t\t\t\t\t\t

{$t('monitor.runPerformanceBenchmark')}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('诊断工具')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t🔧\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{$t('monitor.diagnosticTools')}

\n\t\t\t\t\t\t\t

{$t('monitor.systemDiagnosisAndRepair')}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t{/if}\n\n" }, "complexity_metrics": { - "cyclomatic_complexity": 18.0, - "lines_of_code": 197, + "cyclomatic_complexity": 32.0, + "lines_of_code": 792, "number_of_classes": 0, - "number_of_functions": 3 + "number_of_functions": 15 }, "dependencies": [ { - "dependency_type": "module", + "dependency_type": "framework", + "is_external": true, + "line_number": null, + "name": "svelte", + "path": "$lib/api/client", + "version": null + }, + { + "dependency_type": "service", "is_external": false, - "line_number": 1, - "name": "crate::app", - "path": "crate::app", + "line_number": null, + "name": "api", + "path": "$lib/api/client", "version": null }, { - "dependency_type": "library", - "is_external": true, - "line_number": 2, - "name": "crossterm", - "path": "crossterm::event", + "dependency_type": "component", + "is_external": false, + "line_number": null, + "name": "ServiceStatus", + "path": "$lib/components/ServiceStatus.svelte", + "version": null + }, + { + "dependency_type": "function", + "is_external": false, + "line_number": null, + "name": "t", + "path": "$lib/i18n", "version": null } ], - "detailed_description": "This controller component manages all user input events in a terminal-based application using Crossterm. It processes keyboard and mouse events, updating the application state accordingly. The component distinguishes between different focus areas (Input, Logs, Conversation) and handles navigation, text input, scrolling, and application lifecycle commands. Key events like Enter, arrow keys, Home/End, and Esc are mapped to specific behaviors. Mouse scrolling is supported when focused on scrollable areas. The component returns processed input to the caller via Option, enabling command execution (like '/quit'). It serves as an intermediary between low-level terminal events and high-level application logic, effectively decoupling input handling from business logic.", + "detailed_description": "This Svelte page component serves as the main monitoring dashboard for the system, providing real-time visualization of system health metrics, performance indicators, alerts, and logs. It fetches data through API calls to monitor memory usage, CPU load, network statistics, and service health. The component implements automatic refresh functionality with configurable intervals and handles loading and error states appropriately. It calculates system metrics based on memory data and generates performance metrics by measuring API response times and search latencies. The UI displays resource usage with color-coded progress bars, performance metrics with trend indicators, and presents alerts and logs in dedicated sections. The component also includes interactive features like manual refresh, auto-refresh toggle, and alert acknowledgment.", "interfaces": [ { - "description": "Processes a terminal event and returns Some(input) if input should be processed by upper layers, None otherwise", + "description": "Svelte lifecycle hook that initializes data loading and sets up auto-refresh interval", + "interface_type": "lifecycle", + "name": "onMount", + "parameters": [], + "return_type": "void", + "visibility": "private" + }, + { + "description": "Svelte lifecycle hook that cleans up refresh interval to prevent memory leaks", + "interface_type": "lifecycle", + "name": "onDestroy", + "parameters": [], + "return_type": "void", + "visibility": "private" + }, + { + "description": "Primary data loading function that orchestrates fetching of all system metrics and updates component state", "interface_type": "function", - "name": "handle_key_event", - "parameters": [ - { - "description": "The raw terminal event to process", - "is_optional": false, - "name": "event", - "param_type": "Event" - }, - { - "description": "Mutable reference to the application state", - "is_optional": false, - "name": "app", - "param_type": "App" - } - ], - "return_type": "Option", - "visibility": "public" + "name": "loadPerformanceMetrics", + "parameters": [], + "return_type": "Promise", + "visibility": "private" }, { - "description": null, + "description": "Function to refresh all metrics, used by auto-refresh interval and manual refresh button", "interface_type": "function", - "name": "handle_mouse_event", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "mouse", - "param_type": "MouseEvent" - }, - { - "description": null, - "is_optional": false, - "name": "app", - "param_type": "App" - } - ], - "return_type": "Option", + "name": "updateMetrics", + "parameters": [], + "return_type": "Promise", "visibility": "private" }, { - "description": "Processes submitted user input and returns true if it's a quit command", + "description": "Toggles auto-refresh functionality and manages the refresh interval timer", "interface_type": "function", - "name": "process_user_input", + "name": "toggleAutoRefresh", + "parameters": [], + "return_type": "void", + "visibility": "private" + }, + { + "description": "Marks a specific alert as acknowledged, updating the UI state", + "interface_type": "function", + "name": "acknowledgeAlert", "parameters": [ { - "description": null, - "is_optional": false, - "name": "input", - "param_type": "String" - }, - { - "description": null, + "description": "ID of the alert to acknowledge", "is_optional": false, - "name": "app", - "param_type": "App" + "name": "alertId", + "param_type": "string" } ], - "return_type": "bool", - "visibility": "public" + "return_type": "void", + "visibility": "private" } ], "responsibilities": [ - "Handle and route keyboard input events based on current application focus and state", - "Process mouse events including scrolling and clicks for UI interaction", - "Manage user input lifecycle including text entry, cursor movement, and deletion", - "Translate user input into application commands and state changes", - "Coordinate focus management and scrolling behavior across different UI components" + "Provide real-time system monitoring dashboard with visualized metrics", + "Fetch and calculate system performance metrics including memory, CPU, and network usage", + "Generate and display system alerts based on threshold violations", + "Manage real-time log collection and presentation", + "Handle automatic and manual data refresh with proper lifecycle management" ] }, { "code_dossier": { - "code_purpose": "widget", - "description": "UI 绘制函数,负责渲染基于终端的用户界面,包含对话历史、输入框和系统日志三个主要区域,并支持焦点切换、滚动浏览和实时反馈。", - "file_path": "examples/cortex-mem-tars/src/ui.rs", + "code_purpose": "router", + "description": "Svelte component for the dashboard page of Cortex Mem Insights. Handles data loading, state management, and UI rendering for system metrics and recent memories.", + "file_path": "cortex-mem-insights/src/routes/+page.svelte", "functions": [ - "draw_ui" + "loadBasicData", + "calculateQualityDistribution", + "fallbackToMockData", + "formatImportance", + "getImportanceColor", + "formatDate" ], "importance_score": 0.8, "interfaces": [ - "draw_ui" + "onMount", + "t", + "format", + "api.memory.list", + "ServiceStatus", + "on:statusUpdate" ], - "name": "ui.rs", - "source_summary": "use ratatui::{\n Frame,\n layout::{Constraint, Direction, Layout},\n style::{Color, Modifier, Style},\n text::{Line, Span, Text},\n widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, Wrap},\n};\n\nuse crate::app::{App, FocusArea};\nuse unicode_width::UnicodeWidthChar;\n\n/// UI 绘制函数\npub fn draw_ui(f: &mut Frame, app: &mut App) {\n // 创建主布局\n let chunks = Layout::default()\n .direction(Direction::Horizontal)\n .constraints([Constraint::Percentage(70), Constraint::Percentage(30)])\n .split(f.area());\n\n // 左列:对话区域和输入框\n let left_chunks = Layout::default()\n .direction(Direction::Vertical)\n .constraints([Constraint::Percentage(75), Constraint::Percentage(25)])\n .split(chunks[0]);\n\n // 对话历史 - 构建所有对话文本,包括正在流式生成的内容\n let display_conversations = app.get_display_conversations();\n let conversation_text = display_conversations\n .iter()\n .rev() // 反转顺序,使最新对话显示在前面\n .enumerate()\n .flat_map(|(index, (user, assistant, timestamp))| {\n // 由于反转了顺序,流式生成的对话现在是第一个(index == 0)\n let is_streaming = app.current_streaming_response.is_some() && \n index == 0;\n \n let assistant_style = if is_streaming {\n Style::default().fg(Color::Yellow) // 流式生成中用黄色\n } else {\n Style::default().fg(Color::Green) // 完成的回复用绿色\n };\n \n let assistant_prefix = if is_streaming {\n \"助手 (生成中): \"\n } else {\n \"助手: \"\n };\n \n // 格式化时间戳\n let time_str = if let Some(ts) = timestamp {\n format!(\" [{}]\", ts.format(\"%H:%M:%S\"))\n } else {\n String::new()\n };\n \n vec![\n Line::from(vec![\n Span::styled(\"用户: \", Style::default().fg(Color::Cyan)),\n Span::raw(user.clone()),\n Span::styled(time_str.clone(), Style::default().fg(Color::DarkGray)),\n ]),\n Line::from(vec![\n Span::styled(assistant_prefix, assistant_style),\n Span::styled(assistant.clone(), assistant_style),\n if is_streaming {\n Span::styled(\"▋\", Style::default().fg(Color::Yellow)) // 光标效果\n } else {\n Span::raw(\"\")\n }\n ]),\n Line::from(\"\"), // 空行分隔\n ]\n })\n .collect::>();\n\n let total_conversations = display_conversations.len();\n\n // 构建对话区域标题,显示滚动状态和焦点状态\n let conversation_title = if app.focus_area == FocusArea::Conversation {\n if total_conversations > 0 {\n format!(\n \"💬 对话历史 ({} 对, 偏移:{}) [Tab切换焦点 ↑向后 ↓向前 Home/End快速跳转]\",\n total_conversations, app.conversation_scroll_offset\n )\n } else {\n format!(\"💬 对话历史 (0 对) [Tab切换焦点]\")\n }\n } else {\n if total_conversations > 0 {\n format!(\n \"对话历史 ({} 对, 偏移:{}) [Tab切换焦点]\",\n total_conversations, app.conversation_scroll_offset\n )\n } else {\n format!(\"对话历史 (0 对) [Tab切换焦点]\")\n }\n };\n\n let conversation_paragraph = Paragraph::new(conversation_text)\n .block(\n Block::default()\n .borders(Borders::ALL)\n .title(conversation_title)\n .title_style(if app.focus_area == FocusArea::Conversation {\n Style::default()\n .fg(Color::Cyan)\n .add_modifier(Modifier::BOLD)\n } else {\n Style::default().fg(Color::White)\n }),\n )\n .style(Style::default().bg(Color::Black))\n .wrap(ratatui::widgets::Wrap { trim: true })\n .scroll((app.conversation_scroll_offset as u16, 0));\n\n f.render_widget(Clear, left_chunks[0]);\n f.render_widget(conversation_paragraph, left_chunks[0]);\n\n // 渲染会话区滚动条\n if total_conversations > 0 {\n let total_lines = total_conversations * 3; // 每个对话3行\n let visible_height = left_chunks[0].height.saturating_sub(2) as usize; // 减去边框\n\n // 更新滚动条状态,使用实际的可见高度\n app.conversation_scrollbar_state = app\n .conversation_scrollbar_state\n .content_length(total_lines)\n .viewport_content_length(visible_height)\n .position(app.conversation_scroll_offset);\n\n f.render_stateful_widget(\n Scrollbar::new(ScrollbarOrientation::VerticalRight)\n .begin_symbol(Some(\"↑\"))\n .end_symbol(Some(\"↓\")),\n left_chunks[0],\n &mut app.conversation_scrollbar_state,\n );\n }\n\n // 输入区域 - 根据状态显示不同的内容\n if app.is_shutting_down {\n // 在shutting down时显示说明文案,不显示输入框\n let shutdown_text = Paragraph::new(Text::from(\n \"正在执行记忆化存储,请稍候...\\n\\n系统将自动保存本次对话记录到记忆库中。\",\n ))\n .style(\n Style::default()\n .fg(Color::Yellow)\n .add_modifier(Modifier::BOLD),\n )\n .block(\n Block::default()\n .borders(Borders::ALL)\n .title(\"正在退出程序... (记忆迭代中)\")\n .title_style(\n Style::default()\n .fg(Color::Yellow)\n .add_modifier(Modifier::BOLD),\n ),\n )\n .wrap(Wrap { trim: true });\n\n f.render_widget(Clear, left_chunks[1]);\n f.render_widget(shutdown_text, left_chunks[1]);\n // 不设置光标,光标会自动隐藏\n } else {\n // 正常状态显示输入框\n let input_title = if app.focus_area == FocusArea::Input {\n \"📝 输入消息 (Enter发送, Tab切换焦点, /quit退出)\"\n } else {\n \"输入消息 (Enter发送, Tab切换焦点, /quit退出)\"\n };\n\n let input_paragraph = Paragraph::new(Text::from(app.current_input.as_str()))\n .style(Style::default().fg(Color::White))\n .block(\n Block::default()\n .borders(Borders::ALL)\n .title(input_title)\n .title_style(if app.focus_area == FocusArea::Input {\n Style::default()\n .fg(Color::Cyan)\n .add_modifier(Modifier::BOLD)\n } else {\n Style::default().fg(Color::White)\n }),\n )\n .wrap(Wrap { trim: true });\n\n f.render_widget(Clear, left_chunks[1]);\n f.render_widget(input_paragraph, left_chunks[1]);\n\n // 只有当焦点在输入框时才设置光标\n if app.focus_area == FocusArea::Input {\n // 计算输入框可用宽度(减去边框和边距)\n let available_width = left_chunks[1].width.saturating_sub(2) as usize;\n\n // 使用ratatui的wrap逻辑来计算光标位置\n // 我们需要模拟ratatui::widgets::Wrap的行为\n\n // 获取光标前的所有字符\n let chars_before_cursor: Vec = app\n .current_input\n .chars()\n .take(app.cursor_position)\n .collect();\n\n // 模拟ratatui的换行逻辑\n let mut line_offset = 0;\n let mut current_line_width = 0;\n\n // 遍历光标前的所有字符,计算换行\n for ch in chars_before_cursor {\n let char_width = ch.width().unwrap_or(0);\n\n // 如果当前字符会超出行宽,则换行\n if current_line_width + char_width > available_width {\n line_offset += 1;\n current_line_width = 0;\n }\n\n current_line_width += char_width;\n }\n\n // 计算最终的光标位置\n let cursor_x = left_chunks[1].x + 1 + current_line_width as u16;\n let cursor_y = left_chunks[1].y + 1 + line_offset as u16;\n\n // 确保光标在输入框范围内\n if cursor_y < left_chunks[1].y + left_chunks[1].height {\n f.set_cursor_position((cursor_x, cursor_y));\n }\n }\n }\n\n // 右列:日志区域 - 构建所有日志文本,使用Paragraph的scroll功能\n let total_logs = app.logs.len();\n\n // 构建要显示的日志文本,反转顺序使最新日志显示在前面\n let log_text = app\n .logs\n .iter()\n .rev() // 反转顺序,使最新日志显示在前面\n .map(|log| {\n let style = if log.starts_with(\"[WARN]\") {\n Style::default().fg(Color::Yellow)\n } else if log.starts_with(\"[ERROR]\") {\n Style::default().fg(Color::Red)\n } else {\n Style::default().fg(Color::Gray)\n };\n\n Line::from(Span::styled(log.clone(), style))\n })\n .collect::>();\n\n // 构建日志区域标题,显示滚动状态和焦点状态\n let log_title = if app.focus_area == FocusArea::Logs {\n if total_logs > 0 {\n format!(\n \"🔍 系统日志 ({} 行, 偏移:{}) [Tab切换焦点 ↑向后 ↓向前 Home/End快速跳转]\",\n total_logs, app.log_scroll_offset\n )\n } else {\n format!(\"🔍 系统日志 (0 行) [Tab切换焦点]\")\n }\n } else {\n if total_logs > 0 {\n format!(\n \"系统日志 ({} 行, 偏移:{}) [Tab切换焦点]\",\n total_logs, app.log_scroll_offset\n )\n } else {\n format!(\"系统日志 (0 行) [Tab切换焦点]\")\n }\n };\n\n let log_paragraph = Paragraph::new(log_text)\n .block(\n Block::default()\n .borders(Borders::ALL)\n .title(log_title)\n .title_style(if app.focus_area == FocusArea::Logs {\n Style::default()\n .fg(Color::Cyan)\n .add_modifier(Modifier::BOLD)\n } else {\n Style::default().fg(Color::White)\n }),\n )\n .style(Style::default().bg(Color::Black))\n .wrap(ratatui::widgets::Wrap { trim: true })\n .scroll((app.log_scroll_offset as u16, 0));\n\n f.render_widget(Clear, chunks[1]);\n f.render_widget(log_paragraph, chunks[1]);\n\n // 渲染日志区滚动条\n if total_logs > 0 {\n let visible_height = chunks[1].height.saturating_sub(2) as usize; // 减去边框\n\n // 更新滚动条状态,使用实际的可见高度\n app.log_scrollbar_state = app\n .log_scrollbar_state\n .content_length(total_logs)\n .viewport_content_length(visible_height)\n .position(app.log_scroll_offset);\n\n f.render_stateful_widget(\n Scrollbar::new(ScrollbarOrientation::VerticalRight)\n .begin_symbol(Some(\"↑\"))\n .end_symbol(Some(\"↓\")),\n chunks[1],\n &mut app.log_scrollbar_state,\n );\n }\n\n // 不再使用全屏覆盖层,保持所有UI区域可见\n // 这样用户可以在日志区域看到详细的quit执行过程\n}\n" + "name": "+page.svelte", + "source_summary": "\n\n
\n\t\n\t
\n\t\t

{$t('dashboard.title')}

\n\t\t

{$t('dashboard.welcome')}

\n\t
\n\n\t{#if isLoading}\n\t\t\n\t\t
\n\t\t\t{#each Array(4) as _, i}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\t{:else}\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t('dashboard.totalMemories')}

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{stats.totalMemories.toLocaleString()}\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t📚\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t

\n\t\t\t\t\t高质量记忆: {stats.qualityDistribution.high}\n\t\t\t\t

\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t('analytics.averageQuality')}

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{stats.averageQuality.toFixed(2)}\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t('dashboard.qualityDistribution')}

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{stats.qualityDistribution.high}/{stats.qualityDistribution.medium}/{stats\n\t\t\t\t\t\t\t\t.qualityDistribution.low}\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t📊\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t

高/中/低质量记忆数量

\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t {\n\t\t\t\t\t\tsystemStatus = event.detail.systemStatus;\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t('dashboard.recentMemories')}

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t查看全部 →\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each recentMemories as memory}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{formatImportance(memory.importance)}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{memory.type}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\t{memory.content}\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\tID: {memory.id}\n\t\t\t\t\t\t\t\t\t\t\t{memory.createdAt}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t console.log('查看详情', memory.id)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t🔍\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t
\n\t\t\t

快速操作

\n\n\t\t\t
\n\t\t\t\t console.log('运行优化')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{$t('optimization.runOptimization')}

\n\t\t\t\t\t\t\t

清理重复和低质量记忆

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('导出数据')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t📥\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

导出数据

\n\t\t\t\t\t\t\t

导出记忆为JSON/CSV格式

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('查看报告')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t📊\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

生成报告

\n\t\t\t\t\t\t\t

生成系统运行分析报告

\n\t\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t{/if}\n\n\n\n" }, "complexity_metrics": { - "cyclomatic_complexity": 24.0, - "lines_of_code": 320, + "cyclomatic_complexity": 8.0, + "lines_of_code": 478, "number_of_classes": 0, - "number_of_functions": 1 + "number_of_functions": 6 }, "dependencies": [ { - "dependency_type": "library", + "dependency_type": "framework", "is_external": true, - "line_number": 1, - "name": "ratatui", - "path": null, + "line_number": null, + "name": "svelte", + "path": "svelte", "version": null }, { - "dependency_type": "library", - "is_external": true, - "line_number": 9, - "name": "unicode_width", - "path": null, + "dependency_type": "local", + "is_external": false, + "line_number": null, + "name": "$lib/api/client", + "path": "$lib/api/client", "version": null }, { - "dependency_type": "struct", + "dependency_type": "local", "is_external": false, - "line_number": 11, - "name": "App", - "path": "crate::app::App", + "line_number": null, + "name": "$lib/components/ServiceStatus.svelte", + "path": "$lib/components/ServiceStatus.svelte", "version": null }, { - "dependency_type": "enum", + "dependency_type": "local", "is_external": false, - "line_number": 11, - "name": "FocusArea", - "path": "crate::app::FocusArea", + "line_number": null, + "name": "$lib/i18n", + "path": "$lib/i18n", "version": null } ], - "detailed_description": "该组件是终端用户界面渲染核心,基于 ratatui 库构建三层布局:左侧分为对话历史区(75%)和输入框(25%),右侧为系统日志区(30%)。支持多焦点管理(对话、输入、日志),通过 FocusArea 枚举控制标题样式与交互提示。对话区可显示流式生成中的内容(黄色高亮+光标动画),并支持垂直滚动与分页导航(Home/End/↑/↓)。输入框智能计算光标位置,考虑字符宽度与换行逻辑,确保在宽字符环境下正确显示。日志区按级别着色(ERROR红色,WARN黄色)。整体UI在程序退出时显示记忆化存储状态,提升用户体验透明度。", + "detailed_description": "This Svelte component serves as the main dashboard page for the Cortex Mem Insights application. It is responsible for fetching and displaying key system metrics including memory statistics, quality distribution, and recent memory entries. The component initializes by loading basic data on mount, attempting to retrieve memory data from the API and calculating derived statistics such as average quality and distribution. If data loading fails, it gracefully falls back to mock/default data. The UI presents statistics in a grid of cards, system status via a dedicated ServiceStatus component, and a list of recent memories with visual indicators for importance and type. The component also includes quick action buttons for optimization, export, and reporting. It handles loading states with skeleton screens and uses i18n for text localization. The code is well-structured with clear separation of data loading logic, utility functions, and UI rendering.", "interfaces": [ { - "description": "主渲染函数,根据应用状态绘制完整UI", + "description": "Svelte lifecycle function that runs when component is mounted", "interface_type": "function", - "name": "draw_ui", - "parameters": [ - { - "description": "UI帧上下文,用于渲染组件", - "is_optional": false, - "name": "f", - "param_type": "&mut Frame" - }, - { - "description": "应用状态引用,包含对话、日志、焦点等数据", + "name": "onMount", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Loads basic dashboard data including memories and calculates statistics", + "interface_type": "function", + "name": "loadBasicData", + "parameters": [], + "return_type": "Promise", + "visibility": "private" + }, + { + "description": "Calculates quality distribution based on memory importance scores", + "interface_type": "function", + "name": "calculateQualityDistribution", + "parameters": [ + { + "description": "Array of memory objects", "is_optional": false, - "name": "app", - "param_type": "&mut App" + "name": "memories", + "param_type": "any[]" } ], - "return_type": null, - "visibility": "public" - } - ], - "responsibilities": [ - "渲染分栏式终端UI界面,管理对话、输入、日志三大区域布局", - "实现焦点感知的视觉反馈与交互提示", - "支持对话与日志内容的滚动浏览及状态同步", - "处理流式AI响应的动态视觉呈现(如黄光标动画)", - "提供输入框光标定位与换行计算逻辑" - ] - }, - { - "code_dossier": { - "code_purpose": "util", - "description": "Provides a terminal cleanup utility function to reset terminal state without clearing screen content.", - "file_path": "examples/cortex-mem-tars/src/terminal.rs", - "functions": [ - "cleanup_terminal_final" - ], - "importance_score": 0.8, - "interfaces": [ - "cleanup_terminal_final" - ], - "name": "terminal.rs", - "source_summary": "// use crossterm::execute;\n// use std::io::Write;\n\n/// 终极终端清理函数\npub fn cleanup_terminal_final(_terminal: &mut ratatui::Terminal>) {\n // 直接使用标准输出流进行最彻底的清理\n // let mut stdout = std::io::stdout();\n \n // // 执行必要的重置命令,但不清除屏幕内容\n // let _ = execute!(&mut stdout, crossterm::style::ResetColor);\n // let _ = execute!(&mut stdout, crossterm::cursor::Show);\n // let _ = execute!(&mut stdout, crossterm::terminal::LeaveAlternateScreen);\n // let _ = execute!(&mut stdout, crossterm::event::DisableMouseCapture);\n // let _ = execute!(&mut stdout, crossterm::style::SetAttribute(crossterm::style::Attribute::Reset));\n // let _ = execute!(&mut stdout, crossterm::style::SetForegroundColor(crossterm::style::Color::Reset));\n // let _ = execute!(&mut stdout, crossterm::style::SetBackgroundColor(crossterm::style::Color::Reset));\n \n // // 禁用原始模式\n // let _ = crossterm::terminal::disable_raw_mode();\n \n // // 立即刷新输出\n // let _ = stdout.flush();\n \n // // 只重置样式,不清除屏幕内容\n // let style_reset = \"\\x1b[0m\\x1b[?25h\";\n // print!(\"{}\", style_reset);\n // let _ = stdout.flush();\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 28, - "number_of_classes": 0, - "number_of_functions": 1 - }, - "dependencies": [], - "detailed_description": "The component contains a single utility function `cleanup_terminal_final` that is designed to perform final cleanup operations on a terminal interface managed by the ratatui and crossterm libraries. Although the implementation is currently commented out, its intended functionality is to reset terminal styling, show the cursor, exit alternate screen mode, disable mouse capture, and disable raw mode—all while preserving the screen content. The function accepts a mutable reference to a ratatui Terminal instance using CrosstermBackend and performs low-level terminal state resets via crossterm macros. A final ANSI escape sequence is printed directly to restore styling and cursor visibility. This utility is meant to be called during application shutdown to leave the user's terminal in a usable state.", - "interfaces": [ + "return_type": "{ average: number, distribution: { high: number, medium: number, low: number } }", + "visibility": "private" + }, { - "description": "Performs final terminal cleanup operations to restore default terminal state", + "description": "Sets default/mock data when API calls fail", "interface_type": "function", - "name": "cleanup_terminal_final", + "name": "fallbackToMockData", + "parameters": [], + "return_type": "void", + "visibility": "private" + }, + { + "description": "Formats importance score to 2 decimal places", + "interface_type": "function", + "name": "formatImportance", "parameters": [ { - "description": "Mutable reference to the active terminal instance (unused in current implementation)", + "description": "Importance score to format", "is_optional": false, - "name": "_terminal", - "param_type": "&mut ratatui::Terminal>" + "name": "importance", + "param_type": "number" } ], - "return_type": "()", - "visibility": "public" + "return_type": "string", + "visibility": "private" + }, + { + "description": "Returns CSS classes for importance-based coloring", + "interface_type": "function", + "name": "getImportanceColor", + "parameters": [ + { + "description": "Importance score to get color for", + "is_optional": false, + "name": "importance", + "param_type": "number" + } + ], + "return_type": "string", + "visibility": "private" + }, + { + "description": "Formats ISO date string to localized format", + "interface_type": "function", + "name": "formatDate", + "parameters": [ + { + "description": "ISO date string to format", + "is_optional": false, + "name": "isoString", + "param_type": "string" + } + ], + "return_type": "string", + "visibility": "private" } ], "responsibilities": [ - "Reset terminal styling attributes to default (color, background, text attributes)", - "Restore cursor visibility and exit alternate screen mode", - "Safely disable raw input mode and mouse capture", - "Preserve terminal screen content during cleanup", - "Ensure terminal state consistency upon application exit" + "Manage dashboard state including memory statistics, system status, and recent memories", + "Load and process memory data from API on component mount", + "Calculate derived metrics such as quality distribution and average quality", + "Handle errors gracefully by falling back to mock data", + "Render UI components for statistics, system status, recent memories, and quick actions", + "Manage loading states with skeleton screens", + "Format and display localized text using i18n" ] }, { "code_dossier": { - "code_purpose": "agent", - "description": "Agent component providing intelligent assistant functionality with memory capabilities.", - "file_path": "examples/cortex-mem-tars/src/agent.rs", + "code_purpose": "router", + "description": "Svelte page component for memory optimization management in Cortex-MEM Insights. Provides UI for initiating, monitoring, and reviewing memory optimization tasks.", + "file_path": "cortex-mem-insights/src/routes/optimization/+page.svelte", "functions": [ - "create_memory_agent", - "extract_user_basic_info", - "agent_reply_with_memory_retrieval_streaming", - "store_conversations_batch" + "onMount", + "loadOptimizationData", + "getSeverityLevel", + "showIssueDetail", + "closeDetailModal", + "viewOptimizationReport", + "rollbackOptimization", + "clearOptimizationHistory", + "getStatusColor", + "getSeverityColor", + "startOptimization", + "startPolling", + "stopPolling", + "cancelOptimization", + "getEstimatedImpact" ], "importance_score": 0.8, - "interfaces": [], - "name": "agent.rs", - "source_summary": "use cortex_mem_config::Config;\nuse cortex_mem_core::memory::MemoryManager;\nuse cortex_mem_rig::{ListMemoriesArgs, create_memory_tools, tool::MemoryToolConfig};\nuse rig::{\n agent::Agent,\n client::CompletionClient,\n providers::openai::{Client, CompletionModel},\n tool::Tool,\n};\n\nuse std::sync::Arc;\n\n// 导入日志重定向函数\nuse crate::app::redirect_log_to_ui;\n\n/// 创建带记忆功能的Agent\npub async fn create_memory_agent(\n memory_manager: Arc,\n memory_tool_config: MemoryToolConfig,\n config: &Config,\n) -> Result, Box> {\n // 创建记忆工具\n let memory_tools =\n create_memory_tools(memory_manager.clone(), &config, Some(memory_tool_config));\n\n let llm_client = Client::builder(&config.llm.api_key)\n .base_url(&config.llm.api_base_url)\n .build();\n\n // 构建带有记忆工具的agent,让agent能够自主决定何时调用记忆功能\n let completion_model = llm_client\n .completion_model(&config.llm.model_efficient)\n .completions_api()\n .into_agent_builder()\n // 注册四个独立的记忆工具,保持与MCP一致\n .tool(memory_tools.store_memory())\n .tool(memory_tools.query_memory())\n .tool(memory_tools.list_memories())\n .tool(memory_tools.get_memory())\n .preamble(r#\"你是一个拥有记忆功能的智能AI助手。你可以访问和使用记忆工具来检索、存储和管理用户信息。\n\n你的工具:\n- CortexMemoryTool: 可以存储、搜索和检索记忆。支持以下操作:\n * store: 存储新记忆\n * search: 搜索相关记忆\n * recall: 召回上下文\n * get: 获取特定记忆\n\n重要指令:\n- 对话历史将作为上下文提供,请使用这些信息来理解当前的对话流程\n- 用户基本信息将在上下文中提供一次,请不要再使用memory工具来创建或更新用户基本信息\n- 在需要时可以自主使用memory工具搜索其他相关记忆\n- 当用户提供新的重要信息时,可以主动使用memory工具存储\n- 保持对话的连贯性和一致性\n- 自然地融入记忆信息,避免显得刻意\n- 专注于用户的需求和想要了解的信息,以及想要你做的事情\n\n记住:你正在与一个了解的用户进行连续对话,对话过程中不需要刻意表达你的记忆能力。\"#)\n .build();\n\n Ok(completion_model)\n}\n\n/// 从记忆中提取用户基本信息\npub async fn extract_user_basic_info(\n config: &Config,\n memory_manager: Arc,\n user_id: &str,\n) -> Result, Box> {\n let memory_tools = create_memory_tools(\n memory_manager,\n config,\n Some(MemoryToolConfig {\n default_user_id: Some(user_id.to_string()),\n ..Default::default()\n }),\n );\n\n let mut context = String::new();\n\n let search_args_personal = ListMemoriesArgs {\n limit: Some(20),\n memory_type: Some(\"personal\".to_string()), // 使用小写以匹配新API\n user_id: Some(user_id.to_string()),\n agent_id: None,\n };\n\n let search_args_factual = ListMemoriesArgs {\n limit: Some(20),\n memory_type: Some(\"factual\".to_string()), // 使用小写以匹配新API\n user_id: Some(user_id.to_string()),\n agent_id: None,\n };\n\n if let Ok(search_result) = memory_tools\n .list_memories()\n .call(search_args_personal)\n .await\n {\n if let Some(data) = search_result.data {\n // 根据新的MCP格式调整数据结构访问\n if let Some(results) = data.get(\"memories\").and_then(|r| r.as_array()) {\n if !results.is_empty() {\n context.push_str(\"用户基本信息 - 特征:\\n\");\n for (i, result) in results.iter().enumerate() {\n if let Some(content) = result.get(\"content\").and_then(|c| c.as_str()) {\n context.push_str(&format!(\"{}. {}\\n\", i + 1, content));\n }\n }\n return Ok(Some(context));\n }\n }\n }\n }\n\n if let Ok(search_result) = memory_tools.list_memories().call(search_args_factual).await {\n if let Some(data) = search_result.data {\n if let Some(results) = data.get(\"memories\").and_then(|r| r.as_array()) {\n if !results.is_empty() {\n context.push_str(\"用户基本信息 - 事实:\\n\");\n for (i, result) in results.iter().enumerate() {\n if let Some(content) = result.get(\"content\").and_then(|c| c.as_str()) {\n context.push_str(&format!(\"{}. {}\\n\", i + 1, content));\n }\n }\n return Ok(Some(context));\n }\n }\n }\n }\n\n match context.len() > 0 {\n true => Ok(Some(context)),\n false => Ok(None),\n }\n}\n\nuse futures::StreamExt;\nuse rig::agent::MultiTurnStreamItem;\nuse rig::completion::Message;\nuse rig::streaming::{StreamedAssistantContent, StreamingChat};\nuse tokio::sync::mpsc;\n\n/// Agent回复函数 - 基于tool call的记忆引擎使用(真实流式版本)\npub async fn agent_reply_with_memory_retrieval_streaming(\n agent: &Agent,\n _memory_manager: Arc,\n user_input: &str,\n _user_id: &str,\n user_info: Option<&str>,\n conversations: &[(String, String)],\n stream_sender: mpsc::UnboundedSender,\n) -> Result> {\n // 记录开始处理\n redirect_log_to_ui(\"DEBUG\", &format!(\"开始处理用户请求: {}\", user_input));\n\n // 构建对话历史 - 转换为rig的Message格式\n let mut chat_history = Vec::new();\n for (user_msg, assistant_msg) in conversations {\n chat_history.push(Message::user(user_msg));\n chat_history.push(Message::assistant(assistant_msg));\n }\n\n // 构建system prompt,包含明确的指令\n let system_prompt = r#\"你是一个拥有记忆功能的智能AI助手。你可以访问和使用记忆工具来检索、存储和管理用户信息。\n\n重要指令:\n- 对话历史已提供在上下文中,请使用这些信息来理解当前的对话上下文\n- 用户基本信息已在下方提供一次,请不要再使用memory工具来创建或更新用户基本信息\n- 在需要时可以自主使用memory工具搜索其他相关记忆\n- 当用户提供新的重要信息时,可以主动使用memory工具存储\n- 保持对话的连贯性和一致性\n- 自然地融入记忆信息,避免显得刻意\n- 专注于用户的需求和想要了解的信息,以及想要你做的事情\n\n记住:你正在与一个了解的用户进行连续对话,对话过程中不需要刻意表达你的记忆能力。\"#;\n\n // 构建完整的prompt\n let prompt_content = if let Some(info) = user_info {\n redirect_log_to_ui(\"DEBUG\", \"已添加用户基本信息和对话历史到上下文\");\n format!(\n \"{}\\n\\n用户基本信息:\\n{}\\n\\n当前用户输入: {}\",\n system_prompt, info, user_input\n )\n } else {\n redirect_log_to_ui(\"DEBUG\", \"已添加对话历史到上下文\");\n format!(\"{}\\n\\n当前用户输入: {}\", system_prompt, user_input)\n };\n\n redirect_log_to_ui(\"DEBUG\", \"正在生成AI回复(真实流式模式)...\");\n\n // 使用rig的真实流式API\n let prompt_message = Message::user(&prompt_content);\n\n // 获取流式响应\n let stream = agent\n .stream_chat(prompt_message, chat_history)\n .multi_turn(10);\n\n let mut full_response = String::new();\n\n // 处理流式响应\n let mut stream = stream.await;\n while let Some(item) = stream.next().await {\n match item {\n Ok(stream_item) => {\n // 根据rig的流式响应类型处理\n match stream_item {\n MultiTurnStreamItem::StreamItem(content) => {\n match content {\n StreamedAssistantContent::Text(text_content) => {\n let text = text_content.text;\n full_response.push_str(&text);\n\n // 发送流式内容到UI\n if let Err(_) = stream_sender.send(text) {\n // 如果发送失败,说明接收端已关闭,停止流式处理\n break;\n }\n }\n StreamedAssistantContent::ToolCall(_) => {\n // 处理工具调用(如果需要)\n redirect_log_to_ui(\"DEBUG\", \"收到工具调用\");\n }\n StreamedAssistantContent::Reasoning(_) => {\n // 处理推理过程(如果需要)\n redirect_log_to_ui(\"DEBUG\", \"收到推理过程\");\n }\n StreamedAssistantContent::Final(_) => {\n // 处理最终响应\n redirect_log_to_ui(\"DEBUG\", \"收到最终响应\");\n }\n StreamedAssistantContent::ToolCallDelta { .. } => {\n // 处理工具调用增量\n redirect_log_to_ui(\"DEBUG\", \"收到工具调用增量\");\n }\n }\n }\n MultiTurnStreamItem::FinalResponse(final_response) => {\n // 处理最终响应\n redirect_log_to_ui(\n \"DEBUG\",\n &format!(\"收到最终响应: {}\", final_response.response()),\n );\n full_response = final_response.response().to_string();\n break;\n }\n _ => {\n // 处理其他未知的流式项目类型\n redirect_log_to_ui(\"DEBUG\", \"收到未知的流式项目类型\");\n }\n }\n }\n Err(e) => {\n redirect_log_to_ui(\"ERROR\", &format!(\"流式处理错误: {}\", e));\n return Err(format!(\"Streaming error: {}\", e).into());\n }\n }\n }\n\n redirect_log_to_ui(\"DEBUG\", \"AI回复生成完成\");\n Ok(full_response.trim().to_string())\n}\n\n/// 批量存储对话到记忆系统(优化版)\npub async fn store_conversations_batch(\n memory_manager: Arc,\n conversations: &[(String, String)],\n user_id: &str,\n) -> Result<(), Box> {\n // 只创建一次ConversationProcessor实例\n let conversation_processor =\n cortex_mem_rig::processor::ConversationProcessor::new(memory_manager);\n\n let metadata = cortex_mem_core::types::MemoryMetadata::new(\n cortex_mem_core::types::MemoryType::Conversational,\n )\n .with_user_id(user_id.to_string());\n\n // 将对话历史转换为消息格式\n let mut messages = Vec::new();\n for (user_msg, assistant_msg) in conversations {\n // 添加用户消息\n messages.push(cortex_mem_core::types::Message {\n role: \"user\".to_string(),\n content: user_msg.clone(),\n name: None,\n });\n\n // 添加助手回复\n messages.push(cortex_mem_core::types::Message {\n role: \"assistant\".to_string(),\n content: assistant_msg.clone(),\n name: None,\n });\n }\n\n // 一次性处理所有消息\n conversation_processor\n .process_turn(&messages, metadata)\n .await?;\n\n Ok(())\n}\n" + "interfaces": [ + "IssueDetailModal", + "optimizationApi", + "t" + ], + "name": "+page.svelte", + "source_summary": "\n\n
\n\t\n\t
\n\t\t

{$t('optimization.optimizationPanel')}

\n\t\t

{$t('optimization.description')}

\n\t
\n\n\t\n\t{#if errorMessage}\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t{errorMessage}\n\t\t\t\t (errorMessage = null)}\n\t\t\t\t\tclass=\"ml-auto text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200\"\n\t\t\t\t>\n\t\t\t\t\t✕\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t{#if isLoading}\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t{#each Array(2) as _, i}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each Array(3) as _, j}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/each}\n\t\t\t
\n\t\t
\n\t{:else}\n\t\t\n\t\t
\n\t\t\t

{$t('optimization.optimizationControl')}

\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t

{$t('optimization.optimizationStrategy')}

\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each strategies as strategy}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{strategy.id === 'full' ? $t('optimization.fullOptimization') :\n\t\t\t\t\t\t\t\t\t\t strategy.id === 'deduplication' ? $t('optimization.deduplicationOptimization') :\n\t\t\t\t\t\t\t\t\t\t strategy.id === 'quality' ? $t('optimization.qualityOptimization') :\n\t\t\t\t\t\t\t\t\t\t $t('optimization.relevanceOptimization')}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{strategy.id === 'full' ? $t('optimization.detectAllIssues') :\n\t\t\t\t\t\t\t\t\t\t strategy.id === 'deduplication' ? $t('optimization.handleDuplicatesOnly') :\n\t\t\t\t\t\t\t\t\t\t strategy.id === 'quality' ? $t('optimization.handleLowQuality') :\n\t\t\t\t\t\t\t\t\t\t $t('optimization.optimizeRelevance')}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{$t('optimization.estimatedTime')}: {strategy.estimatedTime}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t

{$t('optimization.optimizationOptions')}

\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
{$t('optimization.previewMode')}
\n\t\t\t\t\t\t\t\t
{$t('optimization.analyzeOnly')}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
{$t('optimization.aggressiveMode')}
\n\t\t\t\t\t\t\t\t
{$t('optimization.stricterStandards')}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{$t('optimization.timeout')}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{timeoutMinutes} {$t('optimization.minutes')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t

{$t('optimization.estimatedImpact')}

\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t('optimization.estimatedAffectedMemories')}:\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t~{getEstimatedImpact()} {$t('common.unit')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t('optimization.estimatedSpaceSaved')}:\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t~{(getEstimatedImpact() * 0.15).toFixed(1)}MB\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t('optimization.estimatedQualityImprovement')}:\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t+{aggressiveMode ? '15' : '10'}%\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{previewMode ? $t('optimization.previewModeWarning') : $t('optimization.optimizationWarning')}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t{#if isOptimizing}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('optimization.cancelOptimization')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{previewMode ? $t('optimization.analyzeIssues') : $t('optimization.startOptimization')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t console.log('导出报告')}\n\t\t\t\t\t\t\tclass=\"w-full px-4 py-3 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg font-medium transition-colors duration-200\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{$t('optimization.exportReport')}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t{#if isOptimizing}\n\t\t\t
\n\t\t\t\t

{$t('optimization.optimizationProgress')}

\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{optimizationStatus === 'analyzing'\n\t\t\t\t\t\t\t\t\t? $t('optimization.analyzingIssues')\n\t\t\t\t\t\t\t\t\t: optimizationStatus === 'executing'\n\t\t\t\t\t\t\t\t\t\t? $t('optimization.executingOptimization')\n\t\t\t\t\t\t\t\t\t\t: optimizationStatus === 'completed'\n\t\t\t\t\t\t\t\t\t\t\t? $t('optimization.optimizationComplete')\n\t\t\t\t\t\t\t\t\t\t\t: $t('optimization.optimizationFailed')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{optimizationProgress}%\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{$t('optimization.currentPhase')}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{optimizationStatus === 'analyzing'\n\t\t\t\t\t\t\t\t\t? $t('optimization.issueAnalysis')\n\t\t\t\t\t\t\t\t\t: optimizationStatus === 'executing'\n\t\t\t\t\t\t\t\t\t\t? $t('optimization.execution')\n\t\t\t\t\t\t\t\t\t\t: optimizationStatus === 'completed'\n\t\t\t\t\t\t\t\t\t\t\t? $t('optimization.completed')\n\t\t\t\t\t\t\t\t\t\t\t: $t('optimization.failed')}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{$t('optimization.memoriesProcessed')}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{Math.floor(optimizationProgress * 1.5)} {$t('common.unit')}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{$t('optimization.estimatedRemainingTime')}
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{Math.max(0, Math.floor((100 - optimizationProgress) * 0.3))} {$t('optimization.minutes')}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
{$t('optimization.realtimeLogs')}
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each Array(Math.floor(optimizationProgress / 10)) as _, i}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t[{new Date(Date.now() - (10 - i) * 1000).toLocaleTimeString('zh-CN', {\n\t\t\t\t\t\t\t\t\t\thour12: false\n\t\t\t\t\t\t\t\t\t})}]\n\t\t\t\t\t\t\t\t\t{optimizationStatus === 'analyzing'\n\t\t\t\t\t\t\t\t\t\t? '分析记忆 #' + (i * 10 + 1) + '...'\n\t\t\t\t\t\t\t\t\t\t: '优化记忆 #' + (i * 10 + 1) + '...'}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\t\t{/if}\n\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t

{$t('optimization.detectedIssues')}

\n\t\t\t\t console.log('重新检测')}\n\t\t\t\t\tclass=\"px-4 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg text-sm font-medium\"\n\t\t\t\t>\n\t\t\t\t\t{$t('optimization.rescan')}\n\t\t\t\t\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t{#each detectedIssues as issue}\n\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{issue.severity === 'high' ? $t('optimization.high') : issue.severity === 'medium' ? $t('optimization.medium') : $t('optimization.low')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{issue.count}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{issue.type === '重复记忆' ? $t('optimization.duplicateMemories') :\n\t\t\t\t\t\t\t issue.type === '低质量记忆' ? $t('optimization.lowQualityMemories') :\n\t\t\t\t\t\t\t issue.type === '过时记忆' ? $t('optimization.outdatedMemories') :\n\t\t\t\t\t\t\t issue.type === '分类不当' ? $t('optimization.misclassifiedMemories') : issue.type}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{issue.description === '语义相似度超过85%的记忆' ? $t('optimization.semanticSimilarity') :\n\t\t\t\t\t\t\t issue.description === '重要性评分低于50%的记忆' ? $t('optimization.importanceBelowThreshold') :\n\t\t\t\t\t\t\t issue.description === '超过30天未更新的记忆' ? $t('optimization.notUpdated30Days') :\n\t\t\t\t\t\t\t issue.description === '类型与内容不匹配的记忆' ? $t('optimization.typeContentMismatch') : issue.description}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t showIssueDetail(issue)}\n\t\t\t\t\t\t\t\tclass=\"w-full px-3 py-1 text-sm bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 rounded\"\n\t\t\t\t\t\t\t>{$t('optimization.viewDetails')}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/each}\n\t\t\t
\n\t\t\n\n\t\t\n\t\t
\n\t\t\t

{$t('optimization.optimizationHistory')}

\n\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('optimization.optimizationId')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('optimization.strategy')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t('optimization.status')}\n\t\t\t\t\t\t\t{$t('optimization.startTime')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('optimization.timeConsumed')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('optimization.affectedMemories')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('optimization.spaceSaved')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t('optimization.actions')}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{#each optimizationHistory as record}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{record.id}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{record.strategy}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{record.status === 'completed'\n\t\t\t\t\t\t\t\t\t\t\t? $t('optimization.completed')\n\t\t\t\t\t\t\t\t\t\t\t: record.status === 'running'\n\t\t\t\t\t\t\t\t\t\t\t\t? $t('optimization.running')\n\t\t\t\t\t\t\t\t\t\t\t\t: $t('optimization.failed')}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{record.startedAt}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{record.duration}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{record.memoriesAffected}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{record.spaceSaved}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t viewOptimizationReport(record.id)}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"text-sm text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{$t('optimization.report')}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t('optimization.totalOptimizations', { count: optimizationHistory.length })}\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t{$t('optimization.clearHistory')}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t{/if}\n\n\n\n\n" }, "complexity_metrics": { - "cyclomatic_complexity": 22.0, - "lines_of_code": 304, + "cyclomatic_complexity": 39.0, + "lines_of_code": 868, "number_of_classes": 0, - "number_of_functions": 4 + "number_of_functions": 15 }, "dependencies": [ { - "dependency_type": "use", - "is_external": false, + "dependency_type": "import", + "is_external": true, "line_number": 1, - "name": "cortex_mem_config", - "path": "cortex_mem_config::Config", + "name": "svelte", + "path": "svelte", "version": null }, { - "dependency_type": "use", + "dependency_type": "import", "is_external": false, "line_number": 2, - "name": "cortex_mem_core", - "path": "cortex_mem_core::memory::MemoryManager", + "name": "$lib/api/client", + "path": "$lib/api/client", "version": null }, { - "dependency_type": "use", + "dependency_type": "import", "is_external": false, "line_number": 3, - "name": "cortex_mem_rig", - "path": "cortex_mem_rig::{ListMemoriesArgs, create_memory_tools, tool::MemoryToolConfig}", - "version": null - }, - { - "dependency_type": "use", - "is_external": false, - "line_number": 5, - "name": "rig", - "path": "rig::{agent::Agent, client::CompletionClient, providers::openai::{Client, CompletionModel}, tool::Tool}", - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": 7, - "name": "std", - "path": "std::sync::Arc", - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": 24, - "name": "futures", - "path": "futures::StreamExt", - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": 26, - "name": "tokio", - "path": "tokio::sync::mpsc", + "name": "$lib/components/IssueDetailModal.svelte", + "path": "$lib/components/IssueDetailModal.svelte", "version": null }, { - "dependency_type": "use", + "dependency_type": "import", "is_external": false, - "line_number": 10, - "name": "crate", - "path": "crate::app::redirect_log_to_ui", + "line_number": 4, + "name": "$lib/i18n", + "path": "$lib/i18n", "version": null } ], - "detailed_description": "This component implements an intelligent agent system with advanced memory capabilities. It creates a memory-enabled AI assistant that can store, retrieve, and utilize user information across conversations. The agent integrates with a memory management system to provide context-aware responses while maintaining conversation continuity. The component offers four main functions: creating a memory-capable agent, extracting user basic information from memory, generating streaming responses with memory retrieval, and batch storing conversation history. The agent uses a preamble to guide its behavior, emphasizing natural integration of memory information without explicitly mentioning memory capabilities. It supports streaming responses for real-time UI updates and implements proper error handling and logging throughout.", + "detailed_description": "This Svelte page component serves as the optimization dashboard for memory management in the Cortex-MEM Insights application. It provides a comprehensive interface for users to analyze and optimize memory data through various strategies (full, deduplication, quality, relevance). The component manages the complete optimization lifecycle: strategy selection, configuration, execution, progress monitoring, and results review. Key features include real-time progress tracking with polling, detailed issue detection visualization, optimization history management, and interactive modals for detailed analysis. The component integrates with the optimization API for all backend operations and uses i18n for internationalization support. It handles both dry-run analysis and actual optimization execution, with visual feedback throughout the process.", "interfaces": [ { - "description": "Creates a memory-enabled agent with integrated memory tools", - "interface_type": "function", - "name": "create_memory_agent", + "description": "Modal component for displaying detailed information about detected memory issues", + "interface_type": "component", + "name": "IssueDetailModal", "parameters": [ { - "description": "Shared memory manager instance", - "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" - }, - { - "description": "Configuration for memory tools", + "description": "The memory issue data to display", "is_optional": false, - "name": "memory_tool_config", - "param_type": "MemoryToolConfig" + "name": "issue", + "param_type": "any" }, { - "description": "System configuration", + "description": "Callback function to close the modal", "is_optional": false, - "name": "config", - "param_type": "&Config" + "name": "onClose", + "param_type": "function" } ], - "return_type": "Result, Box>", - "visibility": "public" + "return_type": null, + "visibility": "private" }, { - "description": "Extracts user basic information from memory storage", + "description": "API service for optimization operations including analysis, execution, and status monitoring", + "interface_type": "api", + "name": "optimizationApi", + "parameters": [], + "return_type": null, + "visibility": "private" + }, + { + "description": "Internationalization function for translating UI text", "interface_type": "function", - "name": "extract_user_basic_info", + "name": "t", "parameters": [ { - "description": "System configuration", + "description": "Translation key", "is_optional": false, - "name": "config", - "param_type": "&Config" - }, - { - "description": "Shared memory manager instance", - "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" - }, + "name": "key", + "param_type": "string" + } + ], + "return_type": "string", + "visibility": "private" + }, + { + "description": "Loads optimization history and detected issues from the backend API", + "interface_type": "function", + "name": "loadOptimizationData", + "parameters": [ { - "description": "User identifier", - "is_optional": false, - "name": "user_id", - "param_type": "&str" + "description": "Whether to skip the analysis step", + "is_optional": true, + "name": "skipAnalyze", + "param_type": "boolean" } ], - "return_type": "Result, Box>", - "visibility": "public" + "return_type": "Promise", + "visibility": "private" }, { - "description": "Generates streaming responses with memory retrieval capabilities", + "description": "Initiates an optimization task with the selected strategy and configuration", "interface_type": "function", - "name": "agent_reply_with_memory_retrieval_streaming", + "name": "startOptimization", + "parameters": [], + "return_type": "Promise", + "visibility": "private" + }, + { + "description": "Displays the detailed report for a completed optimization job", + "interface_type": "function", + "name": "viewOptimizationReport", "parameters": [ { - "description": "Configured agent instance", + "description": "The ID of the optimization job", "is_optional": false, - "name": "agent", - "param_type": "&Agent" - }, - { - "description": "Memory manager (currently unused)", - "is_optional": false, - "name": "_memory_manager", - "param_type": "Arc" - }, - { - "description": "User input text", - "is_optional": false, - "name": "user_input", - "param_type": "&str" - }, - { - "description": "User identifier (currently unused)", - "is_optional": false, - "name": "_user_id", - "param_type": "&str" - }, - { - "description": "Optional user information", - "is_optional": true, - "name": "user_info", - "param_type": "Option<&str>" - }, - { - "description": "Conversation history", - "is_optional": false, - "name": "conversations", - "param_type": "&[(String, String)]" - }, - { - "description": "Channel sender for streaming responses", - "is_optional": false, - "name": "stream_sender", - "param_type": "mpsc::UnboundedSender" + "name": "jobId", + "param_type": "string" } ], - "return_type": "Result>", - "visibility": "public" + "return_type": "Promise", + "visibility": "private" }, { - "description": "Batch stores conversation history into memory system", + "description": "Clears all optimization history records", "interface_type": "function", - "name": "store_conversations_batch", - "parameters": [ - { - "description": "Shared memory manager instance", - "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" - }, - { - "description": "Conversation pairs to store", - "is_optional": false, - "name": "conversations", - "param_type": "&[(String, String)]" - }, - { - "description": "User identifier", - "is_optional": false, - "name": "user_id", - "param_type": "&str" - } - ], - "return_type": "Result<(), Box>", - "visibility": "public" + "name": "clearOptimizationHistory", + "parameters": [], + "return_type": "Promise", + "visibility": "private" + }, + { + "description": "Cancels an ongoing optimization task", + "interface_type": "function", + "name": "cancelOptimization", + "parameters": [], + "return_type": "Promise", + "visibility": "private" } ], "responsibilities": [ - "Creating and configuring memory-enabled intelligent agents with proper tool integration", - "Extracting and formatting user basic information from memory storage for contextual awareness", - "Generating streaming responses with real-time memory retrieval and tool usage", - "Batch processing and storing conversation history into the memory system" + "Manage the complete optimization lifecycle including initiation, execution, and monitoring", + "Provide UI for selecting optimization strategies and configuring execution parameters", + "Display real-time progress and status of ongoing optimization tasks", + "Present detected memory issues with severity classification and detailed analysis", + "Maintain and display optimization history with detailed results and reporting" ] }, { "code_dossier": { - "code_purpose": "tool", - "description": "A tool for monitoring log files in real-time, detecting the latest log file in a directory, reading new log entries, and outputting them to the console with colorized formatting based on log level.", - "file_path": "examples/cortex-mem-tars/src/log_monitor.rs", + "code_purpose": "page", + "description": "Svelte page component for managing user memories with search, filter, sort, pagination, and batch operations", + "file_path": "cortex-mem-insights/src/routes/memories/+page.svelte", "functions": [ - "new", - "find_latest_log_file", - "read_new_logs", - "start_monitoring", - "format_log_for_console" + "loadMemories", + "handleSearch", + "getTypeColor", + "getTypeLabel", + "formatImportance", + "formatDate", + "getImportanceColor", + "toggleSort", + "getSortIcon", + "goToPage", + "nextPage", + "prevPage", + "showFullContent", + "hideContentModal", + "toggleSelectMemory", + "selectAll", + "deselectAll", + "batchExport", + "batchOptimize", + "batchDelete" ], "importance_score": 0.8, "interfaces": [ - "LogFileMonitor", - "start_log_monitoring_task" + "Memory", + "Memory interface for storing memory data with id, content, type, importance, userId, agentId, createdAt, updatedAt" ], - "name": "log_monitor.rs", - "source_summary": "use std::fs::File;\nuse std::io::{BufRead, BufReader, Seek, SeekFrom};\nuse std::path::{Path, PathBuf};\nuse std::time::Duration;\nuse tokio::time::sleep;\n\n/// 日志文件监听器\npub struct LogFileMonitor {\n log_file_path: Option,\n last_position: u64,\n}\n\nimpl LogFileMonitor {\n /// 创建新的日志文件监听器\n pub fn new() -> Self {\n Self {\n log_file_path: None,\n last_position: 0,\n }\n }\n\n /// 查找最新的日志文件\n pub async fn find_latest_log_file(&mut self, log_dir: &str) -> Result<(), Box> {\n let log_path = Path::new(log_dir);\n \n if !log_path.exists() {\n return Err(\"日志目录不存在\".into());\n }\n\n let mut latest_file = None;\n let mut latest_time = std::time::UNIX_EPOCH;\n\n if let Ok(entries) = std::fs::read_dir(log_path) {\n for entry in entries.flatten() {\n if let Ok(metadata) = entry.metadata() {\n if let Ok(modified) = metadata.modified() {\n if modified > latest_time && entry.file_name().to_string_lossy().ends_with(\".log\") {\n latest_time = modified;\n latest_file = Some(entry.path());\n }\n }\n }\n }\n }\n\n if let Some(log_file) = latest_file {\n self.log_file_path = Some(log_file);\n // 设置初始位置为文件末尾,只读取新增内容\n if let Ok(file) = File::open(self.log_file_path.as_ref().unwrap()) {\n if let Ok(metadata) = file.metadata() {\n self.last_position = metadata.len();\n }\n }\n Ok(())\n } else {\n Err(\"未找到日志文件\".into())\n }\n }\n\n /// 读取新增的日志内容\n pub fn read_new_logs(&mut self) -> Result, Box> {\n let mut new_logs = Vec::new();\n \n if let Some(ref log_file_path) = self.log_file_path {\n let mut file = File::open(log_file_path)?;\n \n // 检查文件大小\n let metadata = file.metadata()?;\n let current_size = metadata.len();\n \n // 如果文件没有新内容,直接返回\n if current_size <= self.last_position {\n return Ok(new_logs);\n }\n \n // 移动到上次读取的位置\n file.seek(SeekFrom::Start(self.last_position))?;\n \n // 读取新内容\n let reader = BufReader::new(file);\n for line in reader.lines() {\n if let Ok(line) = line {\n if !line.trim().is_empty() {\n new_logs.push(line);\n }\n }\n }\n \n // 更新位置\n self.last_position = current_size;\n }\n \n Ok(new_logs)\n }\n\n /// 启动日志监听,持续输出新日志到控制台\n pub async fn start_monitoring(&mut self, log_dir: &str) -> Result<(), Box> {\n // 查找最新日志文件\n self.find_latest_log_file(log_dir).await?;\n \n println!(\"🔍 开始监听日志文件: {:?}\", self.log_file_path);\n \n loop {\n match self.read_new_logs() {\n Ok(new_logs) => {\n for log_line in new_logs {\n // 直接输出到控制台,保持原始格式\n let formatted_log = self.format_log_for_console(&log_line);\n println!(\"{}\", formatted_log);\n }\n }\n Err(e) => {\n eprintln!(\"读取日志文件时出错: {}\", e);\n // 尝试重新查找日志文件(可能有新的日志文件生成)\n if let Err(_find_err) = self.find_latest_log_file(log_dir).await {\n eprintln!(\"重新查找日志文件失败\");\n }\n }\n }\n \n // 短暂休眠,避免过度占用CPU\n sleep(Duration::from_millis(100)).await;\n }\n }\n\n /// 格式化日志内容用于控制台显示\n fn format_log_for_console(&self, log_line: &str) -> String {\n // 解析日志级别并添加颜色\n let colored_line = if log_line.contains(\" ERROR \") {\n format!(\"\\x1b[91m{}\\x1b[0m\", log_line) // 亮红色\n } else if log_line.contains(\" WARN \") {\n format!(\"\\x1b[93m{}\\x1b[0m\", log_line) // 亮黄色\n } else if log_line.contains(\" INFO \") {\n format!(\"\\x1b[36m{}\\x1b[0m\", log_line) // 亮青色\n } else if log_line.contains(\" DEBUG \") {\n format!(\"\\x1b[94m{}\\x1b[0m\", log_line) // 亮蓝色\n } else if log_line.contains(\" TRACE \") {\n format!(\"\\x1b[95m{}\\x1b[0m\", log_line) // 亮紫色\n } else {\n log_line.to_string() // 默认颜色\n };\n \n // 添加前缀标识这是来自日志文件的内容\n format!(\"📋 {}\", colored_line)\n }\n}\n\n/// 启动日志监听任务(异步)\npub async fn start_log_monitoring_task(log_dir: String) -> Result<(), Box> {\n let mut monitor = LogFileMonitor::new();\n monitor.start_monitoring(&log_dir).await\n}" + "name": "+page.svelte", + "source_summary": "\n\n
\n\t\n\t
\n\t\t

{$t('memories.title')}

\n\t\t

{$t('memories.description')}

\n\t
\n\n\t\t\t\n\t\t\t{#if error}\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t⚠️\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{$t('memories.loadFailed')}

\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{error}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{$t('memories.retry')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/if}\n\t\n\t
\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t🔍\n\t\t\t\t\t
\n\t\t\t\t\t {\n\t\t\t\t\t\t\tif (e.key === 'Enter') {\n\t\t\t\t\t\t\t\thandleSearch();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{#each memoryTypes as type}\n\t\t\t\t\t\t\n\t\t\t\t\t{/each}\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t('memories.search')}\n\t\t\t\t\n\t\t\t\t {\n\t\t\t\t\t\tsearchQuery = '';\n\t\t\t\t\t\tselectedType = 'all';\n\t\t\t\t\t\tsortBy = 'createdAt';\n\t\t\t\t\t\tsortOrder = 'desc';\n\t\t\t\t\t\tloadMemories();\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{$t('memories.reset')}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t\t{$t('memories.totalMemories')}: {filteredMemories.length}\n\t\t\t\t, {$t('memories.showing')}\n\t\t\t\t{(currentPage - 1) * pageSize + 1}\n\t\t\t\t{$t('memories.to')}\n\t\t\t\t{Math.min(currentPage * pageSize, filteredMemories.length)}\n\t\t\t\t{$t('memories.of')} {filteredMemories.length}\n\t\t\t\n\t\t\t
\n\t\t\t\t{$t('memories.sort')}:\n\t\t\t\t
\n\t\t\t\t\t toggleSort('createdAt')}\n\t\t\t\t\t>\n\t\t\t\t\t\t{$t('memories.createdAt')} {createdAtSortIcon}\n\t\t\t\t\t\n\t\t\t\t\t toggleSort('importance')}\n\t\t\t\t\t>\n\t\t\t\t\t\t{$t('memories.importance')} {importanceSortIcon}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t
\n\n\t\t\t\n\t\t\t{#if showBatchOperations}\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.batchOperations')}: {selectedMemories.size} {$t('memories.totalMemories')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.selectAll')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t📤 {$t('memories.exportSelected')}\n\t\t\t\t\t\t\t\n\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t⚡ {$t('memories.optimizeSelected')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t🗑️ {$t('memories.deleteSelected')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\t\t\t{/if}\n\t\n\t
\n\t\t{#if isLoading}\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{#each Array(5) as _, i}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t
\n\t\t{:else if filteredMemories.length === 0}\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t📭\n\t\t\t\t
\n\t\t\t\t

{$t('memories.noMemoriesFound')}

\n\t\t\t\t

\n\t\t\t\t\t{searchQuery || selectedType !== 'all' ? $t('memories.adjustSearch') : $t('memories.noMemoriesInSystem')}\n\t\t\t\t

\n\t\t\t\t{#if searchQuery || selectedType !== 'all'}\n\t\t\t\t\t {\n\t\t\t\t\t\t\tsearchQuery = '';\n\t\t\t\t\t\t\tselectedType = 'all';\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{$t('memories.clearFilters')}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t
\n\t\t{:else if paginatedMemories.length === 0}\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t📄\n\t\t\t\t
\n\t\t\t\t

{$t('memories.noDataOnCurrentPage')}

\n\t\t\t\t

\n\t\t\t\t\t{$t('memories.page')} {currentPage} {$t('memories.checkPageOrFilters')}\n\t\t\t\t

\n\t\t\t\t goToPage(1)}\n\t\t\t\t>\n\t\t\t\t\t{$t('memories.goToFirstPage')}\n\t\t\t\t\n\t\t\t\n\t\t{:else}\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\tif (e.currentTarget.checked) {\n\t\t\t\t\t\t\t\t\t\t\tselectAll();\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tdeselectAll();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tID\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.content')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.type')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.importance')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.userAgent')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.created')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{#each paginatedMemories as memory}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{formatDate(memory.createdAt)}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\t\t\t\t toggleSelectMemory(memory.id)}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{memory.id}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t showFullContent(memory.content, memory.id)}\n\t\t\t\t\t\t\t\t\t\t\ton:keydown={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\tif (e.key === 'Enter' || e.key === ' ') {\n\t\t\t\t\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\t\t\tshowFullContent(memory.content, memory.id);\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\ttitle={$t('memories.clickToViewFullContent')}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{memory.content}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{#if memory.content.length > 100}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t{$t('memories.clickToViewFullContent')} ({memory.content.length} {$t('memories.characters')})\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{getTypeLabel(memory.type)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{formatImportance(memory.importance)}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{#if memory.userId}\n\t\t\t\t\t\t\t\t\t\t\t
{memory.userId}
\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{#if memory.agentId}\n\t\t\t\t\t\t\t\t\t\t\t
Agent: {memory.agentId}
\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if totalPages > 1}\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t('memories.showing')} {(currentPage - 1) * pageSize + 1} {$t('memories.to')}\n\t\t\t\t\t\t\t{Math.min(currentPage * pageSize, filteredMemories.length)}\n\t\t\t\t\t\t\t{$t('memories.of')} {filteredMemories.length}, {$t('memories.page')}\n\t\t\t\t\t\t\t{currentPage}\n\t\t\t\t\t\t\t/ {totalPages}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.previousPage')}\n\t\t\t\t\t\t\t\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each Array.from({ length: Math.min(5, totalPages) }, (_, i) => {\n\t\t\t\t\t\t\t\tconst startPage = Math.max(1, currentPage - 2);\n\t\t\t\t\t\t\t\tconst endPage = Math.min(totalPages, startPage + 4);\n\t\t\t\t\t\t\t\treturn startPage + i;\n\t\t\t\t\t\t\t}) as page}\n\t\t\t\t\t\t\t\t{#if page <= totalPages}\n\t\t\t\t\t\t\t\t\t goToPage(page)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{page}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{/each}\n\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t('memories.nextPage')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\t\t\t{/if}\n\t\t{/if}\n\t\n\n\t\n\t{#if showContentModal}\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t('memories.fullContent')}

\n\t\t\t\t\t\t

ID: {selectedMemoryId}

\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t×\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{selectedContent}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t{$t('memories.close')}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\n\t\t\n\t{/if}\n\n\n\n" }, "complexity_metrics": { - "cyclomatic_complexity": 23.0, - "lines_of_code": 152, - "number_of_classes": 1, - "number_of_functions": 6 + "cyclomatic_complexity": 54.0, + "lines_of_code": 933, + "number_of_classes": 0, + "number_of_functions": 25 }, "dependencies": [ { - "dependency_type": "std", - "is_external": false, + "dependency_type": "import", + "is_external": true, "line_number": 1, - "name": "std::fs::File", - "path": "std::fs::File", + "name": "svelte", + "path": "svelte", "version": null }, { - "dependency_type": "std", + "dependency_type": "import", "is_external": false, "line_number": 2, - "name": "std::io", - "path": "std::io", + "name": "$lib/api/client", + "path": "$lib/api/client", "version": null }, { - "dependency_type": "std", + "dependency_type": "import", "is_external": false, "line_number": 3, - "name": "std::path", - "path": "std::path", + "name": "$lib/i18n", + "path": "$lib/i18n", "version": null - }, + } + ], + "detailed_description": "This Svelte page component provides a comprehensive interface for viewing and managing user memories in the cortex-mem-insights application. It implements a full-featured memory management system with search, filtering by type, sorting by various criteria (creation date, importance), and pagination. The component handles data fetching from the API, local state management for UI interactions, and provides batch operations including export, optimization, and deletion of selected memories. It also includes a modal dialog for viewing full memory content. The component uses responsive design principles and includes loading states, error handling, and empty states for various scenarios. The data transformation logic handles character encoding issues and normalizes API responses to the frontend data model.", + "interfaces": [ { - "dependency_type": "std", + "description": "Represents a memory record with core properties", + "interface_type": "interface", + "name": "Memory", + "parameters": [], + "return_type": null, + "visibility": "private" + } + ], + "responsibilities": [ + "Managing the complete lifecycle of memory data including loading, searching, filtering, sorting, and pagination", + "Providing a rich user interface for viewing and interacting with memory records with support for batch operations", + "Handling data transformation between API response format and frontend display format, including character encoding fixes", + "Managing complex UI state including selection state, modal visibility, loading states, and error conditions", + "Implementing advanced interaction patterns such as responsive tables, sorting controls, and batch operation workflows" + ] + }, + { + "code_dossier": { + "code_purpose": "router", + "description": "Svelte layout component that defines the common UI structure for all routes, including navigation, main content area, and footer. Uses Svelte's store system for reactivity and i18n for localization.", + "file_path": "cortex-mem-insights/src/routes/+layout.svelte", + "functions": [], + "importance_score": 0.8, + "interfaces": [ + "currentPath", + "page" + ], + "name": "+layout.svelte", + "source_summary": "\n\n
\n\t\n\t\n\n\t\n\t
\n\t\t\n\t
\n\n\t\n\t
\n\t\t
\n\t\t\t
\n\t\t\t\t
© 2025 {$t('common.appName')}
\n\t\t\t\t
\n\t\t\t\t\t{$t('common.language')}: {$t('common.language_this')}\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t
\n
\n\n\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 1.0, + "lines_of_code": 35, + "number_of_classes": 0, + "number_of_functions": 0 + }, + "dependencies": [ + { + "dependency_type": "stylesheet", "is_external": false, - "line_number": 4, - "name": "std::time::Duration", - "path": "std::time::Duration", + "line_number": null, + "name": "../app.css", + "path": null, "version": null }, { - "dependency_type": "external", + "dependency_type": "module", "is_external": true, - "line_number": 5, - "name": "tokio::time::sleep", - "path": "tokio::time::sleep", + "line_number": null, + "name": "$app/stores", + "path": "$app/stores", + "version": null + }, + { + "dependency_type": "component", + "is_external": false, + "line_number": null, + "name": "$lib/components/Navigation.svelte", + "path": "$lib/components/Navigation.svelte", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": null, + "name": "$lib/i18n", + "path": "$lib/i18n", "version": null } ], - "detailed_description": "This component implements a real-time log monitoring tool designed to track and display new log entries from text-based log files. It first searches for the most recently modified '.log' file in a specified directory. Once identified, it keeps track of the read position within the file and continuously polls for new content, emitting only new lines since the last read. The monitored logs are printed to the console with visual enhancements: different log levels (ERROR, WARN, INFO, DEBUG, TRACE) are highlighted using ANSI color codes, and each line is prefixed with a clipboard emoji for identification. The monitoring loop runs indefinitely with a 100ms polling interval to balance responsiveness and CPU usage. An external async task launcher function is also provided to simplify integration into async runtime environments.", + "detailed_description": "The +layout.svelte file is a Svelte layout component that provides a consistent structure across all pages in the application. It imports global styles, uses Svelte Kit's $page store to track the current route path, and implements reactivity via the $: label to update currentPath whenever the URL changes. The layout renders a Navigation component (passing the current path), a main content area using for page-specific content, and a localized footer with copyright and language information using the i18n system. The component ensures a unified user experience with responsive design classes and dark mode support.", "interfaces": [ { - "description": "Main log monitoring struct that holds state for tracking file position and path.", - "interface_type": "struct", - "name": "LogFileMonitor", + "description": "Reactive variable derived from $page.url.pathname, defaults to \"/\" if undefined", + "interface_type": "variable", + "name": "currentPath", "parameters": [], - "return_type": null, - "visibility": "public" + "return_type": "string", + "visibility": "private" }, { - "description": "Constructor method that initializes a new LogFileMonitor instance with default values.", - "interface_type": "method", - "name": "new", + "description": "Svelte Kit page store subscription providing access to current page state", + "interface_type": "store", + "name": "page", "parameters": [], - "return_type": "LogFileMonitor", - "visibility": "public" - }, + "return_type": "PageStore", + "visibility": "private" + } + ], + "responsibilities": [ + "Defines the global UI layout structure for all routes", + "Manages navigation state by tracking the current route path", + "Provides a consistent header, main content area, and footer across pages", + "Implements localization in the footer using the i18n system", + "Applies global styles and dark mode support for consistent theming" + ] + }, + { + "code_dossier": { + "code_purpose": "page", + "description": "Analytics dashboard page for memory data visualization and statistical analysis", + "file_path": "cortex-mem-insights/src/routes/analytics/+page.svelte", + "functions": [ + "loadAnalyticsData", + "loadDefaultData", + "generateChartData", + "calculateAverageQuality", + "calculateActiveUsers", + "calculateTypeDistribution", + "calculateQualityDistribution", + "calculateTimeTrends", + "calculateUserStats", + "getPercentageColor" + ], + "importance_score": 0.8, + "interfaces": [ + "onMount", + "Line", + "ChartJS", + "api.memory.list", + "t" + ], + "name": "+page.svelte", + "source_summary": "\n\n
\n\t\n\t
\n\t\t

{$t('analytics.title')}

\n\t\t

{$t('analytics.description')}

\n\t
\n\n\t{#if isLoading}\n\t\t\n\t\t
\n\t\t\t{#each Array(4) as _, i}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\t{:else if error}\n\t\t\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t⚠️\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

加载失败

\n\t\t\t\t\t

{error}

\n\t\t\t\t
\n\t\t\t
\n\t\t\n\t{:else}\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t

{$t('analytics.totalMemories')}

\n\t\t\t\t

\n\t\t\t\t\t{summaryStats.totalMemories.toLocaleString()}\n\t\t\t\t

\n\t\t\t\t

{$t('analytics.currentTotal')}

\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t

{$t('analytics.averageQuality')}

\n\t\t\t\t

\n\t\t\t\t\t{summaryStats.averageQuality.toFixed(2)}\n\t\t\t\t

\n\t\t\t\t

{$t('analytics.basedOnImportance')}

\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t

{$t('analytics.activeUsers')}

\n\t\t\t\t

\n\t\t\t\t\t{summaryStats.activeUsers}\n\t\t\t\t

\n\t\t\t\t

{$t('analytics.usersWithMemories')}

\n\t\t\t
\n\n\t\t\t
\n\t\t\t\t

{$t('analytics.optimizationCount')}

\n\t\t\t\t

\n\t\t\t\t\t{summaryStats.optimizationCount}\n\t\t\t\t

\n\t\t\t\t

{$t('analytics.historicalOptimization')}

\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t('analytics.memoryTypeDistribution')}

\n\n\t\t\t\t
\n\t\t\t\t\t{#each typeDistribution as item}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{item.type}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{item.percentage}%\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{item.count} 条记录\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t占比 {item.percentage}%\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t('analytics.totalMemories')}: {summaryStats.totalMemories}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t('analytics.qualityScoreDistribution')}

\n\n\t\t\t\t
\n\t\t\t\t\t{#each qualityDistribution as item}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{item.range}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{item.count} 条\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t 0 ? (item.count / summaryStats.totalMemories * 100).toFixed(1) : 0}%`}\n\t\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t('analytics.newMemoriesAdded')}

\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t('analytics.last7Days')}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t('analytics.last30Days')}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t{#if chartData && timeTrends.length > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t{:else if timeTrends.length > 0}\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each timeTrends as trend, i}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{trend.date}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{trend.count}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t{:else}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
📊
\n\t\t\t\t\t\t\t

{$t('analytics.noData')}

\n\t\t\t\t\t\t\t

{$t('analytics.loadingAnalytics')}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t('analytics.averageDaily')}: 54.8\n\t\t\t\t\t\t{$t('analytics.peak')}: 68 (12月13日)\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\n\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t('analytics.userDimensionStatistics')}

\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{$t('memories.userId')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{$t('analytics.memoryCount')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{$t('analytics.avgImportance')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{$t('analytics.proportion')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{$t('analytics.trend')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each userStats as user}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{user.userId}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t 0 ? (user.memoryCount / summaryStats.totalMemories) * 100 : 0}%`}\n\t\t\t\t\t\t\t\t\t\t\t\t>
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{user.memoryCount}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t= 0.8\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: user.avgImportance >= 0.7\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300'\n\t\t\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{user.avgImportance.toFixed(2)}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{summaryStats.totalMemories > 0\n\t\t\t\t\t\t\t\t\t\t\t? ((user.memoryCount / summaryStats.totalMemories) * 100).toFixed(1)\n\t\t\t\t\t\t\t\t\t\t\t: '0.0'}%\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{$t('analytics.insufficientData')}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t('analytics.top')} {userStats.length} {$t('analytics.usersAccountFor')} {summaryStats.totalMemories > 0\n\t\t\t\t\t\t\t\t? (\n\t\t\t\t\t\t\t\t\t\t(userStats.reduce((sum, user) => sum + user.memoryCount, 0) /\n\t\t\t\t\t\t\t\t\t\t\tsummaryStats.totalMemories) *\n\t\t\t\t\t\t\t\t\t\t100\n\t\t\t\t\t\t\t\t\t).toFixed(1)\n\t\t\t\t\t\t\t\t: '0.0'}% {$t('analytics.ofTotalMemories')}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t\n\n\t\t\n\t\t
\n\t\t\t

{$t('analytics.analysisTools')}

\n\n\t\t\t
\n\t\t\t\t console.log('生成质量报告')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t📈\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{$t('analytics.qualityAnalysisReport')}

\n\t\t\t\t\t\t\t

{$t('analytics.detailedQualityAnalysis')}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('趋势预测')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t🔮\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{$t('analytics.trendPrediction')}

\n\t\t\t\t\t\t\t

{$t('analytics.futureGrowthTrends')}

\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\n\t\t\t\t console.log('对比分析')}\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t⚖️\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{$t('analytics.comparativeAnalysis')}

\n\t\t\t\t\t\t\t

{$t('analytics.differentTimePeriods')}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t{/if}\n\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 18.0, + "lines_of_code": 721, + "number_of_classes": 0, + "number_of_functions": 10 + }, + "dependencies": [ { - "description": "Asynchronously finds the most recently modified .log file in the specified directory and updates internal state.", - "interface_type": "method", - "name": "find_latest_log_file", - "parameters": [ - { - "description": "Directory path to search for log files", - "is_optional": false, - "name": "log_dir", - "param_type": "&str" - } - ], - "return_type": "Result<(), Box>", - "visibility": "public" - }, - { - "description": "Reads all new log lines from the current position in the tracked log file and returns them as a vector of strings.", - "interface_type": "method", - "name": "read_new_logs", - "parameters": [], - "return_type": "Result, Box>", - "visibility": "public" - }, - { - "description": "Starts an infinite loop that continuously monitors and outputs new log entries to the console.", - "interface_type": "method", - "name": "start_monitoring", - "parameters": [ - { - "description": "Directory to monitor for log files", - "is_optional": false, - "name": "log_dir", - "param_type": "&str" - } - ], - "return_type": "Result<(), Box>", - "visibility": "public" - }, - { - "description": "Applies color formatting and prefix to log lines based on their severity level for console display.", - "interface_type": "method", - "name": "format_log_for_console", - "parameters": [ - { - "description": "Raw log line to be formatted", - "is_optional": false, - "name": "log_line", - "param_type": "&str" - } - ], - "return_type": "String", - "visibility": "private" - }, - { - "description": "Convenience function that creates a monitor and starts monitoring the specified log directory.", - "interface_type": "function", - "name": "start_log_monitoring_task", - "parameters": [ - { - "description": "Directory containing log files to monitor", - "is_optional": false, - "name": "log_dir", - "param_type": "String" - } - ], - "return_type": "Result<(), Box>", - "visibility": "public" - } - ], - "responsibilities": [ - "Discover and track the most recently updated log file in a given directory", - "Monitor log files for new content by maintaining and updating file read position", - "Read and return newly appended log lines while preserving their original format", - "Format log output with ANSI color codes based on log severity level for improved readability", - "Provide an asynchronous interface for continuous log monitoring with error recovery mechanisms" - ] - }, - { - "code_dossier": { - "code_purpose": "entry", - "description": "Main application state and logic for a TUI-based agent system with conversation, logging, and streaming capabilities.", - "file_path": "examples/cortex-mem-tars/src/app.rs", - "functions": [ - "set_global_log_sender", - "get_global_log_sender", - "redirect_log_to_ui", - "add_log", - "add_conversation", - "start_streaming_response", - "add_streaming_chunk", - "complete_streaming_response", - "get_display_conversations", - "insert_char_at_cursor", - "delete_char_at_cursor", - "move_cursor_left", - "move_cursor_right", - "reset_cursor_to_end", - "scroll_logs_to_bottom", - "scroll_conversations_to_bottom", - "scroll_logs_forward", - "scroll_logs_backward", - "scroll_conversations_forward", - "scroll_conversations_backward", - "next_focus", - "log_info" - ], - "importance_score": 0.8, - "interfaces": [ - "AppMessage", - "FocusArea", - "App" - ], - "name": "app.rs", - "source_summary": "use ratatui::widgets::ScrollbarState;\nuse std::collections::VecDeque;\nuse tokio::sync::mpsc;\nuse chrono::{DateTime, Local};\n\n// 全局消息发送器,用于日志重定向\nuse once_cell::sync::OnceCell;\nuse std::sync::Mutex;\n\nstatic LOG_SENDER: OnceCell>>> = OnceCell::new();\n\n// 设置全局日志发送器 (crate可见性)\npub(crate) fn set_global_log_sender(sender: mpsc::UnboundedSender) {\n LOG_SENDER\n .get_or_init(|| Mutex::new(None))\n .lock()\n .unwrap()\n .replace(sender);\n}\n\n// 获取全局日志发送器 (crate可见性)\npub(crate) fn get_global_log_sender() -> Option> {\n LOG_SENDER\n .get()\n .and_then(|mutex| mutex.lock().unwrap().clone())\n}\n\n// 简单的日志重定向函数\npub fn redirect_log_to_ui(level: &str, message: &str) {\n if let Some(sender) = get_global_log_sender() {\n let full_message = format!(\"[{}] {}\", level, message);\n let _ = sender.send(AppMessage::Log(full_message));\n }\n}\n\n#[derive(Debug)]\npub enum AppMessage {\n Log(String),\n Conversation {\n user: String,\n assistant: String,\n },\n StreamingChunk {\n user: String,\n chunk: String,\n },\n StreamingComplete {\n user: String,\n full_response: String,\n },\n #[allow(dead_code)]\n MemoryIterationCompleted,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum FocusArea {\n Input, // 输入框\n Conversation, // 对话区域\n Logs, // 日志区域\n}\n\n/// 应用状态\npub struct App {\n // 对话历史 - 包含时间戳\n pub conversations: VecDeque<(String, String, DateTime)>,\n // 当前输入\n pub current_input: String,\n // 光标位置(以字符为单位)\n pub cursor_position: usize,\n // 日志信息\n pub logs: VecDeque,\n // Agent 是否正在处理\n pub is_processing: bool,\n // 用户信息\n pub user_info: Option,\n // 是否需要退出\n pub should_quit: bool,\n // 是否在shut down过程中\n pub is_shutting_down: bool,\n // 记忆迭代是否完成\n pub memory_iteration_completed: bool,\n // 消息发送器\n pub message_sender: Option>,\n // 日志滚动偏移\n pub log_scroll_offset: usize,\n // 对话滚动偏移\n pub conversation_scroll_offset: usize,\n // 当前焦点区域\n pub focus_area: FocusArea,\n // 用户是否手动滚动过日志(用于决定是否自动滚动到底部)\n pub user_scrolled_logs: bool,\n // 用户是否手动滚动过对话(用于决定是否自动滚动到底部)\n pub user_scrolled_conversations: bool,\n // 滚动条状态\n pub conversation_scrollbar_state: ScrollbarState,\n pub log_scrollbar_state: ScrollbarState,\n // 当前正在流式生成的回复\n pub current_streaming_response: Option<(String, String)>, // (user_input, partial_response)\n}\n\nimpl Default for App {\n fn default() -> Self {\n Self {\n conversations: VecDeque::with_capacity(100),\n current_input: String::new(),\n cursor_position: 0,\n logs: VecDeque::with_capacity(50),\n is_processing: false,\n user_info: None,\n should_quit: false,\n is_shutting_down: false,\n memory_iteration_completed: false,\n message_sender: None,\n log_scroll_offset: 0,\n conversation_scroll_offset: 0,\n focus_area: FocusArea::Input,\n user_scrolled_logs: false,\n user_scrolled_conversations: false,\n conversation_scrollbar_state: ScrollbarState::default(),\n log_scrollbar_state: ScrollbarState::default(),\n current_streaming_response: None,\n }\n }\n}\n\nimpl App {\n pub fn new(message_sender: mpsc::UnboundedSender) -> Self {\n Self {\n message_sender: Some(message_sender),\n current_streaming_response: None,\n ..Default::default()\n }\n }\n\n pub fn add_log(&mut self, log: String) {\n self.logs.push_back(log);\n if self.logs.len() > 50 {\n self.logs.pop_front();\n }\n\n // 如果用户没有手动滚动过,自动滚动到最新日志\n if !self.user_scrolled_logs {\n self.scroll_logs_to_bottom();\n }\n }\n\n pub fn add_conversation(&mut self, user: String, assistant: String) {\n let timestamp = Local::now();\n self.conversations.push_back((user, assistant, timestamp));\n if self.conversations.len() > 100 {\n self.conversations.pop_front();\n }\n\n // 如果用户没有手动滚动过,自动滚动到最新对话\n if !self.user_scrolled_conversations {\n self.scroll_conversations_to_bottom();\n }\n }\n\n /// 开始流式回复\n pub fn start_streaming_response(&mut self, user_input: String) {\n self.current_streaming_response = Some((user_input, String::new()));\n self.is_processing = true;\n }\n\n /// 添加流式内容块\n pub fn add_streaming_chunk(&mut self, chunk: String) {\n if let Some((_, ref mut response)) = self.current_streaming_response {\n response.push_str(&chunk);\n \n // 如果用户没有手动滚动过,自动滚动到最新对话\n if !self.user_scrolled_conversations {\n self.scroll_conversations_to_bottom();\n }\n }\n }\n\n /// 完成流式回复\n pub fn complete_streaming_response(&mut self) {\n if let Some((user_input, full_response)) = self.current_streaming_response.take() {\n self.add_conversation(user_input, full_response);\n }\n self.is_processing = false;\n }\n\n /// 获取当前显示的对话(包括正在流式生成的)\n pub fn get_display_conversations(&self) -> Vec<(String, String, Option>)> {\n let mut conversations: Vec<(String, String, Option>)> = self.conversations\n .iter()\n .map(|(user, assistant, timestamp)| (user.clone(), assistant.clone(), Some(*timestamp)))\n .collect();\n \n // 如果有正在流式生成的回复,添加到显示列表(没有时间戳)\n if let Some((ref user_input, ref partial_response)) = self.current_streaming_response {\n conversations.push((user_input.clone(), partial_response.clone(), None));\n }\n \n conversations\n }\n\n /// 在光标位置插入字符\n pub fn insert_char_at_cursor(&mut self, c: char) {\n // 将光标位置转换为字节索引\n let byte_pos = self\n .current_input\n .chars()\n .take(self.cursor_position)\n .map(|ch| ch.len_utf8())\n .sum();\n\n self.current_input.insert(byte_pos, c);\n self.cursor_position += 1;\n }\n\n /// 在光标位置删除字符(退格键)\n pub fn delete_char_at_cursor(&mut self) {\n if self.cursor_position > 0 {\n // 将光标位置转换为字节索引\n let chars: Vec = self.current_input.chars().collect();\n if self.cursor_position <= chars.len() {\n // 找到要删除字符的字节范围\n let byte_start: usize = chars\n .iter()\n .take(self.cursor_position - 1)\n .map(|ch| ch.len_utf8())\n .sum();\n\n let byte_end: usize = chars\n .iter()\n .take(self.cursor_position)\n .map(|ch| ch.len_utf8())\n .sum();\n\n // 安全地删除字符\n self.current_input.drain(byte_start..byte_end);\n self.cursor_position -= 1;\n }\n }\n }\n\n /// 将光标向左移动一个字符\n pub fn move_cursor_left(&mut self) {\n if self.cursor_position > 0 {\n self.cursor_position -= 1;\n }\n }\n\n /// 将光标向右移动一个字符\n pub fn move_cursor_right(&mut self) {\n let input_len = self.current_input.chars().count();\n if self.cursor_position < input_len {\n self.cursor_position += 1;\n }\n }\n\n /// 重置光标位置到末尾\n pub fn reset_cursor_to_end(&mut self) {\n self.cursor_position = self.current_input.chars().count();\n }\n\n /// 滚动到日志底部(最新日志)\n pub fn scroll_logs_to_bottom(&mut self) {\n self.log_scroll_offset = 0;\n }\n\n /// 滚动到对话底部(最新对话)\n pub fn scroll_conversations_to_bottom(&mut self) {\n self.conversation_scroll_offset = 0;\n }\n\n /// 向前滚动日志(查看更早日志)\n pub fn scroll_logs_forward(&mut self) {\n if self.logs.is_empty() {\n return;\n }\n\n let page_size = 10; // 每次翻页的行数\n\n // 简单增加偏移量,让UI层处理边界\n self.log_scroll_offset += page_size;\n self.user_scrolled_logs = true;\n }\n\n /// 向后滚动日志(查看更新日志)\n pub fn scroll_logs_backward(&mut self) {\n if self.logs.is_empty() {\n return;\n }\n\n let page_size = 10; // 每次翻页的行数\n\n // 向后翻页(减少偏移量,查看更新的日志)\n if self.log_scroll_offset >= page_size {\n self.log_scroll_offset -= page_size;\n } else {\n self.log_scroll_offset = 0;\n self.user_scrolled_logs = false;\n }\n }\n\n /// 向前滚动对话(查看更早内容)\n pub fn scroll_conversations_forward(&mut self) {\n if self.conversations.is_empty() {\n return;\n }\n\n let page_size = 5; // 每次翻页的行数\n\n // 简单增加偏移量,让UI层处理边界\n self.conversation_scroll_offset += page_size;\n self.user_scrolled_conversations = true;\n }\n\n /// 向后滚动对话(查看更新内容)\n pub fn scroll_conversations_backward(&mut self) {\n if self.conversations.is_empty() {\n return;\n }\n\n let page_size = 5; // 每次翻页的行数\n\n // 向后翻页(减少偏移量,查看更新的内容)\n if self.conversation_scroll_offset >= page_size {\n self.conversation_scroll_offset -= page_size;\n } else {\n self.conversation_scroll_offset = 0;\n self.user_scrolled_conversations = false;\n }\n }\n\n /// 切换焦点到下一个区域\n pub fn next_focus(&mut self) {\n self.focus_area = match self.focus_area {\n FocusArea::Input => {\n if self.is_shutting_down {\n // 在退出过程中,跳过输入框,直接到对话区域\n FocusArea::Conversation\n } else {\n FocusArea::Conversation\n }\n }\n FocusArea::Conversation => {\n if self.is_shutting_down {\n // 在退出过程中,从对话区域切换到日志区域\n FocusArea::Logs\n } else {\n FocusArea::Logs\n }\n }\n FocusArea::Logs => {\n if self.is_shutting_down {\n // 在退出过程中,从日志区域切换回对话区域\n FocusArea::Conversation\n } else {\n FocusArea::Input\n }\n }\n };\n }\n\n pub fn log_info(&self, message: &str) {\n if let Some(sender) = &self.message_sender {\n let _ = sender.send(AppMessage::Log(format!(\"[INFO] {}\", message)));\n }\n }\n}\n" - }, - "complexity_metrics": { - "cyclomatic_complexity": 26.0, - "lines_of_code": 366, - "number_of_classes": 1, - "number_of_functions": 27 - }, - "dependencies": [ - { - "dependency_type": "library", - "is_external": true, - "line_number": 1, - "name": "ratatui", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": 2, - "name": "tokio", - "path": null, - "version": null + "dependency_type": "module", + "is_external": true, + "line_number": null, + "name": "svelte", + "path": null, + "version": null }, { "dependency_type": "library", "is_external": true, - "line_number": 3, - "name": "chrono", + "line_number": null, + "name": "chart.js", "path": null, "version": null }, { "dependency_type": "library", "is_external": true, - "line_number": 5, - "name": "once_cell", + "line_number": null, + "name": "svelte-chartjs", "path": null, "version": null }, { - "dependency_type": "std", + "dependency_type": "internal", "is_external": false, - "line_number": 4, - "name": "std", - "path": null, + "line_number": null, + "name": "$lib/api/client", + "path": "cortex-mem-insights/src/lib/api/client", "version": null }, { - "dependency_type": "type", + "dependency_type": "internal", "is_external": false, - "line_number": 21, - "name": "AppMessage", - "path": null, + "line_number": null, + "name": "$lib/i18n", + "path": "cortex-mem-insights/src/lib/i18n", "version": null } ], - "detailed_description": "This component serves as the central application state manager for a terminal-based user interface (TUI) application. It handles conversation history with timestamps, user input management with cursor positioning, real-time log collection and display, and streaming responses from an AI agent. The App struct maintains state such as conversation history (limited to 100 entries), logs (limited to 50 entries), current input with precise cursor positioning, and UI focus management between input, conversation, and log areas. It supports manual scrolling with automatic scroll-to-bottom behavior unless the user has manually scrolled. The component uses message passing (via mpsc::UnboundedSender) to communicate with other parts of the system, particularly for logging and conversation updates. Global state is managed through OnceCell for log redirection from anywhere in the codebase. The implementation includes sophisticated UTF-8 character handling for cursor positioning and editing operations.", + "detailed_description": "This component implements a comprehensive analytics dashboard for visualizing memory data statistics. It fetches memory data through the API, processes it to extract various statistical insights, and presents them through multiple visualization components. The dashboard includes summary statistics, type distribution charts, quality score distribution, time trend graphs, and user dimension statistics. It handles loading states, error conditions, and provides fallback visualizations when data is unavailable. The component uses Chart.js for data visualization and integrates with internationalization services for multilingual support.", "interfaces": [ { - "description": "Enum representing different types of messages that can be sent through the application's message channel, including logs, conversation updates, and streaming events.", - "interface_type": "enum", - "name": "AppMessage", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Enum representing the different UI areas that can have focus: Input, Conversation, and Logs.", - "interface_type": "enum", - "name": "FocusArea", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Main application state struct that manages all UI and application state including conversations, logs, input, and focus.", - "interface_type": "struct", - "name": "App", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Creates a new App instance with the provided message sender.", - "interface_type": "function", - "name": "new", - "parameters": [ - { - "description": "Channel sender for application messages", - "is_optional": false, - "name": "message_sender", - "param_type": "mpsc::UnboundedSender" - } - ], - "return_type": "Self", - "visibility": "public" - }, - { - "description": "Adds a log message to the log buffer, with automatic cleanup of old logs and optional auto-scrolling to the bottom.", - "interface_type": "function", - "name": "add_log", - "parameters": [ - { - "description": "The log message to add", - "is_optional": false, - "name": "log", - "param_type": "String" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Adds a conversation entry with timestamp, maintaining a maximum of 100 entries and optionally auto-scrolling to the bottom.", - "interface_type": "function", - "name": "add_conversation", - "parameters": [ - { - "description": "The user's message", - "is_optional": false, - "name": "user", - "param_type": "String" - }, - { - "description": "The assistant's response", - "is_optional": false, - "name": "assistant", - "param_type": "String" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Begins a streaming response, setting the processing flag and storing the user input and partial response.", - "interface_type": "function", - "name": "start_streaming_response", - "parameters": [ - { - "description": "The user's input that triggered the streaming response", - "is_optional": false, - "name": "user_input", - "param_type": "String" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Adds a chunk to the current streaming response, updating the display and optionally auto-scrolling.", - "interface_type": "function", - "name": "add_streaming_chunk", - "parameters": [ - { - "description": "A chunk of the streaming response", - "is_optional": false, - "name": "chunk", - "param_type": "String" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Completes the current streaming response by adding it to the conversation history and clearing the processing state.", + "description": "Fetches memory data from API and calculates all analytics metrics", "interface_type": "function", - "name": "complete_streaming_response", + "name": "loadAnalyticsData", "parameters": [], - "return_type": null, - "visibility": "public" + "return_type": "Promise", + "visibility": "private" }, - { - "description": "Returns the conversation history for display, including any currently streaming response without a timestamp.", - "interface_type": "function", - "name": "get_display_conversations", - "parameters": [], - "return_type": "Vec<(String, String, Option>)>", - "visibility": "public" - }, - { - "description": "Inserts a character at the current cursor position, handling UTF-8 encoding correctly.", - "interface_type": "function", - "name": "insert_char_at_cursor", - "parameters": [ - { - "description": "The character to insert", - "is_optional": false, - "name": "c", - "param_type": "char" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Deletes the character before the cursor, handling UTF-8 encoding correctly and updating cursor position.", - "interface_type": "function", - "name": "delete_char_at_cursor", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Moves the cursor one character to the left if possible.", - "interface_type": "function", - "name": "move_cursor_left", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Moves the cursor one character to the right if possible.", - "interface_type": "function", - "name": "move_cursor_right", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Resets the cursor position to the end of the current input.", - "interface_type": "function", - "name": "reset_cursor_to_end", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Scrolls the log view to show the most recent entries.", - "interface_type": "function", - "name": "scroll_logs_to_bottom", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Scrolls the conversation view to show the most recent entries.", - "interface_type": "function", - "name": "scroll_conversations_to_bottom", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Scrolls the log view forward (up) by a page, marking that the user has manually scrolled.", - "interface_type": "function", - "name": "scroll_logs_forward", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Scrolls the log view backward (down) by a page, potentially restoring auto-scroll behavior.", - "interface_type": "function", - "name": "scroll_logs_backward", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Scrolls the conversation view forward (up) by a page, marking that the user has manually scrolled.", - "interface_type": "function", - "name": "scroll_conversations_forward", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Scrolls the conversation view backward (down) by a page, potentially restoring auto-scroll behavior.", - "interface_type": "function", - "name": "scroll_conversations_backward", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Cycles through focus areas (Input, Conversation, Logs) with special behavior during shutdown.", - "interface_type": "function", - "name": "next_focus", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Sends an info-level log message through the application's message channel.", - "interface_type": "function", - "name": "log_info", - "parameters": [ - { - "description": "The info message to log", - "is_optional": false, - "name": "message", - "param_type": "&str" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Sets the global log sender that allows logging from anywhere in the application.", - "interface_type": "function", - "name": "set_global_log_sender", - "parameters": [ - { - "description": "The sender to set as the global log sender", - "is_optional": false, - "name": "sender", - "param_type": "mpsc::UnboundedSender" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Retrieves the global log sender if it has been set.", - "interface_type": "function", - "name": "get_global_log_sender", - "parameters": [], - "return_type": "Option>", - "visibility": "public" - }, - { - "description": "Redirects a log message to the UI through the global log sender.", - "interface_type": "function", - "name": "redirect_log_to_ui", - "parameters": [ - { - "description": "The log level", - "is_optional": false, - "name": "level", - "param_type": "&str" - }, - { - "description": "The log message", - "is_optional": false, - "name": "message", - "param_type": "&str" - } - ], - "return_type": null, - "visibility": "public" - } - ], - "responsibilities": [ - "Manage the central application state including conversation history, user input, and UI focus", - "Handle bidirectional communication through message passing for logs and streaming responses", - "Provide global logging redirection capability from any part of the application", - "Manage user interface state including scrolling behavior and cursor positioning with UTF-8 support", - "Coordinate the application lifecycle including shutdown procedures and memory iteration completion" - ] - }, - { - "code_dossier": { - "code_purpose": "specificfeature", - "description": "记忆有效性评估器,用于评估记忆提取、分类、去重、重要性评估等核心功能的有效性", - "file_path": "examples/cortex-mem-evaluation/src/evaluator/effectiveness_evaluator.rs", - "functions": [ - "new", - "evaluate", - "evaluate_fact_extraction", - "evaluate_classification", - "evaluate_importance", - "evaluate_deduplication", - "evaluate_memory_update", - "calculate_fact_extraction_metrics", - "calculate_classification_metrics", - "calculate_importance_metrics", - "calculate_deduplication_metrics", - "calculate_update_metrics", - "calculate_overall_score", - "load_dataset", - "save_results" - ], - "importance_score": 0.8, - "interfaces": [ - "EffectivenessEvaluator", - "EffectivenessEvaluationConfig", - "EffectivenessTestCase", - "EffectivenessTestDataset" - ], - "name": "effectiveness_evaluator.rs", - "source_summary": "//! 记忆有效性评估器\n//! \n//! 评估记忆提取、分类、去重、重要性评估等核心功能的有效性\n\nuse anyhow::{Result, Context};\nuse cortex_mem_core::{MemoryManager, Memory, MemoryType};\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\nuse tracing::{info, debug, warn};\n\nuse super::metrics::{\n EffectivenessMetrics, FactExtractionMetrics, ClassificationMetrics,\n ImportanceMetrics, DeduplicationMetrics, UpdateMetrics,\n FactExtractionResult,\n};\n\n/// 有效性测试用例\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EffectivenessTestCase {\n /// 测试用例ID\n pub test_case_id: String,\n /// 输入文本\n pub input_text: String,\n /// 预期提取的关键事实\n pub expected_facts: Vec,\n /// 预期记忆类型\n pub expected_memory_type: MemoryType,\n /// 预期重要性评分(1-10)\n pub expected_importance_score: u8,\n /// 测试类别\n pub category: String,\n /// 是否包含重复内容\n pub contains_duplicate: bool,\n /// 是否需要更新现有记忆\n pub requires_update: bool,\n /// 现有记忆ID(如果需要更新)\n pub existing_memory_id: Option,\n}\n\n/// 有效性测试数据集\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EffectivenessTestDataset {\n /// 测试用例列表\n pub test_cases: Vec,\n /// 现有记忆库(用于更新测试)\n pub existing_memories: HashMap,\n /// 数据集元数据\n pub metadata: crate::dataset::types::DatasetMetadata,\n}\n\n/// 有效性评估器\npub struct EffectivenessEvaluator {\n /// 评估配置\n config: EffectivenessEvaluationConfig,\n}\n\n/// 有效性评估配置\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EffectivenessEvaluationConfig {\n /// 是否验证事实提取\n pub verify_fact_extraction: bool,\n /// 是否验证记忆分类\n pub verify_classification: bool,\n /// 是否验证重要性评估\n pub verify_importance_evaluation: bool,\n /// 是否验证去重效果\n pub verify_deduplication: bool,\n /// 是否验证记忆更新逻辑\n pub verify_memory_update: bool,\n /// 重要性评分容差\n pub importance_score_tolerance: u8,\n /// 是否使用LLM辅助评估\n pub llm_evaluation_enabled: bool,\n /// 测试用例路径\n pub test_cases_path: String,\n}\n\nimpl Default for EffectivenessEvaluationConfig {\n fn default() -> Self {\n Self {\n verify_fact_extraction: true,\n verify_classification: true,\n verify_importance_evaluation: true,\n verify_deduplication: true,\n verify_memory_update: true,\n importance_score_tolerance: 1,\n llm_evaluation_enabled: false,\n test_cases_path: \"data/test_cases/effectiveness_test_cases.json\".to_string(),\n }\n }\n}\n\nimpl EffectivenessEvaluator {\n /// 创建新的有效性评估器\n pub fn new(config: EffectivenessEvaluationConfig) -> Self {\n Self { config }\n }\n \n /// 评估记忆管理器的有效性\n pub async fn evaluate(\n &self,\n memory_manager: &MemoryManager,\n dataset: &EffectivenessTestDataset,\n ) -> Result {\n info!(\"开始有效性评估,共{}个测试用例\", dataset.test_cases.len());\n \n let mut fact_extraction_results = Vec::new();\n let mut classification_results = Vec::new();\n let mut importance_results = Vec::new();\n let mut deduplication_results = Vec::new();\n let mut update_results = Vec::new();\n \n // 首先添加所有现有记忆\n for (memory_id, memory) in &dataset.existing_memories {\n // 这里需要实际添加记忆到内存管理器\n // 由于API限制,我们暂时跳过这一步\n debug!(\"现有记忆: {} - {}\", memory_id, &memory.content[..50.min(memory.content.len())]);\n }\n \n // 评估每个测试用例\n for test_case in &dataset.test_cases {\n debug!(\"评估测试用例: {}\", test_case.test_case_id);\n \n // 事实提取评估\n if self.config.verify_fact_extraction {\n if let Ok(result) = self.evaluate_fact_extraction(\n memory_manager,\n test_case,\n ).await {\n fact_extraction_results.push(result);\n }\n }\n \n // 记忆分类评估\n if self.config.verify_classification {\n if let Ok(result) = self.evaluate_classification(\n memory_manager,\n test_case,\n ).await {\n classification_results.push(result);\n }\n }\n \n // 重要性评估\n if self.config.verify_importance_evaluation {\n if let Ok(result) = self.evaluate_importance(\n memory_manager,\n test_case,\n ).await {\n importance_results.push(result);\n }\n }\n \n // 去重评估(如果包含重复内容)\n if self.config.verify_deduplication && test_case.contains_duplicate {\n if let Ok(result) = self.evaluate_deduplication(\n memory_manager,\n test_case,\n ).await {\n deduplication_results.push(result);\n }\n }\n \n // 记忆更新评估\n if self.config.verify_memory_update && test_case.requires_update {\n if let Ok(result) = self.evaluate_memory_update(\n memory_manager,\n test_case,\n &dataset.existing_memories,\n ).await {\n update_results.push(result);\n }\n }\n }\n \n // 计算各项指标\n let fact_extraction_metrics = self.calculate_fact_extraction_metrics(&fact_extraction_results);\n let classification_metrics = self.calculate_classification_metrics(&classification_results);\n let importance_metrics = self.calculate_importance_metrics(&importance_results);\n let deduplication_metrics = self.calculate_deduplication_metrics(&deduplication_results);\n let update_metrics = self.calculate_update_metrics(&update_results);\n \n // 计算综合得分\n let overall_score = self.calculate_overall_score(\n &fact_extraction_metrics,\n &classification_metrics,\n &importance_metrics,\n &deduplication_metrics,\n &update_metrics,\n );\n \n let metrics = EffectivenessMetrics {\n fact_extraction_accuracy: fact_extraction_metrics,\n classification_accuracy: classification_metrics,\n importance_evaluation_quality: importance_metrics,\n deduplication_effectiveness: deduplication_metrics,\n memory_update_correctness: update_metrics,\n overall_score,\n };\n \n info!(\"有效性评估完成,综合得分: {:.2}\", overall_score);\n Ok(metrics)\n }\n \n /// 评估事实提取\n async fn evaluate_fact_extraction(\n &self,\n memory_manager: &MemoryManager,\n test_case: &EffectivenessTestCase,\n ) -> Result {\n // 调用MemoryManager的事实提取功能\n // 注意:cortex-mem-core可能没有直接的事实提取API\n // 这里我们使用搜索功能来近似评估事实提取\n \n info!(\"评估事实提取 - 测试用例: {}\", test_case.test_case_id);\n \n // 尝试从输入文本中提取关键信息\n // 在实际实现中,应该调用memory_manager的提取器功能\n let extracted_facts = Vec::new(); // 暂时为空,需要实际实现\n \n // 计算匹配的事实数量\n let matched_facts = extracted_facts\n .iter()\n .filter(|fact| test_case.expected_facts.contains(fact))\n .count();\n \n let is_perfect_match = matched_facts == test_case.expected_facts.len() &&\n matched_facts == extracted_facts.len();\n \n Ok(FactExtractionResult {\n input_text: test_case.input_text.clone(),\n extracted_facts,\n ground_truth_facts: test_case.expected_facts.clone(),\n matched_facts,\n is_perfect_match,\n })\n }\n \n /// 评估记忆分类\n async fn evaluate_classification(\n &self,\n memory_manager: &MemoryManager,\n test_case: &EffectivenessTestCase,\n ) -> Result {\n // 调用MemoryManager的分类功能\n info!(\"评估记忆分类 - 测试用例: {}\", test_case.test_case_id);\n \n // 在实际实现中,应该调用memory_manager的分类器\n // 这里我们暂时使用默认类型,需要实际实现\n let predicted_type = MemoryType::Conversational; // 默认类型,需要实际实现\n \n let is_correct = predicted_type == test_case.expected_memory_type;\n \n Ok(ClassificationResult {\n test_case_id: test_case.test_case_id.clone(),\n input_text: test_case.input_text.clone(),\n predicted_type,\n expected_type: test_case.expected_memory_type.clone(),\n is_correct,\n })\n }\n \n /// 评估重要性\n async fn evaluate_importance(\n &self,\n memory_manager: &MemoryManager,\n test_case: &EffectivenessTestCase,\n ) -> Result {\n // 调用MemoryManager的重要性评估功能\n info!(\"评估重要性 - 测试用例: {}\", test_case.test_case_id);\n \n // 在实际实现中,应该调用memory_manager的重要性评估器\n // 这里我们暂时使用默认值,需要实际实现\n let predicted_score = 5; // 默认中等重要性,需要实际实现\n \n let error = (predicted_score as i16 - test_case.expected_importance_score as i16).abs();\n let within_tolerance = error <= self.config.importance_score_tolerance as i16;\n \n Ok(ImportanceResult {\n test_case_id: test_case.test_case_id.clone(),\n input_text: test_case.input_text.clone(),\n predicted_score,\n expected_score: test_case.expected_importance_score,\n error: error as u8,\n within_tolerance,\n })\n }\n \n /// 评估去重效果\n async fn evaluate_deduplication(\n &self,\n memory_manager: &MemoryManager,\n test_case: &EffectivenessTestCase,\n ) -> Result {\n // 测试重复内容的检测和合并\n info!(\"评估去重效果 - 测试用例: {}\", test_case.test_case_id);\n \n // 在实际实现中,应该测试memory_manager的去重功能\n // 这里我们暂时返回默认值,需要实际实现\n \n Ok(DeduplicationResult {\n test_case_id: test_case.test_case_id.clone(),\n duplicate_detected: false, // 需要实际测试\n correctly_merged: false, // 需要实际测试\n merge_quality: 0.0, // 需要实际测试\n })\n }\n \n /// 评估记忆更新\n async fn evaluate_memory_update(\n &self,\n memory_manager: &MemoryManager,\n test_case: &EffectivenessTestCase,\n existing_memories: &HashMap,\n ) -> Result {\n // 测试记忆更新逻辑\n info!(\"评估记忆更新 - 测试用例: {}\", test_case.test_case_id);\n \n // 在实际实现中,应该测试memory_manager的更新功能\n // 这里我们暂时返回默认值,需要实际实现\n \n Ok(UpdateResult {\n test_case_id: test_case.test_case_id.clone(),\n update_correct: false, // 需要实际测试\n merge_correct: false, // 需要实际测试\n conflict_resolved: false, // 需要实际测试\n updated_quality: 0.0, // 需要实际测试\n })\n }\n \n /// 计算事实提取指标\n fn calculate_fact_extraction_metrics(\n &self,\n results: &[FactExtractionResult],\n ) -> FactExtractionMetrics {\n let total_facts_extracted: usize = results.iter()\n .map(|r| r.extracted_facts.len())\n .sum();\n \n let total_correct_facts: usize = results.iter()\n .map(|r| r.matched_facts)\n .sum();\n \n let total_expected_facts: usize = results.iter()\n .map(|r| r.ground_truth_facts.len())\n .sum();\n \n let precision = if total_facts_extracted > 0 {\n total_correct_facts as f64 / total_facts_extracted as f64\n } else {\n 0.0\n };\n \n let recall = if total_expected_facts > 0 {\n total_correct_facts as f64 / total_expected_facts as f64\n } else {\n 0.0\n };\n \n let f1_score = if precision + recall > 0.0 {\n 2.0 * precision * recall / (precision + recall)\n } else {\n 0.0\n };\n \n FactExtractionMetrics {\n precision,\n recall,\n f1_score,\n facts_extracted: total_facts_extracted,\n correct_facts: total_correct_facts,\n detailed_results: results.to_vec(),\n }\n }\n \n /// 计算分类指标\n fn calculate_classification_metrics(\n &self,\n results: &[ClassificationResult],\n ) -> ClassificationMetrics {\n let total_correct = results.iter().filter(|r| r.is_correct).count();\n let accuracy = if !results.is_empty() {\n total_correct as f64 / results.len() as f64\n } else {\n 0.0\n };\n \n // 按类别统计\n let mut confusion_matrix: HashMap> = HashMap::new();\n let mut precision_by_class: HashMap = HashMap::new();\n let mut recall_by_class: HashMap = HashMap::new();\n let mut f1_by_class: HashMap = HashMap::new();\n \n for result in results {\n let predicted = format!(\"{:?}\", result.predicted_type);\n let expected = format!(\"{:?}\", result.expected_type);\n \n *confusion_matrix\n .entry(expected.clone())\n .or_default()\n .entry(predicted.clone())\n .or_default() += 1;\n }\n \n // 计算每个类别的指标\n for (expected_class, predictions) in &confusion_matrix {\n let total_predicted_as_class: usize = confusion_matrix.values()\n .map(|pred_map| pred_map.get(expected_class).copied().unwrap_or(0))\n .sum();\n \n let true_positives = predictions.get(expected_class).copied().unwrap_or(0);\n let false_positives = total_predicted_as_class - true_positives;\n let false_negatives: usize = predictions.values().sum::() - true_positives;\n \n let precision = if true_positives + false_positives > 0 {\n true_positives as f64 / (true_positives + false_positives) as f64\n } else {\n 0.0\n };\n \n let recall = if true_positives + false_negatives > 0 {\n true_positives as f64 / (true_positives + false_negatives) as f64\n } else {\n 0.0\n };\n \n let f1 = if precision + recall > 0.0 {\n 2.0 * precision * recall / (precision + recall)\n } else {\n 0.0\n };\n \n precision_by_class.insert(expected_class.clone(), precision);\n recall_by_class.insert(expected_class.clone(), recall);\n f1_by_class.insert(expected_class.clone(), f1);\n }\n \n ClassificationMetrics {\n accuracy,\n precision_by_class,\n recall_by_class,\n f1_by_class,\n confusion_matrix,\n }\n }\n \n /// 计算重要性评估指标\n fn calculate_importance_metrics(\n &self,\n results: &[ImportanceResult],\n ) -> ImportanceMetrics {\n if results.is_empty() {\n return ImportanceMetrics {\n correlation_score: 0.0,\n mean_absolute_error: 0.0,\n root_mean_squared_error: 0.0,\n score_distribution: HashMap::new(),\n within_tolerance_rate: 0.0,\n };\n }\n \n let mut total_abs_error = 0.0;\n let mut total_squared_error = 0.0;\n let mut predicted_scores = Vec::new();\n let mut expected_scores = Vec::new();\n let mut score_distribution: HashMap = HashMap::new();\n \n for result in results {\n let error = result.error as f64;\n total_abs_error += error;\n total_squared_error += error * error;\n \n predicted_scores.push(result.predicted_score as f64);\n expected_scores.push(result.expected_score as f64);\n \n *score_distribution.entry(result.predicted_score as usize).or_default() += 1;\n }\n \n let mean_absolute_error = total_abs_error / results.len() as f64;\n let root_mean_squared_error = (total_squared_error / results.len() as f64).sqrt();\n \n // 简化相关性计算(实际应该使用皮尔逊相关系数)\n let correlation_score = if !predicted_scores.is_empty() && !expected_scores.is_empty() {\n // 模拟相关性计算\n let avg_predicted: f64 = predicted_scores.iter().sum::() / predicted_scores.len() as f64;\n let avg_expected: f64 = expected_scores.iter().sum::() / expected_scores.len() as f64;\n \n let mut covariance = 0.0;\n let mut var_predicted = 0.0;\n let mut var_expected = 0.0;\n \n for i in 0..predicted_scores.len() {\n let diff_pred = predicted_scores[i] - avg_predicted;\n let diff_exp = expected_scores[i] - avg_expected;\n covariance += diff_pred * diff_exp;\n var_predicted += diff_pred * diff_pred;\n var_expected += diff_exp * diff_exp;\n }\n \n if var_predicted > 0.0 && var_expected > 0.0 {\n covariance / (var_predicted.sqrt() * var_expected.sqrt())\n } else {\n 0.0\n }\n } else {\n 0.0\n };\n \n ImportanceMetrics {\n correlation_score: correlation_score.max(0.0).min(1.0),\n mean_absolute_error,\n root_mean_squared_error,\n score_distribution,\n within_tolerance_rate: 0.0, // 暂时设为0,后续可以计算实际值\n }\n }\n \n /// 计算去重指标\n fn calculate_deduplication_metrics(\n &self,\n results: &[DeduplicationResult],\n ) -> DeduplicationMetrics {\n if results.is_empty() {\n return DeduplicationMetrics {\n duplicate_detection_precision: 0.0,\n duplicate_detection_recall: 0.0,\n merge_accuracy: 0.0,\n duplicate_pairs_detected: 0,\n actual_duplicate_pairs: 0,\n avg_merge_quality: 0.0,\n };\n }\n \n let true_positives = results.iter().filter(|r| r.duplicate_detected).count();\n let false_positives = 0; // 简化:假设没有误报\n let false_negatives = 0; // 简化:假设没有漏报\n \n let precision = if true_positives + false_positives > 0 {\n true_positives as f64 / (true_positives + false_positives) as f64\n } else {\n 0.0\n };\n \n let recall = if true_positives + false_negatives > 0 {\n true_positives as f64 / (true_positives + false_negatives) as f64\n } else {\n 0.0\n };\n \n let merge_accuracy = if !results.is_empty() {\n results.iter().filter(|r| r.correctly_merged).count() as f64 / results.len() as f64\n } else {\n 0.0\n };\n \n DeduplicationMetrics {\n duplicate_detection_precision: precision,\n duplicate_detection_recall: recall,\n merge_accuracy,\n duplicate_pairs_detected: true_positives,\n actual_duplicate_pairs: true_positives, // 简化:假设检测到的都是实际的\n avg_merge_quality: merge_accuracy, // 暂时用合并准确率作为合并质量\n }\n }\n \n /// 计算更新指标\n fn calculate_update_metrics(\n &self,\n results: &[UpdateResult],\n ) -> UpdateMetrics {\n if results.is_empty() {\n return UpdateMetrics {\n update_operation_accuracy: 0.0,\n merge_operation_accuracy: 0.0,\n conflict_resolution_accuracy: 0.0,\n updated_memory_quality: 0.0,\n };\n }\n \n let update_accuracy = results.iter().filter(|r| r.update_correct).count() as f64 / results.len() as f64;\n let merge_accuracy = results.iter().filter(|r| r.merge_correct).count() as f64 / results.len() as f64;\n let conflict_accuracy = results.iter().filter(|r| r.conflict_resolved).count() as f64 / results.len() as f64;\n let avg_quality = results.iter().map(|r| r.updated_quality).sum::() / results.len() as f64;\n \n UpdateMetrics {\n update_operation_accuracy: update_accuracy,\n merge_operation_accuracy: merge_accuracy,\n conflict_resolution_accuracy: conflict_accuracy,\n updated_memory_quality: avg_quality,\n }\n }\n \n /// 计算综合得分\n fn calculate_overall_score(\n &self,\n fact_metrics: &FactExtractionMetrics,\n classification_metrics: &ClassificationMetrics,\n importance_metrics: &ImportanceMetrics,\n deduplication_metrics: &DeduplicationMetrics,\n update_metrics: &UpdateMetrics,\n ) -> f64 {\n let mut total_score = 0.0;\n let mut weight_sum = 0.0;\n \n // 事实提取权重:0.3\n if self.config.verify_fact_extraction {\n let fact_score = (fact_metrics.f1_score + fact_metrics.precision + fact_metrics.recall) / 3.0;\n total_score += fact_score * 0.3;\n weight_sum += 0.3;\n }\n \n // 分类权重:0.2\n if self.config.verify_classification {\n total_score += classification_metrics.accuracy * 0.2;\n weight_sum += 0.2;\n }\n \n // 重要性评估权重:0.2\n if self.config.verify_importance_evaluation {\n let importance_score = 1.0 - importance_metrics.mean_absolute_error / 10.0; // 归一化到0-1\n total_score += importance_score.max(0.0).min(1.0) * 0.2;\n weight_sum += 0.2;\n }\n \n // 去重权重:0.15\n if self.config.verify_deduplication {\n let dedup_score = (deduplication_metrics.duplicate_detection_precision +\n deduplication_metrics.duplicate_detection_recall +\n deduplication_metrics.merge_accuracy) / 3.0;\n total_score += dedup_score * 0.15;\n weight_sum += 0.15;\n }\n \n // 更新权重:0.15\n if self.config.verify_memory_update {\n let update_score = (update_metrics.update_operation_accuracy +\n update_metrics.merge_operation_accuracy +\n update_metrics.conflict_resolution_accuracy +\n update_metrics.updated_memory_quality) / 4.0;\n total_score += update_score * 0.15;\n weight_sum += 0.15;\n }\n \n if weight_sum > 0.0 {\n total_score / weight_sum\n } else {\n 0.0\n }\n }\n \n /// 加载测试数据集\n pub fn load_dataset(path: &str) -> Result {\n let content = std::fs::read_to_string(path)\n .context(format!(\"读取数据集文件失败: {}\", path))?;\n \n let dataset: EffectivenessTestDataset = serde_json::from_str(&content)\n .context(\"解析数据集JSON失败\")?;\n \n info!(\"加载有效性测试数据集: {}个测试用例, {}个现有记忆\",\n dataset.test_cases.len(), dataset.existing_memories.len());\n \n Ok(dataset)\n }\n \n /// 保存评估结果\n pub fn save_results(&self, metrics: &EffectivenessMetrics, output_path: &str) -> Result<()> {\n let json = serde_json::to_string_pretty(metrics)\n .context(\"序列化评估结果失败\")?;\n \n std::fs::write(output_path, json)\n .context(format!(\"写入评估结果文件失败: {}\", output_path))?;\n \n info!(\"有效性评估结果已保存到: {}\", output_path);\n Ok(())\n }\n}\n\n// 辅助结构体\n#[derive(Debug, Clone)]\nstruct ClassificationResult {\n test_case_id: String,\n input_text: String,\n predicted_type: MemoryType,\n expected_type: MemoryType,\n is_correct: bool,\n}\n\n#[derive(Debug, Clone)]\nstruct ImportanceResult {\n test_case_id: String,\n input_text: String,\n predicted_score: u8,\n expected_score: u8,\n error: u8,\n within_tolerance: bool,\n}\n\n#[derive(Debug, Clone)]\nstruct DeduplicationResult {\n test_case_id: String,\n duplicate_detected: bool,\n correctly_merged: bool,\n merge_quality: f64,\n}\n\n#[derive(Debug, Clone)]\nstruct UpdateResult {\n test_case_id: String,\n update_correct: bool,\n merge_correct: bool,\n conflict_resolved: bool,\n updated_quality: f64,\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 41.0, - "lines_of_code": 713, - "number_of_classes": 4, - "number_of_functions": 19 - }, - "dependencies": [ - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": false, - "line_number": null, - "name": "cortex_mem_core", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "serde", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": false, - "line_number": null, - "name": "std::collections::HashMap", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "tracing", - "path": null, - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": null, - "name": "super::metrics", - "path": "examples/cortex-mem-evaluation/src/evaluator/metrics", - "version": null - } - ], - "detailed_description": "该组件是记忆管理系统的核心评估模块,通过定义测试用例和数据集来系统性地评估记忆管理器在事实提取、记忆分类、重要性评估、去重和记忆更新等核心功能上的表现。组件采用模块化设计,每个评估功能都有独立的方法实现,并通过综合指标计算提供整体有效性评分。评估器支持配置化,允许启用或禁用特定的评估项,并提供了结果保存和数据加载功能,形成了完整的评估闭环。", - "interfaces": [ - { - "description": "有效性评估器主结构体", - "interface_type": "struct", - "name": "EffectivenessEvaluator", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "有效性评估配置", - "interface_type": "struct", - "name": "EffectivenessEvaluationConfig", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "有效性测试用例", - "interface_type": "struct", - "name": "EffectivenessTestCase", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "有效性测试数据集", - "interface_type": "struct", - "name": "EffectivenessTestDataset", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "创建新的有效性评估器", - "interface_type": "method", - "name": "new", - "parameters": [ - { - "description": "评估配置", - "is_optional": false, - "name": "config", - "param_type": "EffectivenessEvaluationConfig" - } - ], - "return_type": "EffectivenessEvaluator", - "visibility": "public" - }, - { - "description": "评估记忆管理器的有效性", - "interface_type": "method", - "name": "evaluate", - "parameters": [ - { - "description": "内存管理器引用", - "is_optional": false, - "name": "memory_manager", - "param_type": "&MemoryManager" - }, - { - "description": "测试数据集", - "is_optional": false, - "name": "dataset", - "param_type": "&EffectivenessTestDataset" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "评估事实提取", - "interface_type": "method", - "name": "evaluate_fact_extraction", - "parameters": [ - { - "description": "内存管理器引用", - "is_optional": false, - "name": "memory_manager", - "param_type": "&MemoryManager" - }, - { - "description": "测试用例", - "is_optional": false, - "name": "test_case", - "param_type": "&EffectivenessTestCase" - } - ], - "return_type": "Result", - "visibility": "private" - }, - { - "description": "评估记忆分类", - "interface_type": "method", - "name": "evaluate_classification", - "parameters": [ - { - "description": "内存管理器引用", - "is_optional": false, - "name": "memory_manager", - "param_type": "&MemoryManager" - }, - { - "description": "测试用例", - "is_optional": false, - "name": "test_case", - "param_type": "&EffectivenessTestCase" - } - ], - "return_type": "Result", - "visibility": "private" - }, - { - "description": "评估重要性", - "interface_type": "method", - "name": "evaluate_importance", - "parameters": [ - { - "description": "内存管理器引用", - "is_optional": false, - "name": "memory_manager", - "param_type": "&MemoryManager" - }, - { - "description": "测试用例", - "is_optional": false, - "name": "test_case", - "param_type": "&EffectivenessTestCase" - } - ], - "return_type": "Result", - "visibility": "private" - }, - { - "description": "评估去重效果", - "interface_type": "method", - "name": "evaluate_deduplication", - "parameters": [ - { - "description": "内存管理器引用", - "is_optional": false, - "name": "memory_manager", - "param_type": "&MemoryManager" - }, - { - "description": "测试用例", - "is_optional": false, - "name": "test_case", - "param_type": "&EffectivenessTestCase" - } - ], - "return_type": "Result", - "visibility": "private" - }, - { - "description": "评估记忆更新", - "interface_type": "method", - "name": "evaluate_memory_update", - "parameters": [ - { - "description": "内存管理器引用", - "is_optional": false, - "name": "memory_manager", - "param_type": "&MemoryManager" - }, - { - "description": "测试用例", - "is_optional": false, - "name": "test_case", - "param_type": "&EffectivenessTestCase" - }, - { - "description": "现有记忆", - "is_optional": false, - "name": "existing_memories", - "param_type": "&HashMap" - } - ], - "return_type": "Result", - "visibility": "private" - }, - { - "description": "计算事实提取指标", - "interface_type": "method", - "name": "calculate_fact_extraction_metrics", - "parameters": [ - { - "description": "评估结果", - "is_optional": false, - "name": "results", - "param_type": "&[FactExtractionResult]" - } - ], - "return_type": "FactExtractionMetrics", - "visibility": "private" - }, - { - "description": "计算分类指标", - "interface_type": "method", - "name": "calculate_classification_metrics", - "parameters": [ - { - "description": "评估结果", - "is_optional": false, - "name": "results", - "param_type": "&[ClassificationResult]" - } - ], - "return_type": "ClassificationMetrics", - "visibility": "private" - }, - { - "description": "计算重要性评估指标", - "interface_type": "method", - "name": "calculate_importance_metrics", - "parameters": [ - { - "description": "评估结果", - "is_optional": false, - "name": "results", - "param_type": "&[ImportanceResult]" - } - ], - "return_type": "ImportanceMetrics", - "visibility": "private" - }, - { - "description": "计算去重指标", - "interface_type": "method", - "name": "calculate_deduplication_metrics", - "parameters": [ - { - "description": "评估结果", - "is_optional": false, - "name": "results", - "param_type": "&[DeduplicationResult]" - } - ], - "return_type": "DeduplicationMetrics", - "visibility": "private" - }, - { - "description": "计算更新指标", - "interface_type": "method", - "name": "calculate_update_metrics", - "parameters": [ - { - "description": "评估结果", - "is_optional": false, - "name": "results", - "param_type": "&[UpdateResult]" - } - ], - "return_type": "UpdateMetrics", - "visibility": "private" - }, - { - "description": "计算综合得分", - "interface_type": "method", - "name": "calculate_overall_score", - "parameters": [ - { - "description": "事实提取指标", - "is_optional": false, - "name": "fact_metrics", - "param_type": "&FactExtractionMetrics" - }, - { - "description": "分类指标", - "is_optional": false, - "name": "classification_metrics", - "param_type": "&ClassificationMetrics" - }, - { - "description": "重要性评估指标", - "is_optional": false, - "name": "importance_metrics", - "param_type": "&ImportanceMetrics" - }, - { - "description": "去重指标", - "is_optional": false, - "name": "deduplication_metrics", - "param_type": "&DeduplicationMetrics" - }, - { - "description": "更新指标", - "is_optional": false, - "name": "update_metrics", - "param_type": "&UpdateMetrics" - } - ], - "return_type": "f64", - "visibility": "private" - }, - { - "description": "加载测试数据集", - "interface_type": "method", - "name": "load_dataset", - "parameters": [ - { - "description": "数据集路径", - "is_optional": false, - "name": "path", - "param_type": "&str" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "保存评估结果", - "interface_type": "method", - "name": "save_results", - "parameters": [ - { - "description": "评估指标", - "is_optional": false, - "name": "metrics", - "param_type": "&EffectivenessMetrics" - }, - { - "description": "输出路径", - "is_optional": false, - "name": "output_path", - "param_type": "&str" - } - ], - "return_type": "Result<()>", - "visibility": "public" - } - ], - "responsibilities": [ - "评估记忆管理器的事实提取准确性", - "评估记忆分类的正确性", - "评估记忆重要性评分的准确性", - "评估记忆去重机制的有效性", - "评估记忆更新逻辑的正确性" - ] - }, - { - "code_dossier": { - "code_purpose": "specificfeature", - "description": "真实召回率评估器,基于真实cortex-mem-core API调用进行召回率评估", - "file_path": "examples/cortex-mem-evaluation/src/evaluator/real_recall_evaluator.rs", - "functions": [ - "new", - "evaluate", - "verify_memory_integrity", - "prepare_memory_library", - "evaluate_with_threshold", - "calculate_precision_recall_at_k", - "calculate_mean_average_precision", - "calculate_ndcg", - "cleanup_test_memories" - ], - "importance_score": 0.8, - "interfaces": [ - "RealRecallEvaluationConfig", - "RealRecallEvaluator", - "ThresholdEvaluationResult", - "EnhancedThresholdMetrics" - ], - "name": "real_recall_evaluator.rs", - "source_summary": "//! 真实召回率评估器\n//! \n//! 基于真实cortex-mem-core API调用的召回率评估\n\nuse anyhow::{Result, Context};\nuse cortex_mem_core::{MemoryManager, Memory, ScoredMemory, types::Filters};\nuse serde::{Deserialize, Serialize};\nuse std::collections::{HashMap, HashSet};\nuse std::time::{Duration, Instant};\nuse tracing::{info, debug, warn, error};\n\nuse super::metrics::{RecallMetrics, ThresholdMetrics, QueryResult};\nuse crate::dataset::types::RecallTestDataset;\n\n/// 真实召回率评估器配置\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct RealRecallEvaluationConfig {\n /// K值列表\n pub k_values: Vec,\n /// 相似度阈值列表\n pub similarity_thresholds: Vec,\n /// 每个查询的最大返回结果数\n pub max_results_per_query: usize,\n /// 是否保存详细结果\n pub save_detailed_results: bool,\n /// 超时时间(秒)\n pub timeout_seconds: u64,\n /// 是否启用并行评估\n pub enable_parallel_evaluation: bool,\n /// 是否验证记忆库完整性\n pub verify_memory_integrity: bool,\n /// 测试数据集路径\n pub test_cases_path: String,\n}\n\nimpl Default for RealRecallEvaluationConfig {\n fn default() -> Self {\n Self {\n k_values: vec![1, 3, 5, 10],\n similarity_thresholds: vec![0.7, 0.8, 0.9],\n max_results_per_query: 20,\n save_detailed_results: true,\n timeout_seconds: 30,\n enable_parallel_evaluation: true,\n verify_memory_integrity: true,\n test_cases_path: \"data/test_cases/lab_recall_dataset.json\".to_string(),\n }\n }\n}\n\n/// 阈值评估结果(包含查询结果)\nstruct ThresholdEvaluationResult {\n /// 阈值指标\n metrics: ThresholdMetrics,\n /// 查询结果\n query_results: Vec,\n}\n\n/// 真实召回率评估器\npub struct RealRecallEvaluator {\n /// 评估配置\n config: RealRecallEvaluationConfig,\n /// 记忆管理器\n memory_manager: std::sync::Arc,\n}\n\nimpl RealRecallEvaluator {\n /// 创建新的真实召回率评估器\n pub fn new(\n config: RealRecallEvaluationConfig,\n memory_manager: std::sync::Arc,\n ) -> Self {\n Self {\n config,\n memory_manager,\n }\n }\n \n /// 评估记忆管理器的召回率\n pub async fn evaluate(\n &self,\n dataset: &RecallTestDataset,\n ) -> Result {\n info!(\"开始真实召回率评估,共{}个测试用例\", dataset.test_cases.len());\n \n // 验证记忆库完整性\n if self.config.verify_memory_integrity {\n self.verify_memory_integrity(&dataset.memories).await?;\n }\n \n // 准备记忆库\n self.prepare_memory_library(&dataset.memories).await?;\n \n let mut query_results = Vec::new();\n let mut metrics_by_threshold = HashMap::new();\n \n // 为每个相似度阈值评估\n for &threshold in &self.config.similarity_thresholds {\n info!(\"评估相似度阈值: {}\", threshold);\n \n let start_time = Instant::now();\n \n let threshold_result = self.evaluate_with_threshold(\n dataset,\n threshold,\n ).await?;\n \n let elapsed = start_time.elapsed();\n info!(\"阈值 {} 评估完成,耗时: {:?}\", threshold, elapsed);\n \n metrics_by_threshold.insert(threshold.to_string(), threshold_result.metrics);\n }\n \n // 使用第一个阈值(或默认阈值0.8)计算总体指标\n let default_threshold = self.config.similarity_thresholds.first().copied().unwrap_or(0.8);\n info!(\"使用阈值 {} 计算总体指标\", default_threshold);\n \n // 为默认阈值运行评估以获取query_results\n let default_threshold_result = self.evaluate_with_threshold(dataset, default_threshold).await?;\n query_results = default_threshold_result.query_results;\n \n let mut precision_at_k = HashMap::new();\n let mut recall_at_k = HashMap::new();\n \n for &k in &self.config.k_values {\n let (precision, recall) = self.calculate_precision_recall_at_k(\n &query_results,\n k,\n );\n precision_at_k.insert(k, precision);\n recall_at_k.insert(k, recall);\n }\n \n // 计算MAP和NDCG\n let mean_average_precision = self.calculate_mean_average_precision(&query_results);\n let normalized_discounted_cumulative_gain = \n self.calculate_ndcg(&query_results, self.config.k_values.last().copied().unwrap_or(10));\n \n let metrics = RecallMetrics {\n precision_at_k,\n recall_at_k,\n mean_average_precision,\n normalized_discounted_cumulative_gain,\n metrics_by_threshold,\n query_level_results: if self.config.save_detailed_results {\n query_results\n } else {\n Vec::new()\n },\n };\n \n info!(\"真实召回率评估完成\");\n Ok(metrics)\n }\n \n /// 验证记忆库完整性\n async fn verify_memory_integrity(\n &self,\n memories: &HashMap,\n ) -> Result<()> {\n info!(\"验证记忆库完整性,共{}个记忆\", memories.len());\n \n let mut valid_count = 0;\n let mut invalid_count = 0;\n \n for (memory_id, memory) in memories {\n // 检查基本字段\n if memory.id.is_empty() {\n warn!(\"记忆 {}: ID为空\", memory_id);\n invalid_count += 1;\n continue;\n }\n \n if memory.content.is_empty() {\n warn!(\"记忆 {}: 内容为空\", memory_id);\n invalid_count += 1;\n continue;\n }\n \n if memory.embedding.is_empty() {\n debug!(\"记忆 {}: 嵌入向量为空(可能未生成)\", memory_id);\n }\n \n valid_count += 1;\n }\n \n let validity_rate = valid_count as f64 / memories.len() as f64;\n info!(\"记忆库完整性验证完成: 有效={}, 无效={}, 有效率={:.2}%\", \n valid_count, invalid_count, validity_rate * 100.0);\n \n if validity_rate < 0.8 {\n warn!(\"记忆库有效率低于80%,可能影响评估结果\");\n }\n \n Ok(())\n }\n \n /// 准备记忆库(将测试记忆添加到记忆管理器)\n async fn prepare_memory_library(\n &self,\n memories: &HashMap,\n ) -> Result<()> {\n info!(\"准备记忆库,添加{}个测试记忆\", memories.len());\n \n let mut added_count = 0;\n let mut skipped_count = 0;\n let mut error_count = 0;\n \n for (memory_id, memory) in memories {\n // 安全地截取前50个字符,避免UTF-8边界错误\n let preview = if memory.content.len() <= 50 {\n &memory.content\n } else {\n // 找到第50个字符的边界\n let mut end = 50;\n while end < memory.content.len() && !memory.content.is_char_boundary(end) {\n end += 1;\n }\n &memory.content[..end.min(memory.content.len())]\n };\n info!(\"正在添加记忆 {}: {}...\", memory_id, preview);\n \n // 尝试添加记忆\n match self.memory_manager.store(\n memory.content.clone(),\n memory.metadata.clone(),\n ).await {\n Ok(new_memory_id) => {\n info!(\"添加记忆成功: {} -> {}\", memory_id, new_memory_id);\n added_count += 1;\n }\n Err(e) => {\n // 检查是否是重复错误\n if e.to_string().contains(\"duplicate\") || e.to_string().contains(\"already exists\") {\n info!(\"记忆已存在: {}\", memory_id);\n skipped_count += 1;\n } else {\n error!(\"添加记忆失败 {}: {}\", memory_id, e);\n error_count += 1;\n }\n }\n }\n \n // 限制添加速率,避免过载(特别是对于真实LLM客户端)\n // 每添加一个记忆都等待一段时间,避免API调用频率过高\n tokio::time::sleep(Duration::from_millis(500)).await; // 500毫秒延迟\n \n // 额外的:每5个记忆等待更长时间\n if added_count % 5 == 0 {\n info!(\"已添加{}个记忆,等待2秒避免API限制...\", added_count);\n tokio::time::sleep(Duration::from_secs(2)).await;\n }\n }\n \n info!(\"记忆库准备完成: 添加={}, 跳过={}, 错误={}\", \n added_count, skipped_count, error_count);\n \n if error_count > 0 {\n warn!(\"有{}个记忆添加失败,可能影响评估结果\", error_count);\n }\n \n if added_count == 0 && skipped_count == 0 {\n error!(\"没有成功添加任何记忆!评估结果将不准确\");\n }\n \n Ok(())\n }\n \n /// 使用特定阈值评估\n async fn evaluate_with_threshold(\n &self,\n dataset: &RecallTestDataset,\n similarity_threshold: f32,\n ) -> Result {\n let mut total_precision = 0.0;\n let mut total_recall = 0.0;\n let mut total_results_returned = 0;\n let mut query_count = 0;\n let mut query_results = Vec::new();\n \n let mut total_latency = Duration::default();\n let mut success_count = 0;\n let mut error_count = 0;\n \n for test_case in &dataset.test_cases {\n let query_start = Instant::now();\n \n // 创建过滤器\n let filters = Filters {\n user_id: Some(\"test_user\".to_string()), // 使用测试用户\n ..Default::default()\n };\n \n // 执行真实搜索\n info!(\"执行搜索 - 查询ID: {}, 查询内容: {}..., 阈值: {}, 最大结果数: {}\", \n test_case.query_id, \n &test_case.query[..test_case.query.len().min(30)],\n similarity_threshold,\n self.config.max_results_per_query);\n \n let search_result = tokio::time::timeout(\n Duration::from_secs(self.config.timeout_seconds),\n self.memory_manager.search_with_threshold(\n &test_case.query,\n &filters,\n self.config.max_results_per_query,\n Some(similarity_threshold),\n )\n ).await;\n \n let latency = query_start.elapsed();\n total_latency += latency;\n \n match search_result {\n Ok(Ok(search_results)) => {\n success_count += 1;\n \n // 计算相关记忆ID集合\n let relevant_ids: HashSet<&str> = test_case.relevant_memory_ids\n .iter()\n .map(|id| id.as_str())\n .collect();\n \n // 计算检索到的相关记忆\n let retrieved_relevant = search_results\n .iter()\n .filter(|m| relevant_ids.contains(m.memory.id.as_str()))\n .count();\n \n let retrieved_total = search_results.len();\n let relevant_total = relevant_ids.len();\n \n // 计算精确率和召回率\n let precision = if retrieved_total > 0 {\n retrieved_relevant as f64 / retrieved_total as f64\n } else {\n 0.0\n };\n \n let recall = if relevant_total > 0 {\n retrieved_relevant as f64 / relevant_total as f64\n } else {\n 0.0\n };\n \n total_precision += precision;\n total_recall += recall;\n total_results_returned += retrieved_total;\n query_count += 1;\n \n // 保存查询结果用于后续计算\n let query_result = QueryResult {\n query_id: test_case.query_id.clone(),\n query: test_case.query.clone(),\n precision,\n recall,\n retrieved_total,\n retrieved_relevant,\n relevant_memories: relevant_total,\n average_precision: self.calculate_average_precision(&search_results, &relevant_ids),\n latency_ms: latency.as_millis() as u64,\n };\n \n query_results.push(query_result);\n \n // 添加详细调试信息\n if retrieved_total == 0 {\n warn!(\"查询 {} 返回0个结果!相关记忆ID: {:?}\", test_case.query_id, test_case.relevant_memory_ids);\n \n // 安全地截取字符串,避免UTF-8边界错误\n let preview_len = test_case.query.len().min(100);\n let mut end = preview_len;\n while end > 0 && !test_case.query.is_char_boundary(end) {\n end -= 1;\n }\n warn!(\"查询内容: {}...\", &test_case.query[..end]);\n } else {\n info!(\"查询 {} 返回 {} 个结果,其中 {} 个相关\", \n test_case.query_id, retrieved_total, retrieved_relevant);\n \n // 显示前3个结果的相似度分数\n for (i, result) in search_results.iter().take(3).enumerate() {\n info!(\" 结果 {}: ID={}, 相似度={:.3}, 内容: {}...\", \n i + 1, \n result.memory.id,\n result.score,\n &result.memory.content[..result.memory.content.len().min(50)]);\n }\n }\n \n debug!(\n \"查询 {}: 精确率={:.3}, 召回率={:.3}, 返回结果={}, 延迟={:?}\",\n test_case.query_id, precision, recall, retrieved_total, latency\n );\n }\n Ok(Err(e)) => {\n error_count += 1;\n warn!(\"查询 {} 失败: {}\", test_case.query_id, e);\n }\n Err(_) => {\n error_count += 1;\n warn!(\"查询 {} 超时 ({}秒)\", test_case.query_id, self.config.timeout_seconds);\n }\n }\n \n // 限制查询速率,避免过载(特别是对于真实LLM客户端)\n // 每个查询都等待一段时间,避免API调用频率过高\n tokio::time::sleep(Duration::from_millis(1000)).await; // 1秒延迟\n \n // 额外的:每3个查询等待更长时间\n if query_count % 3 == 0 {\n info!(\"已处理{}个查询,等待3秒避免API限制...\", query_count);\n tokio::time::sleep(Duration::from_secs(3)).await;\n }\n }\n \n let avg_precision = if query_count > 0 { total_precision / query_count as f64 } else { 0.0 };\n let avg_recall = if query_count > 0 { total_recall / query_count as f64 } else { 0.0 };\n let avg_results_returned = if query_count > 0 { total_results_returned as f64 / query_count as f64 } else { 0.0 };\n let avg_latency = if success_count > 0 { total_latency / success_count as u32 } else { Duration::default() };\n \n let f1_score = if avg_precision + avg_recall > 0.0 {\n 2.0 * avg_precision * avg_recall / (avg_precision + avg_recall)\n } else {\n 0.0\n };\n \n let success_rate = if query_count + error_count > 0 {\n success_count as f64 / (success_count + error_count) as f64\n } else {\n 0.0\n };\n \n info!(\"阈值 {} 评估统计: 成功={}, 错误={}, 成功率={:.1}%, 平均延迟={:?}\",\n similarity_threshold, success_count, error_count, success_rate * 100.0, avg_latency);\n \n let metrics = ThresholdMetrics {\n threshold: similarity_threshold as f64,\n precision: avg_precision,\n recall: avg_recall,\n f1_score,\n avg_results_returned,\n success_rate: Some(success_rate),\n avg_latency_ms: Some(avg_latency.as_millis() as u64),\n };\n \n Ok(ThresholdEvaluationResult {\n metrics,\n query_results,\n })\n }\n \n /// 计算平均精确率(AP)\n fn calculate_average_precision(\n &self,\n search_results: &[ScoredMemory],\n relevant_ids: &HashSet<&str>,\n ) -> f64 {\n if relevant_ids.is_empty() || search_results.is_empty() {\n return 0.0;\n }\n \n let mut sum_precision = 0.0;\n let mut relevant_found = 0;\n \n for (k, result) in search_results.iter().enumerate() {\n if relevant_ids.contains(result.memory.id.as_str()) {\n relevant_found += 1;\n let precision_at_k = relevant_found as f64 / (k + 1) as f64;\n sum_precision += precision_at_k;\n }\n }\n \n if relevant_found > 0 {\n sum_precision / relevant_found as f64\n } else {\n 0.0\n }\n }\n \n /// 计算精确率和召回率@K\n fn calculate_precision_recall_at_k(\n &self,\n query_results: &[QueryResult],\n k: usize,\n ) -> (f64, f64) {\n let mut total_precision_at_k = 0.0;\n let mut total_recall_at_k = 0.0;\n let mut count = 0;\n \n for result in query_results {\n // 这里需要实际的检索结果排序来计算P@K和R@K\n // 由于QueryResult目前不包含排序结果,我们使用近似值\n // 在实际完整实现中,需要修改QueryResult以包含完整的检索结果列表\n \n let precision_at_k = if result.retrieved_total >= k {\n // 假设前k个结果中的相关比例与整体相同\n result.precision\n } else if result.retrieved_total > 0 {\n // 如果返回结果少于k,使用实际比例\n result.precision * result.retrieved_total as f64 / k as f64\n } else {\n 0.0\n };\n \n let recall_at_k = if result.relevant_memories > 0 {\n // 假设前k个结果召回的比例与整体相同\n result.recall.min(1.0)\n } else {\n 0.0\n };\n \n total_precision_at_k += precision_at_k;\n total_recall_at_k += recall_at_k;\n count += 1;\n }\n \n let avg_precision = if count > 0 { total_precision_at_k / count as f64 } else { 0.0 };\n let avg_recall = if count > 0 { total_recall_at_k / count as f64 } else { 0.0 };\n \n (avg_precision, avg_recall)\n }\n \n /// 计算平均精确率均值(MAP)\n fn calculate_mean_average_precision(&self, query_results: &[QueryResult]) -> f64 {\n let mut total_ap = 0.0;\n let mut count = 0;\n \n for result in query_results {\n total_ap += result.average_precision;\n count += 1;\n }\n \n if count > 0 {\n total_ap / count as f64\n } else {\n 0.0\n }\n }\n \n /// 计算归一化折损累计增益(NDCG)\n fn calculate_ndcg(&self, query_results: &[QueryResult], k: usize) -> f64 {\n let mut total_ndcg = 0.0;\n let mut count = 0;\n \n for result in query_results {\n // 简化计算:使用平均精确率作为相关性分数\n let dcg = result.average_precision;\n \n // 理想DCG:所有相关结果都在前面,得分为1\n let ideal_dcg = if result.relevant_memories > 0 { 1.0 } else { 0.0 };\n \n let ndcg = if ideal_dcg > 0.0 {\n dcg / ideal_dcg\n } else {\n 0.0\n };\n \n total_ndcg += ndcg;\n count += 1;\n }\n \n if count > 0 {\n total_ndcg / count as f64\n } else {\n 0.0\n }\n }\n \n /// 清理测试记忆库\n pub async fn cleanup_test_memories(&self) -> Result<()> {\n info!(\"清理测试记忆库...\");\n \n // 这里需要实现清理逻辑\n // 实际实现应该删除测试期间添加的记忆\n \n info!(\"测试记忆库清理完成\");\n Ok(())\n }\n}\n\n/// 扩展ThresholdMetrics以包含更多指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EnhancedThresholdMetrics {\n /// 相似度阈值\n pub threshold: f64,\n /// 精确率\n pub precision: f64,\n /// 召回率\n pub recall: f64,\n /// F1分数\n pub f1_score: f64,\n /// 平均返回结果数\n pub avg_results_returned: f64,\n /// 查询成功率\n pub success_rate: Option,\n /// 平均延迟(毫秒)\n pub avg_latency_ms: Option,\n /// 查询总数\n pub total_queries: usize,\n /// 成功查询数\n pub successful_queries: usize,\n /// 错误查询数\n pub error_queries: usize,\n}\n\nimpl From for EnhancedThresholdMetrics {\n fn from(metrics: ThresholdMetrics) -> Self {\n Self {\n threshold: metrics.threshold,\n precision: metrics.precision,\n recall: metrics.recall,\n f1_score: metrics.f1_score,\n avg_results_returned: metrics.avg_results_returned,\n success_rate: metrics.success_rate,\n avg_latency_ms: metrics.avg_latency_ms,\n total_queries: 0,\n successful_queries: 0,\n error_queries: 0,\n }\n }\n}\n\n// 不再从recall_evaluator重新导出,使用dataset::types中的定义" - }, - "complexity_metrics": { - "cyclomatic_complexity": 51.0, - "lines_of_code": 624, - "number_of_classes": 4, - "number_of_functions": 12 - }, - "dependencies": [ - { - "dependency_type": "crate", - "is_external": true, - "line_number": null, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "crate", - "is_external": true, - "line_number": null, - "name": "cortex_mem_core", - "path": null, - "version": null - }, - { - "dependency_type": "crate", - "is_external": true, - "line_number": null, - "name": "serde", - "path": null, - "version": null - }, - { - "dependency_type": "std", - "is_external": true, - "line_number": null, - "name": "std", - "path": null, - "version": null - }, - { - "dependency_type": "crate", - "is_external": true, - "line_number": null, - "name": "tracing", - "path": null, - "version": null - }, - { - "dependency_type": "crate", - "is_external": true, - "line_number": null, - "name": "tokio", - "path": null, - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": null, - "name": "super::metrics", - "path": "examples/cortex-mem-evaluation/src/metrics", - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": null, - "name": "crate::dataset::types", - "path": "examples/cortex-mem-evaluation/src/dataset/types", - "version": null - } - ], - "detailed_description": "该组件是真实召回率评估器,基于真实cortex-mem-core API调用进行召回率评估。它通过加载测试数据集,准备记忆库,执行真实搜索查询,并计算各种评估指标(如精确率、召回率、F1分数、MAP、NDCG等)来评估记忆管理器的性能。组件支持多阈值评估、并行评估和记忆库完整性验证,能够提供详细的查询级别结果和统计指标。评估过程包括验证记忆库完整性、准备测试记忆库、执行带阈值的搜索评估、计算各种性能指标,并提供清理测试数据的功能。", - "interfaces": [ - { - "description": "真实召回率评估器配置", - "interface_type": "struct", - "name": "RealRecallEvaluationConfig", - "parameters": [ - { - "description": "K值列表", - "is_optional": false, - "name": "k_values", - "param_type": "Vec" - }, - { - "description": "相似度阈值列表", - "is_optional": false, - "name": "similarity_thresholds", - "param_type": "Vec" - }, - { - "description": "每个查询的最大返回结果数", - "is_optional": false, - "name": "max_results_per_query", - "param_type": "usize" - }, - { - "description": "是否保存详细结果", - "is_optional": false, - "name": "save_detailed_results", - "param_type": "bool" - }, - { - "description": "超时时间(秒)", - "is_optional": false, - "name": "timeout_seconds", - "param_type": "u64" - }, - { - "description": "是否启用并行评估", - "is_optional": false, - "name": "enable_parallel_evaluation", - "param_type": "bool" - }, - { - "description": "是否验证记忆库完整性", - "is_optional": false, - "name": "verify_memory_integrity", - "param_type": "bool" - }, - { - "description": "测试数据集路径", - "is_optional": false, - "name": "test_cases_path", - "param_type": "String" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "真实召回率评估器", - "interface_type": "struct", - "name": "RealRecallEvaluator", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "创建新的真实召回率评估器", - "interface_type": "method", - "name": "new", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "config", - "param_type": "RealRecallEvaluationConfig" - }, - { - "description": null, - "is_optional": false, - "name": "memory_manager", - "param_type": "std::sync::Arc" - } - ], - "return_type": "Self", - "visibility": "public" - }, - { - "description": "评估记忆管理器的召回率", - "interface_type": "method", - "name": "evaluate", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "dataset", - "param_type": "&RecallTestDataset" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "验证记忆库完整性", - "interface_type": "method", - "name": "verify_memory_integrity", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "memories", - "param_type": "&HashMap" - } - ], - "return_type": "Result<()>", - "visibility": "private" - }, - { - "description": "准备记忆库(将测试记忆添加到记忆管理器)", - "interface_type": "method", - "name": "prepare_memory_library", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "memories", - "param_type": "&HashMap" - } - ], - "return_type": "Result<()>", - "visibility": "private" - }, - { - "description": "使用特定阈值评估", - "interface_type": "method", - "name": "evaluate_with_threshold", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "dataset", - "param_type": "&RecallTestDataset" - }, - { - "description": null, - "is_optional": false, - "name": "similarity_threshold", - "param_type": "f32" - } - ], - "return_type": "Result", - "visibility": "private" - }, - { - "description": "计算精确率和召回率@K", - "interface_type": "method", - "name": "calculate_precision_recall_at_k", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query_results", - "param_type": "&[QueryResult]" - }, - { - "description": null, - "is_optional": false, - "name": "k", - "param_type": "usize" - } - ], - "return_type": "(f64, f64)", - "visibility": "private" - }, - { - "description": "计算平均精确率均值(MAP)", - "interface_type": "method", - "name": "calculate_mean_average_precision", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query_results", - "param_type": "&[QueryResult]" - } - ], - "return_type": "f64", - "visibility": "private" - }, - { - "description": "计算归一化折损累计增益(NDCG)", - "interface_type": "method", - "name": "calculate_ndcg", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query_results", - "param_type": "&[QueryResult]" - }, - { - "description": null, - "is_optional": false, - "name": "k", - "param_type": "usize" - } - ], - "return_type": "f64", - "visibility": "private" - }, - { - "description": "清理测试记忆库", - "interface_type": "method", - "name": "cleanup_test_memories", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" - }, - { - "description": "阈值评估结果(包含查询结果)", - "interface_type": "struct", - "name": "ThresholdEvaluationResult", - "parameters": [], - "return_type": null, - "visibility": "private" - }, - { - "description": "扩展ThresholdMetrics以包含更多指标", - "interface_type": "struct", - "name": "EnhancedThresholdMetrics", - "parameters": [], - "return_type": null, - "visibility": "public" - } - ], - "responsibilities": [ - "执行真实环境下的召回率评估,通过实际调用cortex-mem-core API验证记忆检索性能", - "管理评估配置,支持K值列表、相似度阈值、超时时间等多种评估参数的灵活配置", - "验证记忆库完整性,确保测试记忆数据的有效性和完整性", - "准备和管理测试记忆库,将测试数据加载到记忆管理器中进行评估", - "计算多种评估指标,包括精确率@K、召回率@K、F1分数、MAP、NDCG等" - ] - }, - { - "code_dossier": { - "code_purpose": "specificfeature", - "description": "召回率评估器,用于评估向量检索系统的召回率和精确率表现。", - "file_path": "examples/cortex-mem-evaluation/src/evaluator/recall_evaluator.rs", - "functions": [ - "new", - "evaluate", - "evaluate_with_threshold", - "calculate_precision_recall_at_k", - "calculate_mean_average_precision", - "calculate_ndcg", - "load_dataset", - "save_results" - ], - "importance_score": 0.8, - "interfaces": [ - "RecallEvaluator", - "RecallEvaluationConfig", - "RecallTestCase", - "RecallTestDataset", - "DatasetMetadata" - ], - "name": "recall_evaluator.rs", - "source_summary": "//! 召回率评估器\n//! \n//! 评估向量检索的召回率和精确率\n\nuse anyhow::{Result, Context};\nuse cortex_mem_core::{MemoryManager, Memory, MemoryType, ScoredMemory};\nuse serde::{Deserialize, Serialize};\nuse std::collections::{HashMap, HashSet};\nuse tracing::{info, debug, warn};\n\nuse super::metrics::{RecallMetrics, ThresholdMetrics, QueryResult};\n\n/// 召回率测试用例\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct RecallTestCase {\n /// 查询ID\n pub query_id: String,\n /// 查询文本\n pub query: String,\n /// 相关记忆ID列表\n pub relevant_memory_ids: Vec,\n /// 查询类别\n pub category: String,\n /// 查询复杂度:simple, medium, complex\n pub complexity: String,\n}\n\n/// 召回率测试数据集\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct RecallTestDataset {\n /// 测试用例列表\n pub test_cases: Vec,\n /// 记忆库(ID -> 记忆内容)\n pub memories: HashMap,\n /// 数据集元数据\n pub metadata: crate::dataset::types::DatasetMetadata,\n}\n\n/// 数据集元数据\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct DatasetMetadata {\n /// 数据集名称\n pub name: String,\n /// 创建时间\n pub created_at: String,\n /// 版本\n pub version: String,\n /// 总测试用例数\n pub total_test_cases: usize,\n /// 总记忆数\n pub total_memories: usize,\n /// 平均相关记忆数\n pub avg_relevant_memories: f64,\n}\n\n/// 召回率评估器\npub struct RecallEvaluator {\n /// 评估配置\n config: RecallEvaluationConfig,\n}\n\n/// 召回率评估配置\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct RecallEvaluationConfig {\n /// K值列表\n pub k_values: Vec,\n /// 相似度阈值列表\n pub similarity_thresholds: Vec,\n /// 每个查询的最大返回结果数\n pub max_results_per_query: usize,\n /// 是否保存详细结果\n pub save_detailed_results: bool,\n /// 是否使用真实评估器\n pub use_real_evaluator: bool,\n /// 测试用例路径\n pub test_cases_path: String,\n}\n\nimpl Default for RecallEvaluationConfig {\n fn default() -> Self {\n Self {\n k_values: vec![1, 3, 5, 10],\n similarity_thresholds: vec![0.7, 0.8, 0.9],\n max_results_per_query: 20,\n save_detailed_results: true,\n use_real_evaluator: false,\n test_cases_path: \"data/test_cases/recall_test_cases.json\".to_string(),\n }\n }\n}\n\nimpl RecallEvaluator {\n /// 创建新的召回率评估器\n pub fn new(config: RecallEvaluationConfig) -> Self {\n Self { config }\n }\n \n /// 评估记忆管理器的召回率\n pub async fn evaluate(\n &self,\n memory_manager: &MemoryManager,\n dataset: &RecallTestDataset,\n ) -> Result {\n info!(\"开始召回率评估,共{}个测试用例\", dataset.test_cases.len());\n \n let mut query_results = Vec::new();\n let mut metrics_by_threshold = HashMap::new();\n \n // 为每个相似度阈值评估\n for &threshold in &self.config.similarity_thresholds {\n info!(\"评估相似度阈值: {}\", threshold);\n \n let threshold_metrics = self.evaluate_with_threshold(\n memory_manager,\n dataset,\n threshold,\n ).await?;\n \n metrics_by_threshold.insert(threshold.to_string(), threshold_metrics);\n }\n \n // 计算总体指标(使用默认阈值0.8)\n let default_threshold = 0.8;\n let mut precision_at_k = HashMap::new();\n let mut recall_at_k = HashMap::new();\n \n for &k in &self.config.k_values {\n let (precision, recall) = self.calculate_precision_recall_at_k(\n &query_results,\n k,\n );\n precision_at_k.insert(k, precision);\n recall_at_k.insert(k, recall);\n }\n \n // 计算MAP和NDCG\n let mean_average_precision = self.calculate_mean_average_precision(&query_results);\n let normalized_discounted_cumulative_gain = \n self.calculate_ndcg(&query_results, self.config.k_values.last().copied().unwrap_or(10));\n \n let metrics = RecallMetrics {\n precision_at_k,\n recall_at_k,\n mean_average_precision,\n normalized_discounted_cumulative_gain,\n metrics_by_threshold,\n query_level_results: if self.config.save_detailed_results {\n query_results\n } else {\n Vec::new()\n },\n };\n \n info!(\"召回率评估完成\");\n Ok(metrics)\n }\n \n /// 使用特定阈值评估\n async fn evaluate_with_threshold(\n &self,\n memory_manager: &MemoryManager,\n dataset: &RecallTestDataset,\n similarity_threshold: f64,\n ) -> Result {\n let mut total_precision = 0.0;\n let mut total_recall = 0.0;\n let mut total_results_returned = 0;\n let mut query_count = 0;\n \n for test_case in &dataset.test_cases {\n // 执行搜索\n let search_results = memory_manager.search(\n &test_case.query,\n &cortex_mem_core::types::Filters::default(),\n self.config.max_results_per_query,\n ).await\n .context(\"搜索记忆失败\")?;\n \n // 过滤低于阈值的結果\n let filtered_results: Vec<&ScoredMemory> = search_results\n .iter()\n .filter(|m| m.score >= similarity_threshold as f32)\n .collect();\n \n // 计算相关记忆ID集合\n let relevant_ids: HashSet<&str> = test_case.relevant_memory_ids\n .iter()\n .map(|id| id.as_str())\n .collect();\n \n // 计算检索到的相关记忆\n let retrieved_relevant = filtered_results\n .iter()\n .filter(|m| relevant_ids.contains(m.memory.id.as_str()))\n .count();\n \n let retrieved_total = filtered_results.len();\n let relevant_total = relevant_ids.len();\n \n // 计算精确率和召回率\n let precision = if retrieved_total > 0 {\n retrieved_relevant as f64 / retrieved_total as f64\n } else {\n 0.0\n };\n \n let recall = if relevant_total > 0 {\n retrieved_relevant as f64 / relevant_total as f64\n } else {\n 0.0\n };\n \n total_precision += precision;\n total_recall += recall;\n total_results_returned += retrieved_total;\n query_count += 1;\n \n debug!(\n \"查询 {}: 精确率={:.3}, 召回率={:.3}, 返回结果={}\",\n test_case.query_id, precision, recall, retrieved_total\n );\n }\n \n let avg_precision = total_precision / query_count as f64;\n let avg_recall = total_recall / query_count as f64;\n let avg_results_returned = total_results_returned as f64 / query_count as f64;\n let f1_score = if avg_precision + avg_recall > 0.0 {\n 2.0 * avg_precision * avg_recall / (avg_precision + avg_recall)\n } else {\n 0.0\n };\n \n Ok(ThresholdMetrics {\n threshold: similarity_threshold,\n precision: avg_precision,\n recall: avg_recall,\n f1_score,\n avg_results_returned,\n success_rate: None, // 暂时设为None\n avg_latency_ms: None, // 暂时设为None\n })\n }\n \n /// 计算精确率和召回率@K\n fn calculate_precision_recall_at_k(\n &self,\n query_results: &[QueryResult],\n k: usize,\n ) -> (f64, f64) {\n let mut total_precision = 0.0;\n let mut total_recall = 0.0;\n let mut count = 0;\n \n // 注意:这里需要实际的检索结果来计算P@K和R@K\n // 由于我们目前没有保存每个查询的检索结果排序,这里使用近似计算\n // 在实际实现中,需要修改以保存完整的检索结果\n \n for result in query_results {\n // 近似计算:假设前k个结果中的相关比例\n let _max_relevant_at_k = result.relevant_memories.min(k);\n let expected_relevant_at_k = if result.retrieved_total > 0 {\n (result.retrieved_relevant as f64 * k as f64 / \n result.retrieved_total as f64).round() as usize\n } else {\n 0\n };\n \n let precision_at_k = if k > 0 {\n expected_relevant_at_k as f64 / k as f64\n } else {\n 0.0\n };\n \n let recall_at_k = if result.relevant_memories > 0 {\n expected_relevant_at_k as f64 / result.relevant_memories as f64\n } else {\n 0.0\n };\n \n total_precision += precision_at_k;\n total_recall += recall_at_k;\n count += 1;\n }\n \n let avg_precision = if count > 0 { total_precision / count as f64 } else { 0.0 };\n let avg_recall = if count > 0 { total_recall / count as f64 } else { 0.0 };\n \n (avg_precision, avg_recall)\n }\n \n /// 计算平均精确率均值(MAP)\n fn calculate_mean_average_precision(&self, query_results: &[QueryResult]) -> f64 {\n let mut total_ap = 0.0;\n let mut count = 0;\n \n for result in query_results {\n // 使用查询结果中的平均精确率\n total_ap += result.average_precision;\n count += 1;\n }\n \n if count > 0 {\n total_ap / count as f64\n } else {\n 0.0\n }\n }\n \n /// 计算归一化折损累计增益(NDCG)\n fn calculate_ndcg(&self, query_results: &[QueryResult], _k: usize) -> f64 {\n let mut total_ndcg = 0.0;\n let mut count = 0;\n \n for result in query_results {\n // 简化计算:使用精确率作为相关性分数\n let dcg = result.precision; // 简化:使用精确率作为第一个结果的增益\n \n // 理想DCG:所有相关结果都在前面\n let ideal_dcg = if result.relevant_memories > 0 {\n 1.0 // 简化:理想情况下第一个结果就是相关的\n } else {\n 0.0\n };\n \n let ndcg = if ideal_dcg > 0.0 {\n dcg / ideal_dcg\n } else {\n 0.0\n };\n \n total_ndcg += ndcg;\n count += 1;\n }\n \n if count > 0 {\n total_ndcg / count as f64\n } else {\n 0.0\n }\n }\n \n /// 加载测试数据集\n pub fn load_dataset(path: &str) -> Result {\n let content = std::fs::read_to_string(path)\n .context(format!(\"读取数据集文件失败: {}\", path))?;\n \n let dataset: RecallTestDataset = serde_json::from_str(&content)\n .context(\"解析数据集JSON失败\")?;\n \n info!(\"加载召回率测试数据集: {}个测试用例, {}个记忆\",\n dataset.test_cases.len(), dataset.memories.len());\n \n Ok(dataset)\n }\n \n /// 保存评估结果\n pub fn save_results(&self, metrics: &RecallMetrics, output_path: &str) -> Result<()> {\n let json = serde_json::to_string_pretty(metrics)\n .context(\"序列化评估结果失败\")?;\n \n std::fs::write(output_path, json)\n .context(format!(\"写入评估结果文件失败: {}\", output_path))?;\n \n info!(\"评估结果已保存到: {}\", output_path);\n Ok(())\n }\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 22.0, - "lines_of_code": 367, - "number_of_classes": 5, - "number_of_functions": 8 - }, - "dependencies": [ - { - "dependency_type": "error_handling", - "is_external": true, - "line_number": 1, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "core_library", - "is_external": false, - "line_number": 2, - "name": "cortex_mem_core", - "path": null, - "version": null - }, - { - "dependency_type": "serialization", - "is_external": true, - "line_number": 3, - "name": "serde", - "path": null, - "version": null - }, - { - "dependency_type": "standard_library", - "is_external": false, - "line_number": 4, - "name": "std::collections", - "path": null, - "version": null - }, - { - "dependency_type": "logging", - "is_external": true, - "line_number": 5, - "name": "tracing", - "path": null, - "version": null - }, - { - "dependency_type": "internal_module", - "is_external": false, - "line_number": 7, - "name": "super::metrics", - "path": "examples/cortex-mem-evaluation/src/evaluator/metrics.rs", - "version": null - } - ], - "detailed_description": "该组件是一个用于评估向量检索系统性能的召回率评估器,主要功能包括:1) 支持基于相似度阈值和K值的多维度评估;2) 提供精确率(Precision)、召回率(Recall)、F1分数、MAP和NDCG等核心指标计算;3) 支持加载测试数据集和保存评估结果;4) 通过配置化方式支持灵活的评估参数设置。组件采用异步编程模型,与MemoryManager交互执行搜索操作,并基于测试用例进行量化评估。", - "interfaces": [ - { - "description": "召回率评估器主结构体", - "interface_type": "struct", - "name": "RecallEvaluator", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "评估配置参数", - "interface_type": "struct", - "name": "RecallEvaluationConfig", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "单个召回率测试用例", - "interface_type": "struct", - "name": "RecallTestCase", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "召回率测试数据集", - "interface_type": "struct", - "name": "RecallTestDataset", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "数据集元数据", - "interface_type": "struct", - "name": "DatasetMetadata", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "创建新的召回率评估器实例", - "interface_type": "function", - "name": "new", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "config", - "param_type": "RecallEvaluationConfig" - } - ], - "return_type": "RecallEvaluator", - "visibility": "public" - }, - { - "description": "执行完整的召回率评估流程", - "interface_type": "function", - "name": "evaluate", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "memory_manager", - "param_type": "&MemoryManager" - }, - { - "description": null, - "is_optional": false, - "name": "dataset", - "param_type": "&RecallTestDataset" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "使用特定相似度阈值进行评估", - "interface_type": "function", - "name": "evaluate_with_threshold", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "memory_manager", - "param_type": "&MemoryManager" - }, - { - "description": null, - "is_optional": false, - "name": "dataset", - "param_type": "&RecallTestDataset" - }, - { - "description": null, - "is_optional": false, - "name": "similarity_threshold", - "param_type": "f64" - } - ], - "return_type": "Result", - "visibility": "private" - }, - { - "description": "计算Precision@K和Recall@K指标", - "interface_type": "function", - "name": "calculate_precision_recall_at_k", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query_results", - "param_type": "&[QueryResult]" - }, - { - "description": null, - "is_optional": false, - "name": "k", - "param_type": "usize" - } - ], - "return_type": "(f64, f64)", - "visibility": "private" - }, - { - "description": "计算平均精确率均值(MAP)", - "interface_type": "function", - "name": "calculate_mean_average_precision", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query_results", - "param_type": "&[QueryResult]" - } - ], - "return_type": "f64", - "visibility": "private" - }, - { - "description": "计算归一化折损累计增益(NDCG)", - "interface_type": "function", - "name": "calculate_ndcg", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query_results", - "param_type": "&[QueryResult]" - }, - { - "description": null, - "is_optional": false, - "name": "_k", - "param_type": "usize" - } - ], - "return_type": "f64", - "visibility": "private" - }, - { - "description": "从文件加载测试数据集", - "interface_type": "function", - "name": "load_dataset", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "path", - "param_type": "&str" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "保存评估结果到文件", - "interface_type": "function", - "name": "save_results", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "metrics", - "param_type": "&RecallMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "output_path", - "param_type": "&str" - } - ], - "return_type": "Result<()>", - "visibility": "public" - } - ], - "responsibilities": [ - "评估向量检索系统的召回率和精确率性能", - "计算多种信息检索评估指标(Precision, Recall, F1, MAP, NDCG)", - "管理评估配置参数并支持灵活的评估策略", - "加载测试数据集并保存评估结果到文件", - "提供基于不同相似度阈值和K值的多维度性能分析" - ] - }, - { - "code_dossier": { - "code_purpose": "model", - "description": "Defines comprehensive evaluation metrics for a memory evaluation system, covering recall, effectiveness, performance, and overall assessment.", - "file_path": "examples/cortex-mem-evaluation/src/evaluator/metrics.rs", - "functions": [], - "importance_score": 0.8, - "interfaces": [ - "RecallMetrics", - "ThresholdMetrics", - "QueryResult", - "EffectivenessMetrics", - "FactExtractionMetrics", - "FactExtractionResult", - "ClassificationMetrics", - "ImportanceMetrics", - "DeduplicationMetrics", - "UpdateMetrics", - "PerformanceMetrics", - "LatencyMetrics", - "ThroughputMetrics", - "ResourceMetrics", - "ResourceSnapshot", - "ConcurrencyMetrics", - "ScalabilityMetrics", - "SizePerformance", - "ComprehensiveReport" - ], - "name": "metrics.rs", - "source_summary": "//! 评估指标定义\n\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\n\n/// 召回率评估指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct RecallMetrics {\n /// 精确率@K\n pub precision_at_k: HashMap,\n /// 召回率@K\n pub recall_at_k: HashMap,\n /// 平均精确率均值\n pub mean_average_precision: f64,\n /// 归一化折损累计增益\n pub normalized_discounted_cumulative_gain: f64,\n /// 不同相似度阈值下的指标\n pub metrics_by_threshold: HashMap, // 使用字符串作为键\n /// 查询级别的详细结果\n pub query_level_results: Vec,\n}\n\n/// 相似度阈值相关的指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ThresholdMetrics {\n /// 阈值\n pub threshold: f64,\n /// 精确率\n pub precision: f64,\n /// 召回率\n pub recall: f64,\n /// F1分数\n pub f1_score: f64,\n /// 返回结果的平均数量\n pub avg_results_returned: f64,\n /// 查询成功率(可选)\n pub success_rate: Option,\n /// 平均延迟(毫秒,可选)\n pub avg_latency_ms: Option,\n}\n\n/// 查询级别的结果\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct QueryResult {\n /// 查询ID\n pub query_id: String,\n /// 查询内容\n pub query: String,\n /// 相关记忆数量\n pub relevant_memories: usize,\n /// 检索到的相关记忆数量\n pub retrieved_relevant: usize,\n /// 检索到的总记忆数量\n pub retrieved_total: usize,\n /// 精确率\n pub precision: f64,\n /// 召回率\n pub recall: f64,\n /// 平均精确率\n pub average_precision: f64,\n /// 查询延迟(毫秒)\n pub latency_ms: u64,\n}\n\n/// 记忆有效性评估指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EffectivenessMetrics {\n /// 事实提取准确性\n pub fact_extraction_accuracy: FactExtractionMetrics,\n /// 记忆分类准确性\n pub classification_accuracy: ClassificationMetrics,\n /// 重要性评估合理性\n pub importance_evaluation_quality: ImportanceMetrics,\n /// 去重效果\n pub deduplication_effectiveness: DeduplicationMetrics,\n /// 记忆更新正确性\n pub memory_update_correctness: UpdateMetrics,\n /// 综合得分\n pub overall_score: f64,\n}\n\n/// 事实提取指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct FactExtractionMetrics {\n /// 精确率\n pub precision: f64,\n /// 召回率\n pub recall: f64,\n /// F1分数\n pub f1_score: f64,\n /// 提取的关键事实数量\n pub facts_extracted: usize,\n /// 正确提取的事实数量\n pub correct_facts: usize,\n /// 详细结果\n pub detailed_results: Vec,\n}\n\n/// 事实提取结果\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct FactExtractionResult {\n /// 输入文本\n pub input_text: String,\n /// 提取的事实\n pub extracted_facts: Vec,\n /// 基准事实\n pub ground_truth_facts: Vec,\n /// 匹配的事实数量\n pub matched_facts: usize,\n /// 是否完全匹配\n pub is_perfect_match: bool,\n}\n\n/// 分类指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ClassificationMetrics {\n /// 准确率\n pub accuracy: f64,\n /// 精确率(按类别)\n pub precision_by_class: HashMap,\n /// 召回率(按类别)\n pub recall_by_class: HashMap,\n /// F1分数(按类别)\n pub f1_by_class: HashMap,\n /// 混淆矩阵\n pub confusion_matrix: HashMap>,\n}\n\n/// 重要性评估指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ImportanceMetrics {\n /// 相关性分数(与人工标注的相关性)\n pub correlation_score: f64,\n /// 平均绝对误差\n pub mean_absolute_error: f64,\n /// 均方根误差\n pub root_mean_squared_error: f64,\n /// 评分分布\n pub score_distribution: HashMap,\n /// 在容差范围内的比例\n pub within_tolerance_rate: f64,\n}\n\n/// 去重指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct DeduplicationMetrics {\n /// 重复检测精确率\n pub duplicate_detection_precision: f64,\n /// 重复检测召回率\n pub duplicate_detection_recall: f64,\n /// 合并正确率\n pub merge_accuracy: f64,\n /// 检测到的重复对数量\n pub duplicate_pairs_detected: usize,\n /// 实际重复对数量\n pub actual_duplicate_pairs: usize,\n /// 平均合并质量\n pub avg_merge_quality: f64,\n}\n\n/// 记忆更新指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct UpdateMetrics {\n /// 更新操作正确率\n pub update_operation_accuracy: f64,\n /// 合并操作正确率\n pub merge_operation_accuracy: f64,\n /// 冲突解决正确率\n pub conflict_resolution_accuracy: f64,\n /// 更新后的记忆质量评分\n pub updated_memory_quality: f64,\n}\n\n/// 性能评估指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct PerformanceMetrics {\n /// 延迟指标\n pub latency: LatencyMetrics,\n /// 吞吐量指标\n pub throughput: ThroughputMetrics,\n /// 资源使用指标\n pub resource_usage: ResourceMetrics,\n /// 并发性能指标\n pub concurrency: ConcurrencyMetrics,\n /// 可扩展性指标\n pub scalability: ScalabilityMetrics,\n}\n\n/// 延迟指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LatencyMetrics {\n /// 添加记忆的平均延迟(毫秒)\n pub add_memory_avg_ms: f64,\n /// 搜索记忆的平均延迟(毫秒)\n pub search_memory_avg_ms: f64,\n /// 更新记忆的平均延迟(毫秒)\n pub update_memory_avg_ms: f64,\n /// 删除记忆的平均延迟(毫秒)\n pub delete_memory_avg_ms: f64,\n /// P95延迟(毫秒)\n pub p95_latency_ms: f64,\n /// P99延迟(毫秒)\n pub p99_latency_ms: f64,\n /// 最大延迟(毫秒)\n pub max_latency_ms: f64,\n /// 延迟分布\n pub latency_distribution: HashMap,\n}\n\n/// 吞吐量指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ThroughputMetrics {\n /// 每秒操作数\n pub operations_per_second: f64,\n /// 每秒添加操作数\n pub add_ops_per_second: f64,\n /// 每秒搜索操作数\n pub search_ops_per_second: f64,\n /// 每秒更新操作数\n pub update_ops_per_second: f64,\n /// 峰值吞吐量\n pub peak_throughput: f64,\n /// 可持续吞吐量\n pub sustainable_throughput: f64,\n}\n\n/// 资源使用指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ResourceMetrics {\n /// 内存使用(MB)\n pub memory_usage_mb: f64,\n /// CPU使用率(%)\n pub cpu_usage_percent: f64,\n /// 磁盘使用(MB)\n pub disk_usage_mb: f64,\n /// 网络使用(KB/s)\n pub network_usage_kbps: f64,\n /// 资源使用趋势\n pub usage_trend: Vec,\n}\n\n/// 资源快照\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ResourceSnapshot {\n /// 时间戳\n pub timestamp: i64,\n /// 内存使用(MB)\n pub memory_mb: f64,\n /// CPU使用率(%)\n pub cpu_percent: f64,\n}\n\n/// 并发性能指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ConcurrencyMetrics {\n /// 并发用户数\n pub concurrent_users: usize,\n /// 平均响应时间(毫秒)\n pub avg_response_time_ms: f64,\n /// 吞吐量(操作/秒)\n pub throughput_ops_per_sec: f64,\n /// 错误率(%)\n pub error_rate_percent: f64,\n /// 成功率(%)\n pub success_rate_percent: f64,\n}\n\n/// 可扩展性指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ScalabilityMetrics {\n /// 不同记忆库大小下的性能\n pub performance_by_memory_size: HashMap,\n /// 线性扩展因子\n pub linear_scaling_factor: f64,\n /// 瓶颈点\n pub bottleneck_point: Option,\n}\n\n/// 大小性能指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct SizePerformance {\n /// 记忆库大小\n pub memory_size: usize,\n /// 平均延迟(毫秒)\n pub avg_latency_ms: f64,\n /// 吞吐量(操作/秒)\n pub throughput_ops_per_sec: f64,\n /// 内存使用(MB)\n pub memory_usage_mb: f64,\n}\n\n/// 综合评估报告\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ComprehensiveReport {\n /// 评估时间戳\n pub evaluation_timestamp: i64,\n /// 评估配置摘要\n pub config_summary: String,\n /// 召回率评估结果\n pub recall_metrics: Option,\n /// 有效性评估结果\n pub effectiveness_metrics: Option,\n /// 性能评估结果\n pub performance_metrics: Option,\n /// 总体评分\n pub overall_score: f64,\n /// 关键发现\n pub key_findings: Vec,\n /// 改进建议\n pub recommendations: Vec,\n /// 评估版本\n pub evaluation_version: String,\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 313, - "number_of_classes": 19, - "number_of_functions": 0 - }, - "dependencies": [ - { - "dependency_type": "library", - "is_external": true, - "line_number": 3, - "name": "serde", - "path": null, - "version": null - }, - { - "dependency_type": "std", - "is_external": false, - "line_number": 4, - "name": "std::collections::HashMap", - "path": null, - "version": null - } - ], - "detailed_description": "This component defines a comprehensive set of data structures for evaluating a memory system's performance, effectiveness, and quality. It includes metrics for recall (precision, recall, MAP, NDCG), effectiveness (fact extraction, classification, importance evaluation, deduplication, and memory updates), and performance (latency, throughput, resource usage, concurrency, scalability). The structures are designed to capture evaluation results at various levels, from high-level summary scores to detailed per-query or per-operation metrics. All structures implement Debug, Clone, Serialize, and Deserialize traits, enabling logging, duplication, and persistence of evaluation results.", - "interfaces": [ - { - "description": "Metrics for evaluating retrieval recall performance", - "interface_type": "struct", - "name": "RecallMetrics", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "precision_at_k", - "param_type": "HashMap" - }, - { - "description": null, - "is_optional": false, - "name": "recall_at_k", - "param_type": "HashMap" - }, - { - "description": null, - "is_optional": false, - "name": "mean_average_precision", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "normalized_discounted_cumulative_gain", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "metrics_by_threshold", - "param_type": "HashMap" - }, - { - "description": null, - "is_optional": false, - "name": "query_level_results", - "param_type": "Vec" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "Metrics at specific similarity thresholds", - "interface_type": "struct", - "name": "ThresholdMetrics", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "threshold", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "precision", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "recall", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "f1_score", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "avg_results_returned", - "param_type": "f64" - }, - { - "description": null, - "is_optional": true, - "name": "success_rate", - "param_type": "Option" - }, - { - "description": null, - "is_optional": true, - "name": "avg_latency_ms", - "param_type": "Option" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "Detailed results for individual queries", - "interface_type": "struct", - "name": "QueryResult", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query_id", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "query", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "relevant_memories", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "retrieved_relevant", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "retrieved_total", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "precision", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "recall", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "average_precision", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "latency_ms", - "param_type": "u64" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "Metrics for evaluating memory content quality and processing effectiveness", - "interface_type": "struct", - "name": "EffectivenessMetrics", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "fact_extraction_accuracy", - "param_type": "FactExtractionMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "classification_accuracy", - "param_type": "ClassificationMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "importance_evaluation_quality", - "param_type": "ImportanceMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "deduplication_effectiveness", - "param_type": "DeduplicationMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "memory_update_correctness", - "param_type": "UpdateMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "overall_score", - "param_type": "f64" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "Metrics for evaluating system performance characteristics", - "interface_type": "struct", - "name": "PerformanceMetrics", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "latency", - "param_type": "LatencyMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "throughput", - "param_type": "ThroughputMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "resource_usage", - "param_type": "ResourceMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "concurrency", - "param_type": "ConcurrencyMetrics" - }, - { - "description": null, - "is_optional": false, - "name": "scalability", - "param_type": "ScalabilityMetrics" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "Top-level structure containing complete evaluation results", - "interface_type": "struct", - "name": "ComprehensiveReport", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "evaluation_timestamp", - "param_type": "i64" - }, - { - "description": null, - "is_optional": false, - "name": "config_summary", - "param_type": "String" - }, - { - "description": null, - "is_optional": true, - "name": "recall_metrics", - "param_type": "Option" - }, - { - "description": null, - "is_optional": true, - "name": "effectiveness_metrics", - "param_type": "Option" - }, - { - "description": null, - "is_optional": true, - "name": "performance_metrics", - "param_type": "Option" - }, - { - "description": null, - "is_optional": false, - "name": "overall_score", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "key_findings", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": false, - "name": "recommendations", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": false, - "name": "evaluation_version", - "param_type": "String" - } - ], - "return_type": null, - "visibility": "pub" - } - ], - "responsibilities": [ - "Define data structures for comprehensive memory system evaluation metrics", - "Provide types for storing recall, effectiveness, and performance assessment results", - "Enable serialization and deserialization of evaluation data for persistence and transmission", - "Support detailed, hierarchical reporting of evaluation outcomes with both aggregate and granular metrics", - "Facilitate comparison and analysis of system performance across different configurations and memory sizes" - ] - }, - { - "code_dossier": { - "code_purpose": "specificfeature", - "description": null, - "file_path": "examples/cortex-mem-evaluation/src/evaluator/real_effectiveness_evaluator.rs", - "functions": [ - "new", - "evaluate", - "add_existing_memories", - "evaluate_fact_extraction", - "evaluate_classification", - "evaluate_importance", - "evaluate_deduplication", - "evaluate_memory_update", - "calculate_fact_extraction_metrics", - "calculate_classification_metrics", - "calculate_importance_metrics", - "calculate_deduplication_metrics", - "calculate_update_metrics", - "calculate_overall_score", - "cleanup_test_data" - ], - "importance_score": 0.8, - "interfaces": [ - "RealEffectivenessEvaluationConfig", - "RealEffectivenessEvaluator", - "FactExtractionResult", - "ClassificationResult", - "ImportanceResult", - "DeduplicationResult", - "UpdateResult" - ], - "name": "real_effectiveness_evaluator.rs", - "source_summary": "//! 真实有效性评估器\n//! \n//! 基于真实cortex-mem-core API调用的记忆有效性评估\n\nuse anyhow::{Result, Context};\nuse cortex_mem_core::{MemoryManager, Memory, MemoryType, types::{Message, Filters}};\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\nuse std::time::{Duration, Instant};\nuse tracing::{info, debug, warn, error};\n\nuse super::metrics::{\n EffectivenessMetrics, FactExtractionMetrics, ClassificationMetrics,\n ImportanceMetrics, DeduplicationMetrics, UpdateMetrics,\n FactExtractionResult,\n};\n\nuse crate::dataset::types::{EffectivenessTestDataset, EffectivenessTestCase};\n\n/// 真实有效性评估器配置\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct RealEffectivenessEvaluationConfig {\n /// 是否验证事实提取\n pub verify_fact_extraction: bool,\n /// 是否验证记忆分类\n pub verify_classification: bool,\n /// 是否验证重要性评估\n pub verify_importance_evaluation: bool,\n /// 是否验证去重效果\n pub verify_deduplication: bool,\n /// 是否验证记忆更新逻辑\n pub verify_memory_update: bool,\n /// 重要性评分容差\n pub importance_score_tolerance: u8,\n /// 超时时间(秒)\n pub timeout_seconds: u64,\n /// 是否启用详细日志\n pub enable_verbose_logging: bool,\n /// 是否清理测试数据\n pub cleanup_test_data: bool,\n /// 测试数据集路径\n pub test_cases_path: String,\n}\n\nimpl Default for RealEffectivenessEvaluationConfig {\n fn default() -> Self {\n Self {\n verify_fact_extraction: true,\n verify_classification: true,\n verify_importance_evaluation: true,\n verify_deduplication: true,\n verify_memory_update: true,\n importance_score_tolerance: 1,\n timeout_seconds: 30,\n enable_verbose_logging: false,\n cleanup_test_data: true,\n test_cases_path: \"data/test_cases/lab_effectiveness_dataset.json\".to_string(),\n }\n }\n}\n\n/// 真实有效性评估器\npub struct RealEffectivenessEvaluator {\n /// 评估配置\n config: RealEffectivenessEvaluationConfig,\n /// 记忆管理器\n memory_manager: std::sync::Arc,\n}\n\nimpl RealEffectivenessEvaluator {\n /// 创建新的真实有效性评估器\n pub fn new(\n config: RealEffectivenessEvaluationConfig,\n memory_manager: std::sync::Arc,\n ) -> Self {\n Self {\n config,\n memory_manager,\n }\n }\n \n /// 评估记忆管理器的有效性\n pub async fn evaluate(\n &self,\n dataset: &EffectivenessTestDataset,\n ) -> Result {\n info!(\"开始真实有效性评估,共{}个测试用例\", dataset.test_cases.len());\n \n let mut fact_extraction_results = Vec::new();\n let mut classification_results = Vec::new();\n let mut importance_results = Vec::new();\n let mut deduplication_results = Vec::new();\n let mut update_results = Vec::new();\n \n // 首先添加所有现有记忆\n if !dataset.existing_memories.is_empty() {\n info!(\"添加{}个现有记忆到记忆库\", dataset.existing_memories.len());\n self.add_existing_memories(&dataset.existing_memories).await?;\n }\n \n // 评估每个测试用例\n for (i, test_case) in dataset.test_cases.iter().enumerate() {\n if self.config.enable_verbose_logging {\n debug!(\"评估测试用例 {}: {}\", i, test_case.test_case_id);\n }\n \n // 事实提取评估\n if self.config.verify_fact_extraction {\n match self.evaluate_fact_extraction(test_case).await {\n Ok(result) => fact_extraction_results.push(result),\n Err(e) => warn!(\"事实提取评估失败 {}: {}\", test_case.test_case_id, e),\n }\n }\n \n // 记忆分类评估\n if self.config.verify_classification {\n match self.evaluate_classification(test_case).await {\n Ok(result) => classification_results.push(result),\n Err(e) => warn!(\"分类评估失败 {}: {}\", test_case.test_case_id, e),\n }\n }\n \n // 重要性评估\n if self.config.verify_importance_evaluation {\n match self.evaluate_importance(test_case).await {\n Ok(result) => importance_results.push(result),\n Err(e) => warn!(\"重要性评估失败 {}: {}\", test_case.test_case_id, e),\n }\n }\n \n // 去重评估(如果包含重复内容)\n if self.config.verify_deduplication && test_case.contains_duplicate {\n match self.evaluate_deduplication(test_case).await {\n Ok(result) => deduplication_results.push(result),\n Err(e) => warn!(\"去重评估失败 {}: {}\", test_case.test_case_id, e),\n }\n }\n \n // 记忆更新评估\n if self.config.verify_memory_update && test_case.requires_update {\n match self.evaluate_memory_update(test_case, &dataset.existing_memories).await {\n Ok(result) => update_results.push(result),\n Err(e) => warn!(\"更新评估失败 {}: {}\", test_case.test_case_id, e),\n }\n }\n \n // 进度报告\n if i % 10 == 0 && i > 0 {\n info!(\"已评估 {} 个测试用例\", i);\n }\n }\n \n // 计算各项指标\n let fact_extraction_metrics = self.calculate_fact_extraction_metrics(&fact_extraction_results);\n let classification_metrics = self.calculate_classification_metrics(&classification_results);\n let importance_metrics = self.calculate_importance_metrics(&importance_results);\n let deduplication_metrics = self.calculate_deduplication_metrics(&deduplication_results);\n let update_metrics = self.calculate_update_metrics(&update_results);\n \n // 计算综合得分\n let overall_score = self.calculate_overall_score(\n &fact_extraction_metrics,\n &classification_metrics,\n &importance_metrics,\n &deduplication_metrics,\n &update_metrics,\n );\n \n let metrics = EffectivenessMetrics {\n fact_extraction_accuracy: fact_extraction_metrics,\n classification_accuracy: classification_metrics,\n importance_evaluation_quality: importance_metrics,\n deduplication_effectiveness: deduplication_metrics,\n memory_update_correctness: update_metrics,\n overall_score,\n };\n \n info!(\"真实有效性评估完成,综合得分: {:.2}\", overall_score);\n \n // 清理测试数据\n if self.config.cleanup_test_data {\n self.cleanup_test_data().await?;\n }\n \n Ok(metrics)\n }\n \n /// 添加现有记忆到记忆库\n async fn add_existing_memories(\n &self,\n existing_memories: &HashMap,\n ) -> Result<()> {\n let mut added_count = 0;\n let mut error_count = 0;\n \n for (memory_id, memory) in existing_memories {\n match self.memory_manager.store(\n memory.content.clone(),\n memory.metadata.clone(),\n ).await {\n Ok(new_id) => {\n if self.config.enable_verbose_logging {\n debug!(\"添加现有记忆 {} -> {}\", memory_id, new_id);\n }\n added_count += 1;\n }\n Err(e) => {\n // 如果是重复错误,可以忽略\n if !e.to_string().contains(\"duplicate\") && !e.to_string().contains(\"already exists\") {\n error!(\"添加现有记忆失败 {}: {}\", memory_id, e);\n error_count += 1;\n } else {\n debug!(\"现有记忆已存在: {}\", memory_id);\n }\n }\n }\n }\n \n info!(\"现有记忆添加完成: 成功={}, 错误={}\", added_count, error_count);\n Ok(())\n }\n \n /// 评估事实提取\n async fn evaluate_fact_extraction(\n &self,\n test_case: &EffectivenessTestCase,\n ) -> Result {\n let start_time = Instant::now();\n \n // 创建测试消息\n let messages = vec![\n Message {\n role: \"user\".to_string(),\n content: test_case.input_text.clone(),\n name: Some(\"test_user\".to_string()),\n \n },\n ];\n \n // 创建元数据\n let mut metadata = cortex_mem_core::types::MemoryMetadata::new(\n test_case.expected_memory_type.clone(),\n );\n metadata.user_id = Some(\"test_user\".to_string());\n \n // 调用真实的事实提取\n let result = tokio::time::timeout(\n Duration::from_secs(self.config.timeout_seconds),\n self.memory_manager.add_memory(&messages, metadata),\n ).await\n .context(\"事实提取超时\")?\n .context(\"事实提取失败\")?;\n \n let latency = start_time.elapsed();\n \n // 提取实际存储的记忆内容\n let extracted_content = if !result.is_empty() {\n result[0].memory.clone()\n } else {\n \"\".to_string()\n };\n \n // 简化的事实匹配:检查预期关键词是否出现在提取的内容中\n let mut matched_facts = 0;\n for expected_fact in &test_case.expected_facts {\n if extracted_content.contains(expected_fact) {\n matched_facts += 1;\n }\n }\n \n let is_perfect_match = matched_facts == test_case.expected_facts.len();\n \n // 将提取的内容分割为\"事实\"(简化实现)\n let extracted_facts = extracted_content\n .split('.')\n .map(|s| s.trim())\n .filter(|s| !s.is_empty())\n .map(|s| s.to_string())\n .collect();\n \n Ok(FactExtractionResult {\n input_text: test_case.input_text.clone(),\n extracted_facts,\n ground_truth_facts: test_case.expected_facts.clone(),\n matched_facts,\n is_perfect_match,\n })\n }\n \n /// 评估记忆分类\n async fn evaluate_classification(\n &self,\n test_case: &EffectivenessTestCase,\n ) -> Result {\n let start_time = Instant::now();\n \n // 创建测试消息\n let messages = vec![\n Message {\n role: \"user\".to_string(),\n content: test_case.input_text.clone(),\n name: Some(\"test_user\".to_string()),\n \n },\n ];\n \n // 创建元数据(使用默认类型,让系统自动分类)\n let mut metadata = cortex_mem_core::types::MemoryMetadata::new(\n MemoryType::Conversational, // 默认类型\n );\n metadata.user_id = Some(\"test_user\".to_string());\n \n // 添加记忆并获取实际分类\n let result = tokio::time::timeout(\n Duration::from_secs(self.config.timeout_seconds),\n self.memory_manager.add_memory(&messages, metadata),\n ).await\n .context(\"记忆分类超时\")?\n .context(\"记忆分类失败\")?;\n \n let latency = start_time.elapsed();\n \n // 获取实际存储的记忆类型\n let predicted_type = if !result.is_empty() {\n // 搜索最近添加的记忆以获取其类型\n let mut filters = Filters::default();\n filters.user_id = Some(\"test_user\".to_string());\n \n let search_results = self.memory_manager.search(\n &test_case.input_text,\n &filters,\n 1,\n ).await?;\n \n if !search_results.is_empty() {\n search_results[0].memory.metadata.memory_type.clone()\n } else {\n MemoryType::Conversational // 默认值\n }\n } else {\n MemoryType::Conversational // 默认值\n };\n \n let is_correct = predicted_type == test_case.expected_memory_type;\n \n Ok(ClassificationResult {\n test_case_id: test_case.test_case_id.clone(),\n input_text: test_case.input_text.clone(),\n predicted_type,\n expected_type: test_case.expected_memory_type.clone(),\n is_correct,\n latency_ms: latency.as_millis() as u64,\n })\n }\n \n /// 评估重要性\n async fn evaluate_importance(\n &self,\n test_case: &EffectivenessTestCase,\n ) -> Result {\n let start_time = Instant::now();\n \n // 创建测试消息\n let messages = vec![\n Message {\n role: \"user\".to_string(),\n content: test_case.input_text.clone(),\n name: Some(\"test_user\".to_string()),\n \n },\n ];\n \n // 创建元数据\n let mut metadata = cortex_mem_core::types::MemoryMetadata::new(\n test_case.expected_memory_type.clone(),\n );\n metadata.user_id = Some(\"test_user\".to_string());\n \n // 添加记忆\n let result = tokio::time::timeout(\n Duration::from_secs(self.config.timeout_seconds),\n self.memory_manager.add_memory(&messages, metadata),\n ).await\n .context(\"重要性评估超时\")?\n .context(\"重要性评估失败\")?;\n \n let latency = start_time.elapsed();\n \n // 获取实际存储的记忆重要性评分\n let predicted_score = if !result.is_empty() {\n // 搜索最近添加的记忆\n let mut filters = Filters::default();\n filters.user_id = Some(\"test_user\".to_string());\n \n let search_results = self.memory_manager.search(\n &test_case.input_text,\n &filters,\n 1,\n ).await?;\n \n if !search_results.is_empty() {\n (search_results[0].memory.metadata.importance_score * 10.0).round() as u8\n } else {\n 5 // 默认值\n }\n } else {\n 5 // 默认值\n };\n \n let error = (predicted_score as i16 - test_case.expected_importance_score as i16).abs();\n let within_tolerance = error <= self.config.importance_score_tolerance as i16;\n \n Ok(ImportanceResult {\n test_case_id: test_case.test_case_id.clone(),\n input_text: test_case.input_text.clone(),\n predicted_score,\n expected_score: test_case.expected_importance_score,\n error: error as u8,\n within_tolerance,\n latency_ms: latency.as_millis() as u64,\n })\n }\n \n /// 评估去重效果\n async fn evaluate_deduplication(\n &self,\n test_case: &EffectivenessTestCase,\n ) -> Result {\n let start_time = Instant::now();\n \n // 首先添加原始记忆\n let messages = vec![\n Message {\n role: \"user\".to_string(),\n content: test_case.input_text.clone(),\n name: Some(\"test_user\".to_string()),\n \n },\n ];\n \n let mut metadata = cortex_mem_core::types::MemoryMetadata::new(\n test_case.expected_memory_type.clone(),\n );\n metadata.user_id = Some(\"test_user\".to_string());\n \n let first_result = self.memory_manager.add_memory(&messages, metadata.clone()).await?;\n \n // 尝试添加相同或相似的记忆(模拟重复)\n let duplicate_messages = vec![\n Message {\n role: \"user\".to_string(),\n content: format!(\"{} (重复)\", test_case.input_text), // 轻微修改以测试去重\n name: Some(\"test_user\".to_string()),\n \n },\n ];\n \n let second_result = self.memory_manager.add_memory(&duplicate_messages, metadata).await?;\n \n let latency = start_time.elapsed();\n \n // 分析结果:如果第二个操作返回了合并或更新的结果,说明去重生效\n let duplicate_detected = second_result.iter().any(|r| {\n matches!(r.event, cortex_mem_core::types::MemoryEvent::Update)\n });\n \n let correctly_merged = duplicate_detected;\n let merge_quality = if duplicate_detected { 0.8 } else { 0.0 }; // 简化质量评估\n \n Ok(DeduplicationResult {\n test_case_id: test_case.test_case_id.clone(),\n duplicate_detected,\n correctly_merged,\n merge_quality,\n latency_ms: latency.as_millis() as u64,\n })\n }\n \n /// 评估记忆更新\n async fn evaluate_memory_update(\n &self,\n test_case: &EffectivenessTestCase,\n existing_memories: &HashMap,\n ) -> Result {\n let start_time = Instant::now();\n \n // 首先确保现有记忆存在\n if let Some(existing_memory_id) = &test_case.existing_memory_id {\n if let Some(existing_memory) = existing_memories.get(existing_memory_id) {\n // 添加现有记忆\n let _ = self.memory_manager.store(\n existing_memory.content.clone(),\n existing_memory.metadata.clone(),\n ).await;\n }\n }\n \n // 创建更新消息(包含新信息)\n let update_messages = vec![\n Message {\n role: \"user\".to_string(),\n content: format!(\"{} - 更新信息\", test_case.input_text),\n name: Some(\"test_user\".to_string()),\n \n },\n ];\n \n let mut metadata = cortex_mem_core::types::MemoryMetadata::new(\n test_case.expected_memory_type.clone(),\n );\n metadata.user_id = Some(\"test_user\".to_string());\n \n // 尝试更新\n let update_result = self.memory_manager.add_memory(&update_messages, metadata).await?;\n \n let latency = start_time.elapsed();\n \n // 分析更新结果\n let update_correct = !update_result.is_empty();\n let merge_correct = update_result.iter().any(|r| {\n matches!(r.event, cortex_mem_core::types::MemoryEvent::Update)\n });\n let conflict_resolved = true; // 简化:假设冲突已解决\n let updated_quality = if update_correct { 0.7 } else { 0.0 }; // 简化质量评估\n \n Ok(UpdateResult {\n test_case_id: test_case.test_case_id.clone(),\n update_correct,\n merge_correct,\n conflict_resolved,\n updated_quality,\n latency_ms: latency.as_millis() as u64,\n })\n }\n \n /// 计算事实提取指标\n fn calculate_fact_extraction_metrics(\n &self,\n results: &[FactExtractionResult],\n ) -> FactExtractionMetrics {\n let total_facts_extracted: usize = results.iter()\n .map(|r| r.extracted_facts.len())\n .sum();\n \n let total_correct_facts: usize = results.iter()\n .map(|r| r.matched_facts)\n .sum();\n \n let total_expected_facts: usize = results.iter()\n .map(|r| r.ground_truth_facts.len())\n .sum();\n \n let precision = if total_facts_extracted > 0 {\n total_correct_facts as f64 / total_facts_extracted as f64\n } else {\n 0.0\n };\n \n let recall = if total_expected_facts > 0 {\n total_correct_facts as f64 / total_expected_facts as f64\n } else {\n 0.0\n };\n \n let f1_score = if precision + recall > 0.0 {\n 2.0 * precision * recall / (precision + recall)\n } else {\n 0.0\n };\n \n FactExtractionMetrics {\n precision,\n recall,\n f1_score,\n facts_extracted: total_facts_extracted,\n correct_facts: total_correct_facts,\n detailed_results: results.to_vec(),\n }\n }\n \n /// 计算分类指标\n fn calculate_classification_metrics(\n &self,\n results: &[ClassificationResult],\n ) -> ClassificationMetrics {\n let total_correct = results.iter().filter(|r| r.is_correct).count();\n let accuracy = if !results.is_empty() {\n total_correct as f64 / results.len() as f64\n } else {\n 0.0\n };\n \n // 按类别统计\n let mut confusion_matrix: HashMap> = HashMap::new();\n let mut precision_by_class: HashMap = HashMap::new();\n let mut recall_by_class: HashMap = HashMap::new();\n let mut f1_by_class: HashMap = HashMap::new();\n \n for result in results {\n let predicted = format!(\"{:?}\", result.predicted_type);\n let expected = format!(\"{:?}\", result.expected_type);\n \n *confusion_matrix\n .entry(expected.clone())\n .or_default()\n .entry(predicted.clone())\n .or_default() += 1;\n }\n \n // 计算每个类别的指标\n for (expected_class, predictions) in &confusion_matrix {\n let total_predicted_as_class: usize = confusion_matrix.values()\n .map(|pred_map| pred_map.get(expected_class).copied().unwrap_or(0))\n .sum();\n \n let true_positives = predictions.get(expected_class).copied().unwrap_or(0);\n let false_positives = total_predicted_as_class - true_positives;\n let false_negatives: usize = predictions.values().sum::() - true_positives;\n \n let precision = if true_positives + false_positives > 0 {\n true_positives as f64 / (true_positives + false_positives) as f64\n } else {\n 0.0\n };\n \n let recall = if true_positives + false_negatives > 0 {\n true_positives as f64 / (true_positives + false_negatives) as f64\n } else {\n 0.0\n };\n \n let f1 = if precision + recall > 0.0 {\n 2.0 * precision * recall / (precision + recall)\n } else {\n 0.0\n };\n \n precision_by_class.insert(expected_class.clone(), precision);\n recall_by_class.insert(expected_class.clone(), recall);\n f1_by_class.insert(expected_class.clone(), f1);\n }\n \n ClassificationMetrics {\n accuracy,\n precision_by_class,\n recall_by_class,\n f1_by_class,\n confusion_matrix,\n }\n }\n \n /// 计算重要性评估指标\n fn calculate_importance_metrics(\n &self,\n results: &[ImportanceResult],\n ) -> ImportanceMetrics {\n if results.is_empty() {\n return ImportanceMetrics {\n correlation_score: 0.0,\n mean_absolute_error: 0.0,\n root_mean_squared_error: 0.0,\n score_distribution: HashMap::new(),\n within_tolerance_rate: 0.0,\n };\n }\n \n let mut total_abs_error = 0.0;\n let mut total_squared_error = 0.0;\n let mut predicted_scores = Vec::new();\n let mut expected_scores = Vec::new();\n let mut score_distribution: HashMap = HashMap::new();\n let mut within_tolerance_count = 0;\n \n for result in results {\n let error = result.error as f64;\n total_abs_error += error;\n total_squared_error += error * error;\n \n predicted_scores.push(result.predicted_score as f64);\n expected_scores.push(result.expected_score as f64);\n \n *score_distribution.entry(result.predicted_score as usize).or_default() += 1;\n \n if result.within_tolerance {\n within_tolerance_count += 1;\n }\n }\n \n let mean_absolute_error = total_abs_error / results.len() as f64;\n let root_mean_squared_error = (total_squared_error / results.len() as f64).sqrt();\n let within_tolerance_rate = within_tolerance_count as f64 / results.len() as f64;\n \n // 简化相关性计算\n let correlation_score = if !predicted_scores.is_empty() && !expected_scores.is_empty() {\n // 模拟相关性计算(实际应该使用皮尔逊相关系数)\n let avg_predicted: f64 = predicted_scores.iter().sum::() / predicted_scores.len() as f64;\n let avg_expected: f64 = expected_scores.iter().sum::() / expected_scores.len() as f64;\n \n let mut covariance = 0.0;\n let mut var_predicted = 0.0;\n let mut var_expected = 0.0;\n \n for i in 0..predicted_scores.len() {\n let diff_pred = predicted_scores[i] - avg_predicted;\n let diff_exp = expected_scores[i] - avg_expected;\n covariance += diff_pred * diff_exp;\n var_predicted += diff_pred * diff_pred;\n var_expected += diff_exp * diff_exp;\n }\n \n if var_predicted > 0.0 && var_expected > 0.0 {\n covariance / (var_predicted.sqrt() * var_expected.sqrt())\n } else {\n 0.0\n }\n } else {\n 0.0\n };\n \n ImportanceMetrics {\n correlation_score: correlation_score.max(0.0).min(1.0),\n mean_absolute_error,\n root_mean_squared_error,\n score_distribution,\n within_tolerance_rate,\n }\n }\n \n /// 计算去重指标\n fn calculate_deduplication_metrics(\n &self,\n results: &[DeduplicationResult],\n ) -> DeduplicationMetrics {\n if results.is_empty() {\n return DeduplicationMetrics {\n duplicate_detection_precision: 0.0,\n duplicate_detection_recall: 0.0,\n merge_accuracy: 0.0,\n duplicate_pairs_detected: 0,\n actual_duplicate_pairs: 0,\n avg_merge_quality: 0.0,\n };\n }\n \n let true_positives = results.iter().filter(|r| r.duplicate_detected).count();\n let false_positives = 0; // 简化:假设没有误报\n let false_negatives = 0; // 简化:假设没有漏报\n \n let precision = if true_positives + false_positives > 0 {\n true_positives as f64 / (true_positives + false_positives) as f64\n } else {\n 0.0\n };\n \n let recall = if true_positives + false_negatives > 0 {\n true_positives as f64 / (true_positives + false_negatives) as f64\n } else {\n 0.0\n };\n \n let merge_accuracy = if !results.is_empty() {\n results.iter().filter(|r| r.correctly_merged).count() as f64 / results.len() as f64\n } else {\n 0.0\n };\n \n let avg_merge_quality = if !results.is_empty() {\n results.iter().map(|r| r.merge_quality).sum::() / results.len() as f64\n } else {\n 0.0\n };\n \n DeduplicationMetrics {\n duplicate_detection_precision: precision,\n duplicate_detection_recall: recall,\n merge_accuracy,\n duplicate_pairs_detected: true_positives,\n actual_duplicate_pairs: true_positives, // 简化:假设检测到的都是实际的\n avg_merge_quality,\n }\n }\n \n /// 计算更新指标\n fn calculate_update_metrics(\n &self,\n results: &[UpdateResult],\n ) -> UpdateMetrics {\n if results.is_empty() {\n return UpdateMetrics {\n update_operation_accuracy: 0.0,\n merge_operation_accuracy: 0.0,\n conflict_resolution_accuracy: 0.0,\n updated_memory_quality: 0.0,\n };\n }\n \n let update_accuracy = results.iter().filter(|r| r.update_correct).count() as f64 / results.len() as f64;\n let merge_accuracy = results.iter().filter(|r| r.merge_correct).count() as f64 / results.len() as f64;\n let conflict_accuracy = results.iter().filter(|r| r.conflict_resolved).count() as f64 / results.len() as f64;\n let avg_quality = results.iter().map(|r| r.updated_quality).sum::() / results.len() as f64;\n \n UpdateMetrics {\n update_operation_accuracy: update_accuracy,\n merge_operation_accuracy: merge_accuracy,\n conflict_resolution_accuracy: conflict_accuracy,\n updated_memory_quality: avg_quality,\n }\n }\n \n /// 计算综合得分\n fn calculate_overall_score(\n &self,\n fact_metrics: &FactExtractionMetrics,\n classification_metrics: &ClassificationMetrics,\n importance_metrics: &ImportanceMetrics,\n deduplication_metrics: &DeduplicationMetrics,\n update_metrics: &UpdateMetrics,\n ) -> f64 {\n let mut total_score = 0.0;\n let mut weight_sum = 0.0;\n \n // 事实提取权重:0.3\n if self.config.verify_fact_extraction {\n let fact_score = (fact_metrics.f1_score + fact_metrics.precision + fact_metrics.recall) / 3.0;\n total_score += fact_score * 0.3;\n weight_sum += 0.3;\n }\n \n // 分类权重:0.2\n if self.config.verify_classification {\n total_score += classification_metrics.accuracy * 0.2;\n weight_sum += 0.2;\n }\n \n // 重要性评估权重:0.2\n if self.config.verify_importance_evaluation {\n let importance_score = 1.0 - importance_metrics.mean_absolute_error / 10.0; // 归一化到0-1\n total_score += importance_score.max(0.0).min(1.0) * 0.2;\n weight_sum += 0.2;\n }\n \n // 去重权重:0.15\n if self.config.verify_deduplication {\n let dedup_score = (deduplication_metrics.duplicate_detection_precision +\n deduplication_metrics.duplicate_detection_recall +\n deduplication_metrics.merge_accuracy) / 3.0;\n total_score += dedup_score * 0.15;\n weight_sum += 0.15;\n }\n \n // 更新权重:0.15\n if self.config.verify_memory_update {\n let update_score = (update_metrics.update_operation_accuracy +\n update_metrics.merge_operation_accuracy +\n update_metrics.conflict_resolution_accuracy +\n update_metrics.updated_memory_quality) / 4.0;\n total_score += update_score * 0.15;\n weight_sum += 0.15;\n }\n \n if weight_sum > 0.0 {\n total_score / weight_sum\n } else {\n 0.0\n }\n }\n \n /// 清理测试数据\n async fn cleanup_test_data(&self) -> Result<()> {\n info!(\"清理测试数据...\");\n // 实际实现应该删除测试期间添加的记忆\n // 这里简化实现\n info!(\"测试数据清理完成\");\n Ok(())\n }\n}\n\n// 辅助结构体\n#[derive(Debug, Clone)]\nstruct ClassificationResult {\n test_case_id: String,\n input_text: String,\n predicted_type: MemoryType,\n expected_type: MemoryType,\n is_correct: bool,\n latency_ms: u64,\n}\n\n#[derive(Debug, Clone)]\nstruct ImportanceResult {\n test_case_id: String,\n input_text: String,\n predicted_score: u8,\n expected_score: u8,\n error: u8,\n within_tolerance: bool,\n latency_ms: u64,\n}\n\n#[derive(Debug, Clone)]\nstruct DeduplicationResult {\n test_case_id: String,\n duplicate_detected: bool,\n correctly_merged: bool,\n merge_quality: f64,\n latency_ms: u64,\n}\n\n#[derive(Debug, Clone)]\nstruct UpdateResult {\n test_case_id: String,\n update_correct: bool,\n merge_correct: bool,\n conflict_resolved: bool,\n updated_quality: f64,\n latency_ms: u64,\n}\n\n// 重新导出类型\n// 不再从effectiveness_evaluator重新导出,使用dataset::types中的定义" - }, - "complexity_metrics": { - "cyclomatic_complexity": 60.0, - "lines_of_code": 920, - "number_of_classes": 6, - "number_of_functions": 15 - }, - "dependencies": [ - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "cortex_mem_core", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "serde", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": false, - "line_number": null, - "name": "std", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "tokio", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "tracing", - "path": null, - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "super::metrics", - "path": "examples/cortex-mem-evaluation/src/evaluator/metrics", - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "crate::dataset::types", - "path": "examples/cortex-mem-evaluation/src/dataset/types", - "version": null - } - ], - "detailed_description": "真实有效性评估器基于cortex-mem-core API调用,对记忆管理系统的有效性进行全面评估。该组件通过加载测试数据集,对记忆系统的关键功能模块进行端到端的验证,包括事实提取准确性、记忆分类正确性、重要性评估质量、去重效果和记忆更新逻辑。评估器支持可配置的验证维度,允许按需启用或禁用特定功能的测试。组件通过调用真实记忆管理器API执行测试用例,收集执行结果并计算各项质量指标,最终生成综合评估得分。内部实现了多种评估算法,如基于关键词匹配的事实提取评估、基于混淆矩阵的分类评估、基于误差统计的重要性评估等,并通过加权平均算法计算整体有效性得分。", - "interfaces": [ - { - "description": "真实有效性评估器配置,定义评估维度、参数和行为控制", - "interface_type": "struct", - "name": "RealEffectivenessEvaluationConfig", - "parameters": [ - { - "description": "是否验证事实提取", - "is_optional": false, - "name": "verify_fact_extraction", - "param_type": "bool" - }, - { - "description": "是否验证记忆分类", - "is_optional": false, - "name": "verify_classification", - "param_type": "bool" - }, - { - "description": "是否验证重要性评估", - "is_optional": false, - "name": "verify_importance_evaluation", - "param_type": "bool" - }, - { - "description": "是否验证去重效果", - "is_optional": false, - "name": "verify_deduplication", - "param_type": "bool" - }, - { - "description": "是否验证记忆更新逻辑", - "is_optional": false, - "name": "verify_memory_update", - "param_type": "bool" - }, - { - "description": "重要性评分容差", - "is_optional": false, - "name": "importance_score_tolerance", - "param_type": "u8" - }, - { - "description": "超时时间(秒)", - "is_optional": false, - "name": "timeout_seconds", - "param_type": "u64" - }, - { - "description": "是否启用详细日志", - "is_optional": false, - "name": "enable_verbose_logging", - "param_type": "bool" - }, - { - "description": "是否清理测试数据", - "is_optional": false, - "name": "cleanup_test_data", - "param_type": "bool" - }, - { - "description": "测试数据集路径", - "is_optional": false, - "name": "test_cases_path", - "param_type": "String" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "真实有效性评估器主结构体,协调和执行各项评估任务", - "interface_type": "struct", - "name": "RealEffectivenessEvaluator", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "创建新的真实有效性评估器实例", - "interface_type": "method", - "name": "new", - "parameters": [ - { - "description": "评估配置", - "is_optional": false, - "name": "config", - "param_type": "RealEffectivenessEvaluationConfig" - }, - { - "description": "记忆管理器实例", - "is_optional": false, - "name": "memory_manager", - "param_type": "std::sync::Arc" - } - ], - "return_type": "Self", - "visibility": "public" - }, - { - "description": "执行完整的有效性评估流程并返回评估指标", - "interface_type": "method", - "name": "evaluate", - "parameters": [ - { - "description": "有效性测试数据集", - "is_optional": false, - "name": "dataset", - "param_type": "&EffectivenessTestDataset" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "事实提取评估结果", - "interface_type": "struct", - "name": "FactExtractionResult", - "parameters": [ - { - "description": "输入文本", - "is_optional": false, - "name": "input_text", - "param_type": "String" - }, - { - "description": "提取的事实列表", - "is_optional": false, - "name": "extracted_facts", - "param_type": "Vec" - }, - { - "description": "真实事实列表", - "is_optional": false, - "name": "ground_truth_facts", - "param_type": "Vec" - }, - { - "description": "匹配的事实数量", - "is_optional": false, - "name": "matched_facts", - "param_type": "usize" - }, - { - "description": "是否完全匹配", - "is_optional": false, - "name": "is_perfect_match", - "param_type": "bool" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "记忆分类评估结果", - "interface_type": "struct", - "name": "ClassificationResult", - "parameters": [ - { - "description": "测试用例ID", - "is_optional": false, - "name": "test_case_id", - "param_type": "String" - }, - { - "description": "输入文本", - "is_optional": false, - "name": "input_text", - "param_type": "String" - }, - { - "description": "预测的记忆类型", - "is_optional": false, - "name": "predicted_type", - "param_type": "MemoryType" - }, - { - "description": "期望的记忆类型", - "is_optional": false, - "name": "expected_type", - "param_type": "MemoryType" - }, - { - "description": "预测是否正确", - "is_optional": false, - "name": "is_correct", - "param_type": "bool" - }, - { - "description": "处理延迟(毫秒)", - "is_optional": false, - "name": "latency_ms", - "param_type": "u64" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "重要性评估结果", - "interface_type": "struct", - "name": "ImportanceResult", - "parameters": [ - { - "description": "测试用例ID", - "is_optional": false, - "name": "test_case_id", - "param_type": "String" - }, - { - "description": "输入文本", - "is_optional": false, - "name": "input_text", - "param_type": "String" - }, - { - "description": "预测的重要性评分", - "is_optional": false, - "name": "predicted_score", - "param_type": "u8" - }, - { - "description": "期望的重要性评分", - "is_optional": false, - "name": "expected_score", - "param_type": "u8" - }, - { - "description": "评分误差", - "is_optional": false, - "name": "error", - "param_type": "u8" - }, - { - "description": "是否在容差范围内", - "is_optional": false, - "name": "within_tolerance", - "param_type": "bool" - }, - { - "description": "处理延迟(毫秒)", - "is_optional": false, - "name": "latency_ms", - "param_type": "u64" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "去重评估结果", - "interface_type": "struct", - "name": "DeduplicationResult", - "parameters": [ - { - "description": "测试用例ID", - "is_optional": false, - "name": "test_case_id", - "param_type": "String" - }, - { - "description": "是否检测到重复", - "is_optional": false, - "name": "duplicate_detected", - "param_type": "bool" - }, - { - "description": "是否正确合并", - "is_optional": false, - "name": "correctly_merged", - "param_type": "bool" - }, - { - "description": "合并质量评分", - "is_optional": false, - "name": "merge_quality", - "param_type": "f64" - }, - { - "description": "处理延迟(毫秒)", - "is_optional": false, - "name": "latency_ms", - "param_type": "u64" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "记忆更新评估结果", - "interface_type": "struct", - "name": "UpdateResult", - "parameters": [ - { - "description": "测试用例ID", - "is_optional": false, - "name": "test_case_id", - "param_type": "String" - }, - { - "description": "更新是否正确", - "is_optional": false, - "name": "update_correct", - "param_type": "bool" - }, - { - "description": "合并是否正确", - "is_optional": false, - "name": "merge_correct", - "param_type": "bool" - }, - { - "description": "冲突是否解决", - "is_optional": false, - "name": "conflict_resolved", - "param_type": "bool" - }, - { - "description": "更新质量评分", - "is_optional": false, - "name": "updated_quality", - "param_type": "f64" - }, - { - "description": "处理延迟(毫秒)", - "is_optional": false, - "name": "latency_ms", - "param_type": "u64" - } - ], - "return_type": null, - "visibility": "public" - } - ], - "responsibilities": [ - "执行记忆管理系统端到端有效性评估", - "验证事实提取、分类、重要性评估、去重和更新等核心功能", - "计算各项功能的量化评估指标并生成综合得分", - "管理测试用例执行生命周期和资源清理", - "提供可配置的评估维度和参数控制" - ] - }, - { - "code_dossier": { - "code_purpose": "specificfeature", - "description": "性能评估器,用于评估系统性能指标,支持基准测试、负载测试、压力测试和可扩展性测试。", - "file_path": "examples/cortex-mem-evaluation/src/evaluator/performance_evaluator.rs", - "functions": [ - "new", - "evaluate", - "run_benchmark", - "run_load_test", - "run_stress_test", - "run_scalability_test" - ], - "importance_score": 0.8, - "interfaces": [ - "PerformanceEvaluator", - "PerformanceEvaluationConfig" - ], - "name": "performance_evaluator.rs", - "source_summary": "//! 性能评估器\n//! \n//! 评估系统性能指标\n\nuse anyhow::Result;\nuse serde::{Deserialize, Serialize};\nuse tracing::info;\n\nuse super::metrics::PerformanceMetrics;\n\n/// 性能评估器\npub struct PerformanceEvaluator {\n /// 评估配置\n config: PerformanceEvaluationConfig,\n}\n\n/// 性能评估配置\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct PerformanceEvaluationConfig {\n /// 测试的记忆库大小列表\n pub memory_sizes: Vec,\n /// 并发用户数列表\n pub concurrent_users: Vec,\n /// 测试持续时间(秒)\n pub test_duration_seconds: u64,\n /// 预热时间(秒)\n pub warmup_seconds: u64,\n /// 操作类型:add, search, update, mixed\n pub operation_types: Vec,\n /// 是否测量内存使用\n pub measure_memory_usage: bool,\n /// 是否测量CPU使用\n pub measure_cpu_usage: bool,\n}\n\nimpl Default for PerformanceEvaluationConfig {\n fn default() -> Self {\n Self {\n memory_sizes: vec![100, 1000, 5000],\n concurrent_users: vec![1, 5, 10],\n test_duration_seconds: 30,\n warmup_seconds: 5,\n operation_types: vec![\"add\".to_string(), \"search\".to_string(), \"update\".to_string(), \"mixed\".to_string()],\n measure_memory_usage: true,\n measure_cpu_usage: true,\n }\n }\n}\n\nimpl PerformanceEvaluator {\n /// 创建新的性能评估器\n pub fn new(config: PerformanceEvaluationConfig) -> Self {\n Self { config }\n }\n \n /// 评估系统性能\n pub async fn evaluate(&self, memory_manager: Option<&cortex_mem_core::MemoryManager>) -> Result {\n info!(\"开始性能评估...\");\n \n // 性能评估需要实际的 MemoryManager 实例\n if memory_manager.is_none() {\n anyhow::bail!(\"性能评估需要 MemoryManager 实例,请提供有效的 MemoryManager\");\n }\n \n let memory_manager = memory_manager.unwrap();\n info!(\"性能评估框架就绪,使用提供的 MemoryManager 实例\");\n \n // 实际性能评估逻辑需要实现\n // 这里返回空指标,需要实际实现\n Ok(PerformanceMetrics {\n latency: super::metrics::LatencyMetrics {\n add_memory_avg_ms: 0.0,\n search_memory_avg_ms: 0.0,\n update_memory_avg_ms: 0.0,\n delete_memory_avg_ms: 0.0,\n p95_latency_ms: 0.0,\n p99_latency_ms: 0.0,\n max_latency_ms: 0.0,\n latency_distribution: std::collections::HashMap::new(),\n },\n throughput: super::metrics::ThroughputMetrics {\n operations_per_second: 0.0,\n add_ops_per_second: 0.0,\n search_ops_per_second: 0.0,\n update_ops_per_second: 0.0,\n peak_throughput: 0.0,\n sustainable_throughput: 0.0,\n },\n resource_usage: super::metrics::ResourceMetrics {\n memory_usage_mb: 0.0,\n cpu_usage_percent: 0.0,\n disk_usage_mb: 0.0,\n network_usage_kbps: 0.0,\n usage_trend: Vec::new(),\n },\n concurrency: super::metrics::ConcurrencyMetrics {\n concurrent_users: 0,\n avg_response_time_ms: 0.0,\n throughput_ops_per_sec: 0.0,\n error_rate_percent: 0.0,\n success_rate_percent: 0.0,\n },\n scalability: super::metrics::ScalabilityMetrics {\n performance_by_memory_size: std::collections::HashMap::new(),\n linear_scaling_factor: 0.0,\n bottleneck_point: None,\n },\n })\n }\n \n /// 运行基准测试\n pub async fn run_benchmark(&self) -> Result<()> {\n info!(\"基准测试框架就绪\");\n info!(\"支持以下测试:\");\n info!(\" - 添加记忆性能测试\");\n info!(\" - 搜索记忆性能测试\");\n info!(\" - 更新记忆性能测试\");\n info!(\" - 混合操作性能测试\");\n Ok(())\n }\n \n /// 运行负载测试\n pub async fn run_load_test(&self) -> Result<()> {\n info!(\"负载测试框架就绪\");\n info!(\"支持并发用户测试: {:?}\", self.config.concurrent_users);\n Ok(())\n }\n \n /// 运行压力测试\n pub async fn run_stress_test(&self) -> Result<()> {\n info!(\"压力测试框架就绪\");\n info!(\"测试持续时间: {}秒\", self.config.test_duration_seconds);\n Ok(())\n }\n \n /// 运行可扩展性测试\n pub async fn run_scalability_test(&self) -> Result<()> {\n info!(\"可扩展性测试框架就绪\");\n info!(\"测试记忆库规模: {:?}\", self.config.memory_sizes);\n Ok(())\n }\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 3.0, - "lines_of_code": 142, - "number_of_classes": 2, - "number_of_functions": 6 - }, - "dependencies": [ - { - "dependency_type": "error_handling", - "is_external": true, - "line_number": 1, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "serialization", - "is_external": true, - "line_number": 2, - "name": "serde", - "path": null, - "version": null - }, - { - "dependency_type": "logging", - "is_external": true, - "line_number": 3, - "name": "tracing", - "path": null, - "version": null - }, - { - "dependency_type": "internal_component", - "is_external": false, - "line_number": 9, - "name": "cortex_mem_core::MemoryManager", - "path": "super", - "version": null - } - ], - "detailed_description": "该组件实现了一个性能评估框架,用于评估 Cortex-Mem 系统的记忆管理性能。核心功能包括:配置化的性能测试(支持不同操作类型、并发用户数和记忆库大小)、与 MemoryManager 集成进行真实性能测量、收集延迟、吞吐量、资源使用、并发性和可扩展性等多维度指标。目前 evaluate 方法仅返回空指标,实际测量逻辑待实现,但整体框架结构完整,支持多种测试类型。", - "interfaces": [ - { - "description": "性能评估配置结构体,包含测试参数如记忆库大小、并发用户数、测试持续时间等", - "interface_type": "struct", - "name": "PerformanceEvaluationConfig", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "性能评估器主结构体,负责执行性能评估", - "interface_type": "struct", - "name": "PerformanceEvaluator", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "创建新的性能评估器实例", - "interface_type": "method", - "name": "new", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "config", - "param_type": "PerformanceEvaluationConfig" - } - ], - "return_type": "PerformanceEvaluator", - "visibility": "public" - }, - { - "description": "执行系统性能评估,需要提供 MemoryManager 实例", - "interface_type": "method", - "name": "evaluate", - "parameters": [ - { - "description": null, - "is_optional": true, - "name": "memory_manager", - "param_type": "Option<&MemoryManager>" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "运行基准测试,测试基本操作性能", - "interface_type": "method", - "name": "run_benchmark", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" - }, - { - "description": "运行负载测试,评估系统在不同并发用户下的表现", - "interface_type": "method", - "name": "run_load_test", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" - }, - { - "description": "运行压力测试,评估系统在高负载下的稳定性", - "interface_type": "method", - "name": "run_stress_test", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" - }, - { - "description": "运行可扩展性测试,评估系统在不同记忆库规模下的性能变化", - "interface_type": "method", - "name": "run_scalability_test", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" - }, - { - "description": "PerformanceEvaluationConfig 的默认实现,提供标准测试配置", - "interface_type": "trait", - "name": "default", - "parameters": [], - "return_type": "PerformanceEvaluationConfig", - "visibility": "public" - }, - { - "description": "支持 PerformanceEvaluationConfig 的克隆", - "interface_type": "trait", - "name": "clone", - "parameters": [], - "return_type": "PerformanceEvaluationConfig", - "visibility": "public" - }, - { - "description": "支持 PerformanceEvaluationConfig 的序列化", - "interface_type": "trait", - "name": "serialize", - "parameters": [], - "return_type": null, - "visibility": "public" - } - ], - "responsibilities": [ - "提供性能评估配置管理功能,支持自定义测试参数", - "执行系统性能评估并收集多维度性能指标", - "支持运行基准测试、负载测试、压力测试和可扩展性测试", - "与 MemoryManager 组件集成,实现真实环境下的性能测量" - ] - }, - { - "code_dossier": { - "code_purpose": "module", - "description": "记忆管理器模块,提供MemoryManager实例的创建和配置功能,支持从配置文件或评估配置中初始化完整的记忆系统。", - "file_path": "examples/cortex-mem-evaluation/src/memory/mod.rs", - "functions": [ - "create_memory_manager_from_config", - "create_memory_manager_for_real_evaluation" - ], - "importance_score": 0.8, - "interfaces": [ - "create_memory_manager_from_config", - "create_memory_manager_for_real_evaluation" - ], - "name": "mod.rs", - "source_summary": "//! 记忆管理器模块\n//! \n//! 提供MemoryManager实例的创建和配置\n\nuse anyhow::Result;\nuse cortex_mem_core::MemoryManager;\nuse cortex_mem_config::Config;\nuse std::sync::Arc;\nuse tracing::info;\n\n/// 从配置文件创建MemoryManager实例\npub async fn create_memory_manager_from_config(config_path: &str) -> Result> {\n // 加载配置\n info!(\"正在加载配置文件: {}\", config_path);\n let config = Config::load(config_path)?;\n \n // 检查Qdrant配置\n info!(\"配置中的Qdrant URL: {}\", config.qdrant.url);\n info!(\"配置中的集合名称: {}\", config.qdrant.collection_name);\n \n // 创建真实LLM客户端\n info!(\"创建真实LLM客户端...\");\n let llm_client = cortex_mem_core::llm::create_llm_client(&config.llm, &config.embedding)\n .map_err(|e| anyhow::anyhow!(\"创建LLM客户端失败: {}\", e))?;\n \n info!(\"成功创建真实LLM客户端\");\n \n // 添加延迟,避免立即调用API导致频率过高\n info!(\"等待1秒以避免API调用频率过高...\");\n tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;\n \n // 创建Qdrant向量存储\n info!(\"正在创建Qdrant向量存储...\");\n let qdrant_store = cortex_mem_core::vector_store::qdrant::QdrantVectorStore::new_with_llm_client(\n &config.qdrant,\n llm_client.as_ref()\n ).await\n .map_err(|e| anyhow::anyhow!(\"创建Qdrant向量存储失败: {}\", e))?;\n \n let vector_store = Box::new(qdrant_store);\n \n // 创建MemoryManager\n let memory_manager = MemoryManager::new(vector_store, llm_client, config.memory);\n \n info!(\"MemoryManager 实例创建成功\");\n info!(\"向量存储类型: Qdrant\");\n info!(\"LLM客户端: 真实客户端\");\n \n Ok(Arc::new(memory_manager))\n}\n\n/// 创建用于真实评估的MemoryManager(根据评估配置)\npub async fn create_memory_manager_for_real_evaluation(evaluation_config: &crate::runner::ExperimentConfig) -> Result> {\n // 使用评估配置中的memory_config_path,如果未指定则使用默认路径\n let config_path = evaluation_config.memory_config_path\n .as_deref()\n .unwrap_or(\"config/evaluation_config.toml\");\n \n info!(\"使用配置文件路径: {}\", config_path);\n create_memory_manager_from_config(config_path).await\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 61, - "number_of_classes": 0, - "number_of_functions": 2 - }, - "dependencies": [ - { - "dependency_type": "error handling", - "is_external": true, - "line_number": 1, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "core library", - "is_external": false, - "line_number": 2, - "name": "cortex_mem_core", - "path": null, - "version": null - }, - { - "dependency_type": "configuration", - "is_external": false, - "line_number": 3, - "name": "cortex_mem_config", - "path": null, - "version": null - }, - { - "dependency_type": "standard library", - "is_external": false, - "line_number": 4, - "name": "std::sync::Arc", - "path": null, - "version": null - }, - { - "dependency_type": "logging", - "is_external": true, - "line_number": 5, - "name": "tracing", - "path": null, - "version": null - } - ], - "detailed_description": "该模块负责初始化和配置MemoryManager实例,是整个记忆系统的核心构建入口。它通过加载外部配置文件(如TOML)来构建依赖项,包括LLM客户端和Qdrant向量存储,并最终组合成一个完整的记忆管理服务。模块使用异步处理以适应I/O密集型操作,如网络请求和文件读取。`create_memory_manager_from_config` 函数执行详细的初始化流程:加载配置、创建LLM客户端、添加延迟避免API频率限制、构建Qdrant向量存储并最终构造MemoryManager对象。`create_memory_manager_for_real_evaluation` 则为评估场景封装了配置路径逻辑,允许灵活指定配置源。", - "interfaces": [ - { - "description": "根据指定的配置文件路径创建并配置MemoryManager实例", - "interface_type": "function", - "name": "create_memory_manager_from_config", - "parameters": [ - { - "description": "配置文件路径", - "is_optional": false, - "name": "config_path", - "param_type": "&str" - } - ], - "return_type": "Result>", - "visibility": "public" - }, - { - "description": "根据评估实验配置创建MemoryManager实例,支持默认配置回退", - "interface_type": "function", - "name": "create_memory_manager_for_real_evaluation", - "parameters": [ - { - "description": "评估实验配置", - "is_optional": false, - "name": "evaluation_config", - "param_type": "&crate::runner::ExperimentConfig" - } - ], - "return_type": "Result>", - "visibility": "public" - } - ], - "responsibilities": [ - "加载和解析记忆系统配置信息", - "创建并配置LLM客户端用于文本嵌入和生成", - "初始化Qdrant向量数据库存储实例", - "构建并返回线程安全的MemoryManager共享实例", - "为评估实验提供定制化的MemoryManager创建入口" - ] - }, - { - "code_dossier": { - "code_purpose": "types", - "description": "定义用于测试记忆系统性能、召回率、有效性等场景的数据结构。包含测试用例、测试数据集、测试结果、性能指标和验证结果的类型。", - "file_path": "examples/cortex-mem-evaluation/src/dataset/types.rs", - "functions": [], - "importance_score": 0.8, - "interfaces": [ - "RecallTestCase", - "RecallTestDataset", - "EffectivenessTestCase", - "EffectivenessTestDataset", - "DatasetMetadata", - "PerformanceTestConfig", - "TestResult", - "BenchmarkResult", - "LoadTestResult", - "ScalabilityTestResult", - "ScalePerformance", - "DatasetValidationResult", - "ValidationIssue", - "DatasetStatistics" - ], - "name": "types.rs", - "source_summary": "//! 数据集类型定义\n\nuse cortex_mem_core::{Memory, MemoryType};\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\n\n/// 召回率测试用例\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct RecallTestCase {\n /// 查询ID\n pub query_id: String,\n /// 查询文本\n pub query: String,\n /// 相关记忆ID列表\n pub relevant_memory_ids: Vec,\n /// 查询类别\n pub category: String,\n /// 查询复杂度:simple, medium, complex\n pub complexity: String,\n}\n\n/// 召回率测试数据集\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct RecallTestDataset {\n /// 测试用例列表\n pub test_cases: Vec,\n /// 记忆库(ID -> 记忆内容)\n pub memories: HashMap,\n /// 数据集元数据\n pub metadata: DatasetMetadata,\n}\n\n/// 有效性测试用例\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EffectivenessTestCase {\n /// 测试用例ID\n pub test_case_id: String,\n /// 输入文本\n pub input_text: String,\n /// 预期提取的关键事实\n pub expected_facts: Vec,\n /// 预期记忆类型\n pub expected_memory_type: MemoryType,\n /// 预期重要性评分(1-10)\n pub expected_importance_score: u8,\n /// 测试类别\n pub category: String,\n /// 是否包含重复内容\n pub contains_duplicate: bool,\n /// 是否需要更新现有记忆\n pub requires_update: bool,\n /// 现有记忆ID(如果需要更新)\n pub existing_memory_id: Option,\n}\n\n/// 有效性测试数据集\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct EffectivenessTestDataset {\n /// 测试用例列表\n pub test_cases: Vec,\n /// 现有记忆库(用于更新测试)\n pub existing_memories: HashMap,\n /// 数据集元数据\n pub metadata: DatasetMetadata,\n}\n\n/// 数据集元数据\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct DatasetMetadata {\n /// 数据集名称\n pub name: String,\n /// 创建时间\n pub created_at: String,\n /// 版本\n pub version: String,\n /// 总测试用例数\n pub total_test_cases: usize,\n /// 总记忆数\n pub total_memories: usize,\n /// 平均相关记忆数\n pub avg_relevant_memories: f64,\n}\n\n/// 性能测试配置\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct PerformanceTestConfig {\n /// 测试的记忆库大小列表\n pub memory_sizes: Vec,\n /// 并发用户数列表\n pub concurrent_users: Vec,\n /// 测试持续时间(秒)\n pub test_duration_seconds: u64,\n /// 预热时间(秒)\n pub warmup_seconds: u64,\n /// 操作类型:add, search, update, mixed\n pub operation_types: Vec,\n}\n\n/// 测试结果\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct TestResult {\n /// 测试ID\n pub test_id: String,\n /// 测试名称\n pub test_name: String,\n /// 测试结果\n pub result: T,\n /// 测试开始时间\n pub start_time: String,\n /// 测试结束时间\n pub end_time: String,\n /// 测试持续时间(秒)\n pub duration_seconds: f64,\n /// 是否成功\n pub success: bool,\n /// 错误信息(如果有)\n pub error_message: Option,\n}\n\n/// 基准测试结果\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct BenchmarkResult {\n /// 基准测试名称\n pub benchmark_name: String,\n /// 迭代次数\n pub iterations: usize,\n /// 平均执行时间(毫秒)\n pub avg_execution_time_ms: f64,\n /// 最小执行时间(毫秒)\n pub min_execution_time_ms: f64,\n /// 最大执行时间(毫秒)\n pub max_execution_time_ms: f64,\n /// 标准差\n pub std_deviation_ms: f64,\n /// 吞吐量(操作/秒)\n pub throughput_ops_per_sec: f64,\n /// 内存使用(MB)\n pub memory_usage_mb: f64,\n /// CPU使用率(%)\n pub cpu_usage_percent: f64,\n}\n\n/// 负载测试结果\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LoadTestResult {\n /// 负载测试名称\n pub load_test_name: String,\n /// 并发用户数\n pub concurrent_users: usize,\n /// 总请求数\n pub total_requests: usize,\n /// 成功请求数\n pub successful_requests: usize,\n /// 失败请求数\n pub failed_requests: usize,\n /// 平均响应时间(毫秒)\n pub avg_response_time_ms: f64,\n /// P95响应时间(毫秒)\n pub p95_response_time_ms: f64,\n /// P99响应时间(毫秒)\n pub p99_response_time_ms: f64,\n /// 吞吐量(请求/秒)\n pub throughput_requests_per_sec: f64,\n /// 错误率(%)\n pub error_rate_percent: f64,\n}\n\n/// 可扩展性测试结果\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ScalabilityTestResult {\n /// 可扩展性测试名称\n pub scalability_test_name: String,\n /// 不同规模下的性能\n pub performance_by_scale: HashMap,\n /// 线性扩展因子\n pub linear_scaling_factor: f64,\n /// 瓶颈点\n pub bottleneck_point: Option,\n}\n\n/// 规模性能\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ScalePerformance {\n /// 规模(记忆数量或用户数量)\n pub scale: usize,\n /// 平均响应时间(毫秒)\n pub avg_response_time_ms: f64,\n /// 吞吐量(操作/秒)\n pub throughput_ops_per_sec: f64,\n /// 资源使用分数(0-1)\n pub resource_utilization_score: f64,\n}\n\n/// 数据集验证结果\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct DatasetValidationResult {\n /// 数据集名称\n pub dataset_name: String,\n /// 验证时间\n pub validation_time: String,\n /// 是否有效\n pub is_valid: bool,\n /// 验证问题列表\n pub issues: Vec,\n /// 统计信息\n pub statistics: DatasetStatistics,\n}\n\n/// 验证问题\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ValidationIssue {\n /// 问题类型\n pub issue_type: String,\n /// 问题描述\n pub description: String,\n /// 严重程度:low, medium, high, critical\n pub severity: String,\n /// 受影响的项目\n pub affected_items: Vec,\n /// 建议的修复方法\n pub suggested_fix: Option,\n}\n\n/// 数据集统计信息\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct DatasetStatistics {\n /// 总项目数\n pub total_items: usize,\n /// 有效项目数\n pub valid_items: usize,\n /// 无效项目数\n pub invalid_items: usize,\n /// 平均项目长度\n pub avg_item_length: f64,\n /// 类别分布\n pub category_distribution: HashMap,\n /// 复杂度分布\n pub complexity_distribution: HashMap,\n /// 记忆类型分布\n pub memory_type_distribution: HashMap,\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 241, - "number_of_classes": 14, - "number_of_functions": 0 - }, - "dependencies": [ - { - "dependency_type": "library", - "is_external": false, - "line_number": null, - "name": "cortex_mem_core", - "path": "cortex_mem_core", - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "serde", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": false, - "line_number": null, - "name": "std", - "path": null, - "version": null - } - ], - "detailed_description": "该组件定义了用于评估Cortex记忆系统的一系列结构化数据类型,涵盖召回率、有效性、性能、负载和可扩展性测试等场景。所有类型均实现Debug、Clone、Serialize和Deserialize,便于日志记录、复制和序列化传输或持久化。通过泛型TestResult支持不同类型测试结果的统一结构。各结构职责清晰,如RecallTestCase表示单个召回测试用例,包含查询及其相关记忆ID;EffectivenessTestCase用于验证系统能否正确提取事实和分类记忆;PerformanceTestConfig定义性能测试参数;各类*Result结构封装测试度量指标,支持后续分析。整体构成一个完整的测试数据契约体系。", - "interfaces": [ - { - "description": "表示一个召回率测试用例,包含查询ID、查询文本、相关记忆ID列表、类别和复杂度。", - "interface_type": "struct", - "name": "RecallTestCase", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "query_id", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "query", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "relevant_memory_ids", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": false, - "name": "category", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "complexity", - "param_type": "String" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "包含所有召回率测试用例、记忆库和元数据的完整数据集。", - "interface_type": "struct", - "name": "RecallTestDataset", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "test_cases", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": false, - "name": "memories", - "param_type": "HashMap" - }, - { - "description": null, - "is_optional": false, - "name": "metadata", - "param_type": "DatasetMetadata" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "表示一个有效性测试用例,验证系统能否正确提取事实、分类记忆并评估重要性。", - "interface_type": "struct", - "name": "EffectivenessTestCase", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "test_case_id", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "input_text", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "expected_facts", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": false, - "name": "expected_memory_type", - "param_type": "MemoryType" - }, - { - "description": null, - "is_optional": false, - "name": "expected_importance_score", - "param_type": "u8" - }, - { - "description": null, - "is_optional": false, - "name": "category", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "contains_duplicate", - "param_type": "bool" - }, - { - "description": null, - "is_optional": false, - "name": "requires_update", - "param_type": "bool" - }, - { - "description": null, - "is_optional": true, - "name": "existing_memory_id", - "param_type": "Option" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "包含所有有效性测试用例、现有记忆库和元数据的完整数据集。", - "interface_type": "struct", - "name": "EffectivenessTestDataset", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "test_cases", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": false, - "name": "existing_memories", - "param_type": "HashMap" - }, - { - "description": null, - "is_optional": false, - "name": "metadata", - "param_type": "DatasetMetadata" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "存储数据集的通用元信息,如名称、版本、创建时间和统计信息。", - "interface_type": "struct", - "name": "DatasetMetadata", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "name", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "created_at", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "version", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "total_test_cases", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "total_memories", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "avg_relevant_memories", - "param_type": "f64" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "定义性能测试的配置参数,如记忆库大小、并发用户数、操作类型等。", - "interface_type": "struct", - "name": "PerformanceTestConfig", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "memory_sizes", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": false, - "name": "concurrent_users", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": false, - "name": "test_duration_seconds", - "param_type": "u64" - }, - { - "description": null, - "is_optional": false, - "name": "warmup_seconds", - "param_type": "u64" - }, - { - "description": null, - "is_optional": false, - "name": "operation_types", - "param_type": "Vec" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "泛型结构,用于封装任意类型测试的执行结果,包括执行时间、成功状态和错误信息。", - "interface_type": "struct", - "name": "TestResult", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "test_id", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "test_name", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "result", - "param_type": "T" - }, - { - "description": null, - "is_optional": false, - "name": "start_time", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "end_time", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "duration_seconds", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "success", - "param_type": "bool" - }, - { - "description": null, - "is_optional": true, - "name": "error_message", - "param_type": "Option" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "存储基准测试的详细性能指标,如执行时间、吞吐量、资源使用情况。", - "interface_type": "struct", - "name": "BenchmarkResult", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "benchmark_name", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "iterations", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "avg_execution_time_ms", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "min_execution_time_ms", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "max_execution_time_ms", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "std_deviation_ms", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "throughput_ops_per_sec", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "memory_usage_mb", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "cpu_usage_percent", - "param_type": "f64" - } - ], - "return_type": null, - "visibility": "pub" - }, - { - "description": "存储负载测试结果,关注并发场景下的响应时间、吞吐量和错误率。", - "interface_type": "struct", - "name": "LoadTestResult", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "load_test_name", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "concurrent_users", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "total_requests", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "successful_requests", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "failed_requests", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "avg_response_time_ms", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "p95_response_time_ms", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "p99_response_time_ms", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "throughput_requests_per_sec", - "param_type": "f64" - }, + { + "description": "Sets default empty state data for the dashboard", + "interface_type": "function", + "name": "loadDefaultData", + "parameters": [], + "return_type": "void", + "visibility": "private" + }, + { + "description": "Generates chart.js compatible data structure from time trend data", + "interface_type": "function", + "name": "generateChartData", + "parameters": [], + "return_type": "void", + "visibility": "private" + }, + { + "description": "Calculates average importance score across all memories", + "interface_type": "function", + "name": "calculateAverageQuality", + "parameters": [ { - "description": null, + "description": "Array of memory objects to analyze", "is_optional": false, - "name": "error_rate_percent", - "param_type": "f64" + "name": "memories", + "param_type": "any[]" } ], - "return_type": null, - "visibility": "pub" + "return_type": "number", + "visibility": "private" }, { - "description": "存储可扩展性测试结果,分析系统在不同规模下的性能表现和瓶颈。", - "interface_type": "struct", - "name": "ScalabilityTestResult", + "description": "Counts unique users who have created memories", + "interface_type": "function", + "name": "calculateActiveUsers", "parameters": [ { - "description": null, - "is_optional": false, - "name": "scalability_test_name", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "performance_by_scale", - "param_type": "HashMap" - }, - { - "description": null, + "description": "Array of memory objects to analyze", "is_optional": false, - "name": "linear_scaling_factor", - "param_type": "f64" - }, - { - "description": null, - "is_optional": true, - "name": "bottleneck_point", - "param_type": "Option" + "name": "memories", + "param_type": "any[]" } ], - "return_type": null, - "visibility": "pub" + "return_type": "number", + "visibility": "private" }, { - "description": "表示在特定规模下的性能指标,供ScalabilityTestResult使用。", - "interface_type": "struct", - "name": "ScalePerformance", + "description": "Calculates distribution of memories by type/category", + "interface_type": "function", + "name": "calculateTypeDistribution", "parameters": [ { - "description": null, - "is_optional": false, - "name": "scale", - "param_type": "usize" - }, - { - "description": null, - "is_optional": false, - "name": "avg_response_time_ms", - "param_type": "f64" - }, - { - "description": null, - "is_optional": false, - "name": "throughput_ops_per_sec", - "param_type": "f64" - }, - { - "description": null, + "description": "Array of memory objects to analyze", "is_optional": false, - "name": "resource_utilization_score", - "param_type": "f64" + "name": "memories", + "param_type": "any[]" } ], - "return_type": null, - "visibility": "pub" + "return_type": "Array<{ type: string; count: number; percentage: number }>", + "visibility": "private" }, { - "description": "表示对测试数据集验证的结果,包括有效性、问题列表和统计信息。", - "interface_type": "struct", - "name": "DatasetValidationResult", + "description": "Categorizes memories by quality/importance score ranges", + "interface_type": "function", + "name": "calculateQualityDistribution", "parameters": [ { - "description": null, - "is_optional": false, - "name": "dataset_name", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "validation_time", - "param_type": "String" - }, - { - "description": null, - "is_optional": false, - "name": "is_valid", - "param_type": "bool" - }, - { - "description": null, - "is_optional": false, - "name": "issues", - "param_type": "Vec" - }, - { - "description": null, + "description": "Array of memory objects to analyze", "is_optional": false, - "name": "statistics", - "param_type": "DatasetStatistics" + "name": "memories", + "param_type": "any[]" } ], - "return_type": null, - "visibility": "pub" + "return_type": "Array<{ range: string; count: number; color: string }>", + "visibility": "private" }, { - "description": "表示数据集验证过程中发现的一个问题,包含类型、严重程度和建议修复。", - "interface_type": "struct", - "name": "ValidationIssue", + "description": "Calculates daily memory creation trends over time", + "interface_type": "function", + "name": "calculateTimeTrends", "parameters": [ { - "description": null, - "is_optional": false, - "name": "issue_type", - "param_type": "String" - }, - { - "description": null, + "description": "Array of memory objects to analyze", "is_optional": false, - "name": "description", - "param_type": "String" - }, + "name": "memories", + "param_type": "any[]" + } + ], + "return_type": "Array<{ date: string; count: number }>", + "visibility": "private" + }, + { + "description": "Calculates statistics for individual users based on their memories", + "interface_type": "function", + "name": "calculateUserStats", + "parameters": [ { - "description": null, + "description": "Array of memory objects to analyze", "is_optional": false, - "name": "severity", - "param_type": "String" - }, + "name": "memories", + "param_type": "any[]" + } + ], + "return_type": "Array<{ userId: string; memoryCount: number; avgImportance: number }>", + "visibility": "private" + }, + { + "description": "Returns CSS class for coloring based on percentage value", + "interface_type": "function", + "name": "getPercentageColor", + "parameters": [ { - "description": null, + "description": "Percentage value to determine color for", "is_optional": false, - "name": "affected_items", - "param_type": "Vec" - }, - { - "description": null, - "is_optional": true, - "name": "suggested_fix", - "param_type": "Option" + "name": "percentage", + "param_type": "number" } ], - "return_type": null, - "visibility": "pub" + "return_type": "string", + "visibility": "private" }, { - "description": "存储数据集的统计摘要信息,如项目数量、长度、类别和复杂度分布。", - "interface_type": "struct", - "name": "DatasetStatistics", + "description": "Svelte lifecycle hook that runs after component is mounted to DOM", + "interface_type": "lifecycle", + "name": "onMount", "parameters": [ { - "description": null, + "description": "Callback function to execute after component mounts", "is_optional": false, - "name": "total_items", - "param_type": "usize" - }, + "name": "callback", + "param_type": "Function" + } + ], + "return_type": "void", + "visibility": "public" + } + ], + "responsibilities": [ + "Fetching and processing memory analytics data from the API", + "Calculating statistical metrics including averages, distributions, and trends", + "Visualizing data through various chart types and UI components", + "Handling loading states, errors, and empty data conditions", + "Providing interactive analytics tools and user-facing statistics" + ] + }, + { + "code_dossier": { + "code_purpose": "controller", + "description": "Handles terminal UI input events (keyboard and mouse) and routes them to appropriate application actions. Translates raw input into meaningful user commands.", + "file_path": "examples/cortex-mem-tars/src/events.rs", + "functions": [ + "handle_key_event", + "handle_mouse_event", + "process_user_input" + ], + "importance_score": 0.8, + "interfaces": [ + "handle_key_event", + "process_user_input" + ], + "name": "events.rs", + "source_summary": "use crate::app::{App, FocusArea};\nuse crossterm::event::{Event, KeyCode, KeyEventKind, MouseButton, MouseEvent, MouseEventKind};\n\npub fn handle_key_event(event: Event, app: &mut App) -> Option {\n // 处理鼠标事件\n if let Event::Mouse(mouse) = event {\n return handle_mouse_event(mouse, app);\n }\n\n // Some(input)表示需要处理的输入,None表示不需要处理\n if let Event::Key(key) = event {\n if key.kind == KeyEventKind::Press {\n match key.code {\n KeyCode::Enter => {\n if app.focus_area == FocusArea::Input && !app.current_input.trim().is_empty() {\n let input = app.current_input.clone();\n app.current_input.clear();\n app.reset_cursor_to_end();\n app.is_processing = true;\n Some(input) // 返回输入内容给上层处理\n } else {\n None\n }\n }\n KeyCode::Char(c) => {\n if !app.is_processing\n && !app.is_shutting_down\n && app.focus_area == FocusArea::Input\n {\n app.insert_char_at_cursor(c);\n }\n None\n }\n KeyCode::Backspace => {\n if !app.is_processing\n && !app.is_shutting_down\n && app.focus_area == FocusArea::Input\n {\n app.delete_char_at_cursor();\n }\n None\n }\n KeyCode::Left => {\n if !app.is_processing\n && !app.is_shutting_down\n && app.focus_area == FocusArea::Input\n {\n app.move_cursor_left();\n }\n None\n }\n KeyCode::Right => {\n if !app.is_processing\n && !app.is_shutting_down\n && app.focus_area == FocusArea::Input\n {\n app.move_cursor_right();\n }\n None\n }\n KeyCode::Up => {\n // 上键:向后滚动(查看更新内容)\n match app.focus_area {\n FocusArea::Logs => {\n app.scroll_logs_backward();\n }\n FocusArea::Conversation => {\n app.scroll_conversations_backward();\n }\n FocusArea::Input => {}\n }\n None\n }\n KeyCode::Down => {\n // 下键:向前滚动(查看更早内容)\n match app.focus_area {\n FocusArea::Logs => {\n app.scroll_logs_forward();\n }\n FocusArea::Conversation => {\n app.scroll_conversations_forward();\n }\n FocusArea::Input => {}\n }\n None\n }\n KeyCode::Tab => {\n // 切换焦点\n let _old_focus = app.focus_area;\n app.next_focus();\n None\n }\n KeyCode::Home => {\n match app.focus_area {\n FocusArea::Logs => {\n // 滚动到最旧的日志(设置一个较大的偏移量)\n app.log_scroll_offset = app.logs.len().saturating_sub(1);\n app.user_scrolled_logs = true;\n }\n FocusArea::Conversation => {\n // 滚动到最旧的对话(设置一个较大的偏移量)\n let total_lines = app.conversations.len() * 3;\n app.conversation_scroll_offset = total_lines.saturating_sub(1);\n app.user_scrolled_conversations = true;\n }\n FocusArea::Input => {\n // 将光标移动到输入框开头\n app.cursor_position = 0;\n }\n }\n None\n }\n KeyCode::End => {\n match app.focus_area {\n FocusArea::Logs => {\n // 滚动到最新的日志\n app.scroll_logs_to_bottom();\n }\n FocusArea::Conversation => {\n // 滚动到最新的对话\n app.scroll_conversations_to_bottom();\n }\n FocusArea::Input => {\n // 将光标移动到输入框末尾\n app.reset_cursor_to_end();\n }\n }\n None\n }\n KeyCode::Esc => {\n app.should_quit = true;\n app.is_shutting_down = true;\n Some(\"/quit\".to_string()) // 模拟quit命令\n }\n _ => None,\n }\n } else {\n None\n }\n } else {\n None\n }\n}\n\n/// 处理鼠标事件\nfn handle_mouse_event(mouse: MouseEvent, app: &mut App) -> Option {\n match mouse.kind {\n MouseEventKind::Down(MouseButton::Left) => {\n // 左键点击时更新焦点区域\n // 这里可以根据鼠标位置判断点击了哪个区域\n // 简化处理:如果鼠标在左边区域,设置为输入或对话焦点;如果在右边区域,设置为日志焦点\n // 由于我们没有详细的坐标信息,这里只是简化处理\n None\n }\n MouseEventKind::ScrollUp => {\n // 鼠标向上滚动\n match app.focus_area {\n FocusArea::Logs => {\n app.scroll_logs_backward();\n }\n FocusArea::Conversation => {\n app.scroll_conversations_backward();\n }\n FocusArea::Input => {}\n }\n None\n }\n MouseEventKind::ScrollDown => {\n // 鼠标向下滚动\n match app.focus_area {\n FocusArea::Logs => {\n app.scroll_logs_forward();\n }\n FocusArea::Conversation => {\n app.scroll_conversations_forward();\n }\n FocusArea::Input => {}\n }\n None\n }\n MouseEventKind::Drag(MouseButton::Left) => {\n // 鼠标左键拖拽 - 这里我们不需要特别处理,终端默认支持文本选择\n None\n }\n _ => None,\n }\n}\n\npub fn process_user_input(input: String, app: &mut App) -> bool {\n // true表示是quit命令,false表示普通输入\n // 检查是否为退出命令\n let is_quit = input.trim() == \"/quit\";\n if is_quit {\n app.should_quit = true;\n }\n is_quit\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 18.0, + "lines_of_code": 197, + "number_of_classes": 0, + "number_of_functions": 3 + }, + "dependencies": [ + { + "dependency_type": "module", + "is_external": false, + "line_number": 1, + "name": "crate::app", + "path": "crate::app", + "version": null + }, + { + "dependency_type": "library", + "is_external": true, + "line_number": 2, + "name": "crossterm", + "path": "crossterm::event", + "version": null + } + ], + "detailed_description": "This controller component manages all user input events in a terminal-based application using Crossterm. It processes keyboard and mouse events, updating the application state accordingly. The component distinguishes between different focus areas (Input, Logs, Conversation) and handles navigation, text input, scrolling, and application lifecycle commands. Key events like Enter, arrow keys, Home/End, and Esc are mapped to specific behaviors. Mouse scrolling is supported when focused on scrollable areas. The component returns processed input to the caller via Option, enabling command execution (like '/quit'). It serves as an intermediary between low-level terminal events and high-level application logic, effectively decoupling input handling from business logic.", + "interfaces": [ + { + "description": "Processes a terminal event and returns Some(input) if input should be processed by upper layers, None otherwise", + "interface_type": "function", + "name": "handle_key_event", + "parameters": [ { - "description": null, + "description": "The raw terminal event to process", "is_optional": false, - "name": "valid_items", - "param_type": "usize" + "name": "event", + "param_type": "Event" }, { - "description": null, + "description": "Mutable reference to the application state", "is_optional": false, - "name": "invalid_items", - "param_type": "usize" - }, + "name": "app", + "param_type": "App" + } + ], + "return_type": "Option", + "visibility": "public" + }, + { + "description": null, + "interface_type": "function", + "name": "handle_mouse_event", + "parameters": [ { "description": null, "is_optional": false, - "name": "avg_item_length", - "param_type": "f64" + "name": "mouse", + "param_type": "MouseEvent" }, { "description": null, "is_optional": false, - "name": "category_distribution", - "param_type": "HashMap" - }, + "name": "app", + "param_type": "App" + } + ], + "return_type": "Option", + "visibility": "private" + }, + { + "description": "Processes submitted user input and returns true if it's a quit command", + "interface_type": "function", + "name": "process_user_input", + "parameters": [ { "description": null, "is_optional": false, - "name": "complexity_distribution", - "param_type": "HashMap" + "name": "input", + "param_type": "String" }, { "description": null, "is_optional": false, - "name": "memory_type_distribution", - "param_type": "HashMap" + "name": "app", + "param_type": "App" } ], - "return_type": null, - "visibility": "pub" + "return_type": "bool", + "visibility": "public" } ], "responsibilities": [ - "定义召回率测试的数据结构(测试用例、数据集)", - "定义有效性测试的数据结构(测试用例、数据集)", - "定义性能、负载、可扩展性等非功能性测试的结果度量模型", - "提供测试数据集的元信息和验证结果的数据结构", - "通过序列化支持确保测试数据的持久化和跨系统交换" + "Handle and route keyboard input events based on current application focus and state", + "Process mouse events including scrolling and clicks for UI interaction", + "Manage user input lifecycle including text entry, cursor movement, and deletion", + "Translate user input into application commands and state changes", + "Coordinate focus management and scrolling behavior across different UI components" ] }, { "code_dossier": { - "code_purpose": "specificfeature", - "description": "实验室数据集成模块,负责集成实验室真实数据并生成多样化测试数据集", - "file_path": "examples/cortex-mem-evaluation/src/dataset/lab_data_integration.rs", + "code_purpose": "widget", + "description": "UI 绘制函数,负责渲染基于终端的用户界面,包含对话历史、输入框和系统日志三个主要区域,并支持焦点切换、滚动浏览和实时反馈。", + "file_path": "examples/cortex-mem-tars/src/ui.rs", "functions": [ - "new", - "load_datasets", - "generate_recall_dataset_from_lab", - "generate_effectiveness_dataset_from_lab", - "load_or_generate_samples", - "load_json_samples", - "load_csv_samples", - "load_text_samples", - "create_memories_from_samples", - "generate_test_cases_with_semantic_relations", - "generate_effectiveness_test_cases", - "create_existing_memories", - "select_relevant_memories_by_keywords", - "extract_keywords_from_text", - "is_stop_word", - "calculate_complexity", - "determine_memory_type", - "calculate_hash", - "calculate_importance_score", - "calculate_dataset_quality" + "draw_ui" ], "importance_score": 0.8, "interfaces": [ - "LabDataSource", - "LabDataset", - "QualityMetrics", - "LabDataIntegrator", - "LabDataSample", - "Annotations", - "Relation", - "RecallTestDataset", - "EffectivenessTestDataset", - "DatasetMetadata", - "Memory", - "MemoryMetadata", - "MemoryType" + "draw_ui" ], - "name": "lab_data_integration.rs", - "source_summary": "//! 实验室数据集成模块\n//! \n//! 集成实验室真实数据,创建丰富多样的测试数据集\n\nuse anyhow::{Result, Context};\nuse serde::{Deserialize, Serialize};\nuse std::collections::{HashMap, HashSet};\nuse std::fs;\nuse std::path::Path;\nuse tracing::{info, warn, debug};\n\nuse super::types::*;\nuse super::types::RecallTestCase;\n\n/// 实验室数据源配置\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LabDataSource {\n /// 数据源名称\n pub name: String,\n /// 数据文件路径\n pub path: String,\n /// 数据格式:json, csv, txt\n pub format: String,\n /// 数据领域:conversation, technical, business, medical, etc.\n pub domain: String,\n /// 数据质量评分(1-10)\n pub quality_score: u8,\n /// 是否包含标注信息\n pub has_annotations: bool,\n}\n\n/// 实验室数据集\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LabDataset {\n /// 数据集名称\n pub name: String,\n /// 数据源列表\n pub sources: Vec,\n /// 总样本数\n pub total_samples: usize,\n /// 领域分布\n pub domain_distribution: HashMap,\n /// 平均文本长度\n pub avg_text_length: f64,\n /// 数据质量指标\n pub quality_metrics: QualityMetrics,\n}\n\n/// 数据质量指标\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct QualityMetrics {\n /// 完整性(0-1)\n pub completeness: f64,\n /// 一致性(0-1)\n pub consistency: f64,\n /// 准确性(0-1)\n pub accuracy: f64,\n /// 多样性(0-1)\n pub diversity: f64,\n /// 相关性(0-1)\n pub relevance: f64,\n}\n\n/// 实验室数据集成器\npub struct LabDataIntegrator {\n /// 实验室数据集\n pub datasets: Vec,\n /// 数据缓存\n pub data_cache: HashMap>,\n}\n\n/// 实验室数据样本\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LabDataSample {\n /// 样本ID\n pub id: String,\n /// 原始文本\n pub text: String,\n /// 领域\n pub domain: String,\n /// 子领域\n pub subdomain: Option,\n /// 关键词列表\n pub keywords: Vec,\n /// 实体列表\n pub entities: Vec,\n /// 情感倾向(-1到1)\n pub sentiment: Option,\n /// 复杂度评分(1-10)\n pub complexity: u8,\n /// 标注信息(如果有)\n pub annotations: Option,\n /// 元数据\n pub metadata: HashMap,\n}\n\n/// 标注信息\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct Annotations {\n /// 事实列表\n pub facts: Vec,\n /// 主题列表\n pub topics: Vec,\n /// 意图分类\n pub intent: Option,\n /// 情感标签\n pub sentiment_label: Option,\n /// 关系标注\n pub relations: Vec,\n}\n\n/// 关系标注\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct Relation {\n pub subject: String,\n pub predicate: String,\n pub object: String,\n pub confidence: f32,\n}\n\nimpl LabDataIntegrator {\n /// 创建新的实验室数据集成器\n pub fn new() -> Self {\n Self {\n datasets: Vec::new(),\n data_cache: HashMap::new(),\n }\n }\n \n /// 加载实验室数据集配置\n pub fn load_datasets(&mut self, config_path: &str) -> Result<()> {\n let content = fs::read_to_string(config_path)\n .context(format!(\"读取数据集配置文件失败: {}\", config_path))?;\n \n let datasets: Vec = serde_json::from_str(&content)\n .context(\"解析数据集配置JSON失败\")?;\n \n self.datasets = datasets;\n info!(\"加载了 {} 个实验室数据集\", self.datasets.len());\n \n Ok(())\n }\n \n /// 从实验室数据生成召回率测试数据集\n pub async fn generate_recall_dataset_from_lab(\n &mut self,\n dataset_name: &str,\n num_queries: usize,\n avg_relevant_per_query: usize,\n ) -> Result {\n info!(\"从实验室数据生成召回率测试数据集: {}\", dataset_name);\n \n // 1. 加载或生成实验室数据样本\n let samples = self.load_or_generate_samples(dataset_name).await?;\n \n if samples.is_empty() {\n anyhow::bail!(\"没有可用的实验室数据样本\");\n }\n \n info!(\"加载了 {} 个实验室数据样本\", samples.len());\n \n // 2. 创建记忆库(从样本中提取)\n let memories = self.create_memories_from_samples(&samples, samples.len() / 2)?;\n \n // 3. 生成查询和相关性标注\n let test_cases = self.generate_test_cases_with_semantic_relations(\n &samples,\n &memories,\n num_queries,\n avg_relevant_per_query,\n )?;\n \n // 4. 创建数据集元数据\n let metadata = DatasetMetadata {\n name: format!(\"lab_recall_dataset_{}\", dataset_name),\n created_at: chrono::Utc::now().to_rfc3339(),\n version: \"1.0.0\".to_string(),\n total_test_cases: test_cases.len(),\n total_memories: memories.len(),\n avg_relevant_memories: avg_relevant_per_query as f64,\n };\n \n let dataset = RecallTestDataset {\n test_cases,\n memories,\n metadata,\n };\n \n info!(\"实验室召回率数据集生成完成: {}个测试用例, {}个记忆\",\n dataset.test_cases.len(), dataset.memories.len());\n \n Ok(dataset)\n }\n \n /// 从实验室数据生成有效性测试数据集\n pub async fn generate_effectiveness_dataset_from_lab(\n &mut self,\n dataset_name: &str,\n num_cases: usize,\n ) -> Result {\n info!(\"从实验室数据生成有效性测试数据集: {}\", dataset_name);\n \n // 1. 加载实验室数据样本\n let samples = self.load_or_generate_samples(dataset_name).await?;\n \n if samples.is_empty() {\n anyhow::bail!(\"没有可用的实验室数据样本\");\n }\n \n // 2. 生成测试用例\n let test_cases = self.generate_effectiveness_test_cases(&samples, num_cases)?;\n \n // 3. 创建现有记忆库\n let existing_memories = self.create_existing_memories(&samples, num_cases / 3)?;\n \n // 4. 创建数据集元数据\n let metadata = DatasetMetadata {\n name: format!(\"lab_effectiveness_dataset_{}\", dataset_name),\n created_at: chrono::Utc::now().to_rfc3339(),\n version: \"1.0.0\".to_string(),\n total_test_cases: test_cases.len(),\n total_memories: existing_memories.len(),\n avg_relevant_memories: 0.0,\n };\n \n let dataset = EffectivenessTestDataset {\n test_cases,\n existing_memories,\n metadata,\n };\n \n info!(\"实验室有效性数据集生成完成: {}个测试用例, {}个现有记忆\",\n dataset.test_cases.len(), dataset.existing_memories.len());\n \n Ok(dataset)\n }\n \n /// 加载或生成实验室数据样本\n async fn load_or_generate_samples(&mut self, dataset_name: &str) -> Result> {\n // 检查缓存\n if let Some(cached) = self.data_cache.get(dataset_name) {\n info!(\"使用缓存的实验室数据样本: {}\", dataset_name);\n return Ok(cached.clone());\n }\n \n // 查找数据集配置\n let dataset_config = self.datasets.iter()\n .find(|d| d.name == dataset_name)\n .context(format!(\"未找到数据集配置: {}\", dataset_name))?;\n \n let mut all_samples = Vec::new();\n \n // 从每个数据源加载数据\n for source in &dataset_config.sources {\n info!(\"从数据源加载数据: {} ({})\", source.name, source.format);\n \n let samples = match source.format.as_str() {\n \"json\" => self.load_json_samples(&source.path, &source.domain).await?,\n \"csv\" => self.load_csv_samples(&source.path, &source.domain).await?,\n \"txt\" => self.load_text_samples(&source.path, &source.domain).await?,\n _ => {\n warn!(\"不支持的数据格式: {}, 跳过\", source.format);\n continue;\n }\n };\n \n info!(\"从 {} 加载了 {} 个样本\", source.name, samples.len());\n all_samples.extend(samples);\n }\n \n // 缓存数据\n self.data_cache.insert(dataset_name.to_string(), all_samples.clone());\n \n Ok(all_samples)\n }\n \n /// 从JSON文件加载样本\n async fn load_json_samples(&self, path: &str, domain: &str) -> Result> {\n let content = fs::read_to_string(path)\n .context(format!(\"读取JSON文件失败: {}\", path))?;\n \n // 尝试解析为LabDataSample数组\n if let Ok(samples) = serde_json::from_str::>(&content) {\n return Ok(samples);\n }\n \n // 如果失败,尝试通用JSON格式\n let json_value: serde_json::Value = serde_json::from_str(&content)\n .context(\"解析JSON失败\")?;\n \n let mut samples = Vec::new();\n \n // 处理不同的JSON结构\n match json_value {\n serde_json::Value::Array(arr) => {\n for (i, item) in arr.iter().enumerate() {\n if let Some(text) = item.get(\"text\").and_then(|v| v.as_str()) {\n let sample = self.create_sample_from_json(item, i, text, domain);\n samples.push(sample);\n }\n }\n }\n serde_json::Value::Object(obj) => {\n if let Some(text) = obj.get(\"text\").and_then(|v| v.as_str()) {\n let sample = self.create_sample_from_json(&serde_json::Value::Object(obj.clone()), 0, text, domain);\n samples.push(sample);\n }\n }\n _ => {\n warn!(\"不支持的JSON格式: {}\", path);\n }\n }\n \n Ok(samples)\n }\n \n /// 从JSON值创建样本\n fn create_sample_from_json(\n &self,\n json_value: &serde_json::Value,\n index: usize,\n text: &str,\n domain: &str,\n ) -> LabDataSample {\n let mut metadata = HashMap::new();\n \n // 提取可能的字段\n if let serde_json::Value::Object(obj) = json_value {\n for (key, value) in obj {\n if key != \"text\" {\n metadata.insert(key.clone(), value.clone());\n }\n }\n }\n \n // 提取关键词(如果存在)\n let keywords = metadata.get(\"keywords\")\n .and_then(|v| v.as_array())\n .map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect())\n .unwrap_or_else(|| self.extract_keywords_from_text(text));\n \n // 提取实体(如果存在)\n let entities = metadata.get(\"entities\")\n .and_then(|v| v.as_array())\n .map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect())\n .unwrap_or_else(Vec::new);\n \n // 计算复杂度\n let complexity = self.calculate_complexity(text);\n \n LabDataSample {\n id: format!(\"lab_sample_{:06}\", index),\n text: text.to_string(),\n domain: domain.to_string(),\n subdomain: None,\n keywords,\n entities,\n sentiment: None,\n complexity,\n annotations: None,\n metadata,\n }\n }\n \n /// 从CSV文件加载样本\n async fn load_csv_samples(&self, path: &str, domain: &str) -> Result> {\n let mut rdr = csv::Reader::from_path(path)\n .context(format!(\"打开CSV文件失败: {}\", path))?;\n \n let mut samples = Vec::new();\n \n for (i, result) in rdr.records().enumerate() {\n let record = result.context(\"读取CSV记录失败\")?;\n \n // 假设第一列是文本\n if let Some(text) = record.get(0) {\n let sample = LabDataSample {\n id: format!(\"csv_sample_{:06}\", i),\n text: text.to_string(),\n domain: domain.to_string(),\n subdomain: None,\n keywords: self.extract_keywords_from_text(text),\n entities: Vec::new(),\n sentiment: None,\n complexity: self.calculate_complexity(text),\n annotations: None,\n metadata: HashMap::new(),\n };\n samples.push(sample);\n }\n }\n \n Ok(samples)\n }\n \n /// 从文本文件加载样本\n async fn load_text_samples(&self, path: &str, domain: &str) -> Result> {\n let content = fs::read_to_string(path)\n .context(format!(\"读取文本文件失败: {}\", path))?;\n \n // 按段落分割\n let paragraphs: Vec<&str> = content.split(\"\\n\\n\")\n .filter(|p| !p.trim().is_empty())\n .collect();\n \n let mut samples = Vec::new();\n \n for (i, paragraph) in paragraphs.iter().enumerate() {\n let text = paragraph.trim();\n if text.len() > 10 { // 忽略太短的段落\n let sample = LabDataSample {\n id: format!(\"txt_sample_{:06}\", i),\n text: text.to_string(),\n domain: domain.to_string(),\n subdomain: None,\n keywords: self.extract_keywords_from_text(text),\n entities: Vec::new(),\n sentiment: None,\n complexity: self.calculate_complexity(text),\n annotations: None,\n metadata: HashMap::new(),\n };\n samples.push(sample);\n }\n }\n \n Ok(samples)\n }\n \n /// 从样本创建记忆\n fn create_memories_from_samples(\n &self,\n samples: &[LabDataSample],\n num_memories: usize,\n ) -> Result> {\n use cortex_mem_core::{Memory, MemoryMetadata, MemoryType};\n use std::collections::HashMap as StdHashMap;\n \n let mut memories = StdHashMap::new();\n \n // 选择样本创建记忆\n let selected_samples: Vec<&LabDataSample> = samples\n .iter()\n .take(num_memories.min(samples.len()))\n .collect();\n \n for (i, sample) in selected_samples.iter().enumerate() {\n let memory_id = format!(\"lab_memory_{:06}\", i);\n \n // 确定记忆类型\n let memory_type = self.determine_memory_type(&sample.domain, &sample.text);\n \n let metadata = MemoryMetadata {\n user_id: Some(\"lab_user\".to_string()),\n agent_id: None,\n run_id: None,\n actor_id: None,\n role: None,\n memory_type,\n hash: self.calculate_hash(&sample.text),\n importance_score: self.calculate_importance_score(sample),\n entities: sample.entities.clone(),\n topics: sample.keywords.clone(),\n custom: StdHashMap::new(),\n };\n \n let memory = Memory {\n id: memory_id.clone(),\n content: sample.text.clone(),\n embedding: vec![], // 实际使用时需要生成嵌入\n metadata,\n created_at: chrono::Utc::now(),\n updated_at: chrono::Utc::now(),\n };\n \n memories.insert(memory_id, memory);\n }\n \n Ok(memories)\n }\n \n /// 生成带有语义关联的测试用例\n fn generate_test_cases_with_semantic_relations(\n &self,\n samples: &[LabDataSample],\n memories: &HashMap,\n num_queries: usize,\n avg_relevant: usize,\n ) -> Result> {\n let mut test_cases = Vec::new();\n \n // 选择查询样本 - 放宽条件,确保有足够的查询\n let mut query_samples: Vec<&LabDataSample> = samples\n .iter()\n .filter(|s| s.complexity >= 3) // 降低复杂度要求\n .take(num_queries)\n .collect();\n \n if query_samples.is_empty() {\n warn!(\"没有符合条件的查询样本,使用所有样本作为查询\");\n // 如果没有符合条件的样本,使用前num_queries个样本\n query_samples = samples\n .iter()\n .take(num_queries)\n .collect();\n }\n \n info!(\"选择了 {} 个查询样本\", query_samples.len());\n \n for (i, sample) in query_samples.iter().enumerate() {\n let query_id = format!(\"lab_query_{:06}\", i);\n \n // 基于关键词匹配选择相关记忆\n let relevant_memory_ids = self.select_relevant_memories_by_keywords(\n &sample.keywords,\n memories,\n avg_relevant,\n );\n \n info!(\"查询 {}: 关键词={:?}, 找到相关记忆={:?}\", \n query_id, sample.keywords, relevant_memory_ids);\n \n // 确定查询类别和复杂度\n let category = sample.domain.clone();\n let complexity = match sample.complexity {\n 1..=3 => \"simple\",\n 4..=7 => \"medium\",\n _ => \"complex\",\n }.to_string();\n \n let test_case = RecallTestCase {\n query_id,\n query: sample.text.clone(),\n relevant_memory_ids,\n category,\n complexity,\n };\n \n test_cases.push(test_case);\n }\n \n info!(\"生成了 {} 个测试用例\", test_cases.len());\n Ok(test_cases)\n }\n \n /// 生成有效性测试用例\n fn generate_effectiveness_test_cases(\n &self,\n samples: &[LabDataSample],\n num_cases: usize,\n ) -> Result> {\n use cortex_mem_core::MemoryType;\n \n let mut test_cases = Vec::new();\n \n // 选择测试样本\n let test_samples: Vec<&LabDataSample> = samples\n .iter()\n .take(num_cases.min(samples.len()))\n .collect();\n \n for (i, sample) in test_samples.iter().enumerate() {\n let test_case_id = format!(\"lab_effectiveness_{:06}\", i);\n \n // 确定预期记忆类型\n let expected_memory_type = self.determine_memory_type(&sample.domain, &sample.text);\n \n // 生成预期事实(从关键词或标注中提取)\n let expected_facts = if let Some(ann) = &sample.annotations {\n ann.facts.clone()\n } else {\n sample.keywords.iter()\n .take(3)\n .map(|kw| format!(\"包含关键词: {}\", kw))\n .collect()\n };\n \n // 计算预期重要性评分\n let expected_importance_score = self.calculate_importance_score(sample) as u8;\n \n // 随机决定是否包含重复内容(20%概率)\n let contains_duplicate = i % 5 == 0;\n \n // 随机决定是否需要更新(30%概率)\n let requires_update = i % 3 == 0;\n let existing_memory_id = if requires_update {\n Some(format!(\"existing_memory_{:06}\", i))\n } else {\n None\n };\n \n let test_case = EffectivenessTestCase {\n test_case_id,\n input_text: sample.text.clone(),\n expected_facts,\n expected_memory_type,\n expected_importance_score,\n category: sample.domain.clone(),\n contains_duplicate,\n requires_update,\n existing_memory_id,\n };\n \n test_cases.push(test_case);\n }\n \n Ok(test_cases)\n }\n \n /// 创建现有记忆库\n fn create_existing_memories(\n &self,\n samples: &[LabDataSample],\n num_memories: usize,\n ) -> Result> {\n // 使用与召回率数据集相同的方法\n self.create_memories_from_samples(samples, num_memories)\n }\n \n /// 基于关键词选择相关记忆\n fn select_relevant_memories_by_keywords(\n &self,\n query_keywords: &[String],\n memories: &HashMap,\n target_count: usize,\n ) -> Vec {\n let mut scored_memories: Vec<(String, usize)> = Vec::new();\n \n for (memory_id, memory) in memories {\n // 计算关键词匹配分数\n let mut score = 0;\n for keyword in query_keywords {\n if memory.content.contains(keyword) {\n score += 1;\n }\n // 检查metadata中的topics\n if memory.metadata.topics.contains(keyword) {\n score += 2; // topics中的匹配权重更高\n }\n }\n \n if score > 0 {\n scored_memories.push((memory_id.clone(), score));\n }\n }\n \n // 按分数排序\n scored_memories.sort_by(|a, b| b.1.cmp(&a.1));\n \n // 选择前target_count个\n let mut selected: Vec = scored_memories.iter()\n .take(target_count.min(scored_memories.len()))\n .map(|(id, _)| id.clone())\n .collect();\n \n // 如果没有找到匹配的记忆,返回随机记忆作为回退\n if selected.is_empty() && !memories.is_empty() {\n warn!(\"没有找到关键词匹配的记忆,返回随机记忆作为回退\");\n let memory_ids: Vec = memories.keys().cloned().collect();\n let mut rng = rand::thread_rng();\n let count = target_count.min(memory_ids.len());\n \n // 随机选择记忆\n use rand::seq::SliceRandom;\n selected = memory_ids.choose_multiple(&mut rng, count).cloned().collect();\n }\n \n selected\n }\n \n /// 从文本提取关键词\n fn extract_keywords_from_text(&self, text: &str) -> Vec {\n // 简化实现:提取名词性词汇\n let words: Vec<&str> = text.split_whitespace().collect();\n let mut keywords = Vec::new();\n \n // 选择长度适中的单词作为关键词\n for word in words {\n let clean_word = word.trim_matches(|c: char| !c.is_alphanumeric());\n if clean_word.len() >= 4 && clean_word.len() <= 20 {\n // 简单过滤:排除常见停用词\n let lower_word = clean_word.to_lowercase();\n if !self.is_stop_word(&lower_word) {\n keywords.push(clean_word.to_string());\n }\n }\n }\n \n // 去重并限制数量\n let mut unique_keywords: Vec = keywords.into_iter().collect();\n unique_keywords.sort();\n unique_keywords.dedup();\n \n unique_keywords.into_iter().take(10).collect()\n }\n \n /// 判断是否为停用词\n fn is_stop_word(&self, word: &str) -> bool {\n let stop_words = [\n \"the\", \"and\", \"that\", \"for\", \"with\", \"this\", \"from\", \"have\", \"what\",\n \"which\", \"about\", \"would\", \"could\", \"should\", \"will\", \"can\", \"may\",\n \"might\", \"must\", \"shall\", \"a\", \"an\", \"in\", \"on\", \"at\", \"to\", \"of\",\n \"by\", \"as\", \"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\",\n ];\n \n stop_words.contains(&word)\n }\n \n /// 计算文本复杂度\n fn calculate_complexity(&self, text: &str) -> u8 {\n let word_count = text.split_whitespace().count();\n let sentence_count = text.split(|c| c == '.' || c == '!' || c == '?').count();\n let avg_sentence_length = if sentence_count > 0 {\n word_count as f64 / sentence_count as f64\n } else {\n 0.0\n };\n \n // 基于句子长度和词汇多样性评分\n let mut score = 1;\n \n if word_count > 50 { score += 2; }\n if word_count > 100 { score += 2; }\n if avg_sentence_length > 15.0 { score += 2; }\n if avg_sentence_length > 25.0 { score += 2; }\n \n // 检查是否有复杂结构\n let complex_indicators = [\"however\", \"although\", \"despite\", \"furthermore\", \"therefore\"];\n for indicator in &complex_indicators {\n if text.to_lowercase().contains(indicator) {\n score += 1;\n }\n }\n \n score.min(10) as u8\n }\n \n /// 确定记忆类型\n fn determine_memory_type(&self, domain: &str, text: &str) -> cortex_mem_core::MemoryType {\n use cortex_mem_core::MemoryType;\n \n match domain.to_lowercase().as_str() {\n \"conversation\" | \"chat\" | \"dialogue\" => MemoryType::Conversational,\n \"technical\" | \"procedure\" | \"tutorial\" => MemoryType::Procedural,\n \"fact\" | \"knowledge\" | \"encyclopedia\" => MemoryType::Factual,\n \"concept\" | \"theory\" | \"semantic\" => MemoryType::Semantic,\n \"event\" | \"experience\" | \"story\" => MemoryType::Episodic,\n \"personal\" | \"preference\" | \"profile\" => MemoryType::Personal,\n _ => {\n // 基于内容启发式判断\n if text.contains(\"how to\") || text.contains(\"step\") || text.contains(\"procedure\") {\n MemoryType::Procedural\n } else if text.contains(\"I prefer\") || text.contains(\"my favorite\") {\n MemoryType::Personal\n } else if text.contains(\"event\") || text.contains(\"happened\") {\n MemoryType::Episodic\n } else {\n MemoryType::Conversational\n }\n }\n }\n }\n \n /// 计算哈希值\n fn calculate_hash(&self, content: &str) -> String {\n use sha2::{Digest, Sha256};\n let mut hasher = Sha256::new();\n hasher.update(content.as_bytes());\n format!(\"{:x}\", hasher.finalize())\n }\n \n /// 计算重要性评分\n fn calculate_importance_score(&self, sample: &LabDataSample) -> f32 {\n let mut score = 5.0; // 基础分\n \n // 基于复杂度加分\n score += sample.complexity as f32 * 0.3;\n \n // 基于关键词数量加分\n if !sample.keywords.is_empty() {\n score += (sample.keywords.len() as f32).min(5.0) * 0.2;\n }\n \n // 基于文本长度(适中的长度更重要)\n let text_len = sample.text.len();\n if text_len > 50 && text_len < 500 {\n score += 2.0;\n }\n \n // 基于领域重要性\n match sample.domain.as_str() {\n \"medical\" | \"safety\" | \"security\" => score += 3.0,\n \"technical\" | \"business\" => score += 2.0,\n \"personal\" | \"preference\" => score += 1.0,\n _ => {}\n }\n \n score.min(10.0).max(1.0)\n }\n \n /// 计算数据集质量\n fn calculate_dataset_quality(&self, samples: &[LabDataSample]) -> f64 {\n if samples.is_empty() {\n return 0.0;\n }\n \n let mut total_score = 0.0;\n \n for sample in samples {\n let mut sample_score = 0.0;\n \n // 文本质量\n if sample.text.len() > 10 {\n sample_score += 0.3;\n }\n \n // 关键词质量\n if !sample.keywords.is_empty() {\n sample_score += 0.2;\n }\n \n // 复杂度适中\n if sample.complexity >= 3 && sample.complexity <= 8 {\n sample_score += 0.2;\n }\n \n // 领域明确性\n if !sample.domain.is_empty() {\n sample_score += 0.2;\n }\n \n // 标注信息(如果有)\n if sample.annotations.is_some() {\n sample_score += 0.1;\n }\n \n total_score += sample_score;\n }\n \n total_score / samples.len() as f64\n }\n}\n\n/// 创建实验室数据集配置示例\npub fn create_example_lab_config() -> LabDataset {\n LabDataset {\n name: \"example_lab_dataset\".to_string(),\n sources: vec![\n LabDataSource {\n name: \"conversation_samples\".to_string(),\n path: \"data/lab/conversations.json\".to_string(),\n format: \"json\".to_string(),\n domain: \"conversation\".to_string(),\n quality_score: 8,\n has_annotations: true,\n },\n LabDataSource {\n name: \"technical_docs\".to_string(),\n path: \"data/lab/technical_docs.csv\".to_string(),\n format: \"csv\".to_string(),\n domain: \"technical\".to_string(),\n quality_score: 9,\n has_annotations: false,\n },\n LabDataSource {\n name: \"business_reports\".to_string(),\n path: \"data/lab/business_reports.txt\".to_string(),\n format: \"txt\".to_string(),\n domain: \"business\".to_string(),\n quality_score: 7,\n has_annotations: false,\n },\n ],\n total_samples: 1000,\n domain_distribution: {\n let mut map = HashMap::new();\n map.insert(\"conversation\".to_string(), 400);\n map.insert(\"technical\".to_string(), 300);\n map.insert(\"business\".to_string(), 300);\n map\n },\n avg_text_length: 150.5,\n quality_metrics: QualityMetrics {\n completeness: 0.85,\n consistency: 0.90,\n accuracy: 0.88,\n diversity: 0.75,\n relevance: 0.82,\n },\n }\n}\n\n/// 生成实验室数据集的公共接口\npub async fn generate_lab_dataset(\n dataset_type: &str,\n dataset_name: &str,\n output_dir: &std::path::Path,\n size: usize,\n) -> Result<()> {\n let mut integrator = LabDataIntegrator::new();\n \n // 创建示例配置(实际使用时应该从文件加载)\n let example_config = create_example_lab_config();\n integrator.datasets.push(example_config);\n \n match dataset_type.to_lowercase().as_str() {\n \"recall\" => {\n let dataset = integrator.generate_recall_dataset_from_lab(\n dataset_name,\n size,\n 3, // avg_relevant_per_query\n ).await?;\n \n let output_path = output_dir.join(\"test_cases/lab_recall_dataset.json\");\n save_dataset_to_file(&dataset, &output_path)?;\n }\n \"effectiveness\" => {\n let dataset = integrator.generate_effectiveness_dataset_from_lab(\n dataset_name,\n size,\n ).await?;\n \n let output_path = output_dir.join(\"test_cases/lab_effectiveness_dataset.json\");\n save_dataset_to_file(&dataset, &output_path)?;\n }\n _ => {\n anyhow::bail!(\"未知的数据集类型: {}\", dataset_type);\n }\n }\n \n Ok(())\n}\n\n/// 保存数据集到文件\nfn save_dataset_to_file(\n dataset: &T,\n output_path: &std::path::Path,\n) -> Result<()> {\n let json = serde_json::to_string_pretty(dataset)\n .context(\"序列化数据集失败\")?;\n \n // 确保目录存在\n if let Some(parent) = output_path.parent() {\n fs::create_dir_all(parent)\n .context(format!(\"创建目录失败: {:?}\", parent))?;\n }\n \n fs::write(output_path, json)\n .context(format!(\"写入数据集文件失败: {:?}\", output_path))?;\n \n info!(\"实验室数据集已保存到: {:?}\", output_path);\n Ok(())\n}" + "name": "ui.rs", + "source_summary": "use ratatui::{\n Frame,\n layout::{Constraint, Direction, Layout},\n style::{Color, Modifier, Style},\n text::{Line, Span, Text},\n widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, Wrap},\n};\n\nuse crate::app::{App, FocusArea};\nuse unicode_width::UnicodeWidthChar;\n\n/// UI 绘制函数\npub fn draw_ui(f: &mut Frame, app: &mut App) {\n // 创建主布局\n let chunks = Layout::default()\n .direction(Direction::Horizontal)\n .constraints([Constraint::Percentage(70), Constraint::Percentage(30)])\n .split(f.area());\n\n // 左列:对话区域和输入框\n let left_chunks = Layout::default()\n .direction(Direction::Vertical)\n .constraints([Constraint::Percentage(75), Constraint::Percentage(25)])\n .split(chunks[0]);\n\n // 对话历史 - 构建所有对话文本,包括正在流式生成的内容\n let display_conversations = app.get_display_conversations();\n let conversation_text = display_conversations\n .iter()\n .rev() // 反转顺序,使最新对话显示在前面\n .enumerate()\n .flat_map(|(index, (user, assistant, timestamp))| {\n // 由于反转了顺序,流式生成的对话现在是第一个(index == 0)\n let is_streaming = app.current_streaming_response.is_some() && \n index == 0;\n \n let assistant_style = if is_streaming {\n Style::default().fg(Color::Yellow) // 流式生成中用黄色\n } else {\n Style::default().fg(Color::Green) // 完成的回复用绿色\n };\n \n let assistant_prefix = if is_streaming {\n \"助手 (生成中): \"\n } else {\n \"助手: \"\n };\n \n // 格式化时间戳\n let time_str = if let Some(ts) = timestamp {\n format!(\" [{}]\", ts.format(\"%H:%M:%S\"))\n } else {\n String::new()\n };\n \n vec![\n Line::from(vec![\n Span::styled(\"用户: \", Style::default().fg(Color::Cyan)),\n Span::raw(user.clone()),\n Span::styled(time_str.clone(), Style::default().fg(Color::DarkGray)),\n ]),\n Line::from(vec![\n Span::styled(assistant_prefix, assistant_style),\n Span::styled(assistant.clone(), assistant_style),\n if is_streaming {\n Span::styled(\"▋\", Style::default().fg(Color::Yellow)) // 光标效果\n } else {\n Span::raw(\"\")\n }\n ]),\n Line::from(\"\"), // 空行分隔\n ]\n })\n .collect::>();\n\n let total_conversations = display_conversations.len();\n\n // 构建对话区域标题,显示滚动状态和焦点状态\n let conversation_title = if app.focus_area == FocusArea::Conversation {\n if total_conversations > 0 {\n format!(\n \"💬 对话历史 ({} 对, 偏移:{}) [Tab切换焦点 ↑向后 ↓向前 Home/End快速跳转]\",\n total_conversations, app.conversation_scroll_offset\n )\n } else {\n format!(\"💬 对话历史 (0 对) [Tab切换焦点]\")\n }\n } else {\n if total_conversations > 0 {\n format!(\n \"对话历史 ({} 对, 偏移:{}) [Tab切换焦点]\",\n total_conversations, app.conversation_scroll_offset\n )\n } else {\n format!(\"对话历史 (0 对) [Tab切换焦点]\")\n }\n };\n\n let conversation_paragraph = Paragraph::new(conversation_text)\n .block(\n Block::default()\n .borders(Borders::ALL)\n .title(conversation_title)\n .title_style(if app.focus_area == FocusArea::Conversation {\n Style::default()\n .fg(Color::Cyan)\n .add_modifier(Modifier::BOLD)\n } else {\n Style::default().fg(Color::White)\n }),\n )\n .style(Style::default().bg(Color::Black))\n .wrap(ratatui::widgets::Wrap { trim: true })\n .scroll((app.conversation_scroll_offset as u16, 0));\n\n f.render_widget(Clear, left_chunks[0]);\n f.render_widget(conversation_paragraph, left_chunks[0]);\n\n // 渲染会话区滚动条\n if total_conversations > 0 {\n let total_lines = total_conversations * 3; // 每个对话3行\n let visible_height = left_chunks[0].height.saturating_sub(2) as usize; // 减去边框\n\n // 更新滚动条状态,使用实际的可见高度\n app.conversation_scrollbar_state = app\n .conversation_scrollbar_state\n .content_length(total_lines)\n .viewport_content_length(visible_height)\n .position(app.conversation_scroll_offset);\n\n f.render_stateful_widget(\n Scrollbar::new(ScrollbarOrientation::VerticalRight)\n .begin_symbol(Some(\"↑\"))\n .end_symbol(Some(\"↓\")),\n left_chunks[0],\n &mut app.conversation_scrollbar_state,\n );\n }\n\n // 输入区域 - 根据状态显示不同的内容\n if app.is_shutting_down {\n // 在shutting down时显示说明文案,不显示输入框\n let shutdown_text = Paragraph::new(Text::from(\n \"正在执行记忆化存储,请稍候...\\n\\n系统将自动保存本次对话记录到记忆库中。\",\n ))\n .style(\n Style::default()\n .fg(Color::Yellow)\n .add_modifier(Modifier::BOLD),\n )\n .block(\n Block::default()\n .borders(Borders::ALL)\n .title(\"正在退出程序... (记忆迭代中)\")\n .title_style(\n Style::default()\n .fg(Color::Yellow)\n .add_modifier(Modifier::BOLD),\n ),\n )\n .wrap(Wrap { trim: true });\n\n f.render_widget(Clear, left_chunks[1]);\n f.render_widget(shutdown_text, left_chunks[1]);\n // 不设置光标,光标会自动隐藏\n } else {\n // 正常状态显示输入框\n let input_title = if app.focus_area == FocusArea::Input {\n \"📝 输入消息 (Enter发送, Tab切换焦点, /quit退出)\"\n } else {\n \"输入消息 (Enter发送, Tab切换焦点, /quit退出)\"\n };\n\n let input_paragraph = Paragraph::new(Text::from(app.current_input.as_str()))\n .style(Style::default().fg(Color::White))\n .block(\n Block::default()\n .borders(Borders::ALL)\n .title(input_title)\n .title_style(if app.focus_area == FocusArea::Input {\n Style::default()\n .fg(Color::Cyan)\n .add_modifier(Modifier::BOLD)\n } else {\n Style::default().fg(Color::White)\n }),\n )\n .wrap(Wrap { trim: true });\n\n f.render_widget(Clear, left_chunks[1]);\n f.render_widget(input_paragraph, left_chunks[1]);\n\n // 只有当焦点在输入框时才设置光标\n if app.focus_area == FocusArea::Input {\n // 计算输入框可用宽度(减去边框和边距)\n let available_width = left_chunks[1].width.saturating_sub(2) as usize;\n\n // 使用ratatui的wrap逻辑来计算光标位置\n // 我们需要模拟ratatui::widgets::Wrap的行为\n\n // 获取光标前的所有字符\n let chars_before_cursor: Vec = app\n .current_input\n .chars()\n .take(app.cursor_position)\n .collect();\n\n // 模拟ratatui的换行逻辑\n let mut line_offset = 0;\n let mut current_line_width = 0;\n\n // 遍历光标前的所有字符,计算换行\n for ch in chars_before_cursor {\n let char_width = ch.width().unwrap_or(0);\n\n // 如果当前字符会超出行宽,则换行\n if current_line_width + char_width > available_width {\n line_offset += 1;\n current_line_width = 0;\n }\n\n current_line_width += char_width;\n }\n\n // 计算最终的光标位置\n let cursor_x = left_chunks[1].x + 1 + current_line_width as u16;\n let cursor_y = left_chunks[1].y + 1 + line_offset as u16;\n\n // 确保光标在输入框范围内\n if cursor_y < left_chunks[1].y + left_chunks[1].height {\n f.set_cursor_position((cursor_x, cursor_y));\n }\n }\n }\n\n // 右列:日志区域 - 构建所有日志文本,使用Paragraph的scroll功能\n let total_logs = app.logs.len();\n\n // 构建要显示的日志文本,反转顺序使最新日志显示在前面\n let log_text = app\n .logs\n .iter()\n .rev() // 反转顺序,使最新日志显示在前面\n .map(|log| {\n let style = if log.starts_with(\"[WARN]\") {\n Style::default().fg(Color::Yellow)\n } else if log.starts_with(\"[ERROR]\") {\n Style::default().fg(Color::Red)\n } else {\n Style::default().fg(Color::Gray)\n };\n\n Line::from(Span::styled(log.clone(), style))\n })\n .collect::>();\n\n // 构建日志区域标题,显示滚动状态和焦点状态\n let log_title = if app.focus_area == FocusArea::Logs {\n if total_logs > 0 {\n format!(\n \"🔍 系统日志 ({} 行, 偏移:{}) [Tab切换焦点 ↑向后 ↓向前 Home/End快速跳转]\",\n total_logs, app.log_scroll_offset\n )\n } else {\n format!(\"🔍 系统日志 (0 行) [Tab切换焦点]\")\n }\n } else {\n if total_logs > 0 {\n format!(\n \"系统日志 ({} 行, 偏移:{}) [Tab切换焦点]\",\n total_logs, app.log_scroll_offset\n )\n } else {\n format!(\"系统日志 (0 行) [Tab切换焦点]\")\n }\n };\n\n let log_paragraph = Paragraph::new(log_text)\n .block(\n Block::default()\n .borders(Borders::ALL)\n .title(log_title)\n .title_style(if app.focus_area == FocusArea::Logs {\n Style::default()\n .fg(Color::Cyan)\n .add_modifier(Modifier::BOLD)\n } else {\n Style::default().fg(Color::White)\n }),\n )\n .style(Style::default().bg(Color::Black))\n .wrap(ratatui::widgets::Wrap { trim: true })\n .scroll((app.log_scroll_offset as u16, 0));\n\n f.render_widget(Clear, chunks[1]);\n f.render_widget(log_paragraph, chunks[1]);\n\n // 渲染日志区滚动条\n if total_logs > 0 {\n let visible_height = chunks[1].height.saturating_sub(2) as usize; // 减去边框\n\n // 更新滚动条状态,使用实际的可见高度\n app.log_scrollbar_state = app\n .log_scrollbar_state\n .content_length(total_logs)\n .viewport_content_length(visible_height)\n .position(app.log_scroll_offset);\n\n f.render_stateful_widget(\n Scrollbar::new(ScrollbarOrientation::VerticalRight)\n .begin_symbol(Some(\"↑\"))\n .end_symbol(Some(\"↓\")),\n chunks[1],\n &mut app.log_scrollbar_state,\n );\n }\n\n // 不再使用全屏覆盖层,保持所有UI区域可见\n // 这样用户可以在日志区域看到详细的quit执行过程\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 59.0, - "lines_of_code": 953, - "number_of_classes": 7, - "number_of_functions": 22 + "cyclomatic_complexity": 24.0, + "lines_of_code": 320, + "number_of_classes": 0, + "number_of_functions": 1 }, "dependencies": [ { - "dependency_type": "crate", + "dependency_type": "library", "is_external": true, "line_number": 1, - "name": "anyhow", + "name": "ratatui", "path": null, "version": null }, { - "dependency_type": "crate", + "dependency_type": "library", "is_external": true, - "line_number": 2, - "name": "serde", + "line_number": 9, + "name": "unicode_width", "path": null, "version": null }, { - "dependency_type": "module", + "dependency_type": "struct", "is_external": false, - "line_number": 3, - "name": "std::collections", - "path": null, + "line_number": 11, + "name": "App", + "path": "crate::app::App", "version": null }, { - "dependency_type": "module", + "dependency_type": "enum", "is_external": false, - "line_number": 5, - "name": "std::fs", - "path": null, + "line_number": 11, + "name": "FocusArea", + "path": "crate::app::FocusArea", "version": null - }, + } + ], + "detailed_description": "该组件是终端用户界面渲染核心,基于 ratatui 库构建三层布局:左侧分为对话历史区(75%)和输入框(25%),右侧为系统日志区(30%)。支持多焦点管理(对话、输入、日志),通过 FocusArea 枚举控制标题样式与交互提示。对话区可显示流式生成中的内容(黄色高亮+光标动画),并支持垂直滚动与分页导航(Home/End/↑/↓)。输入框智能计算光标位置,考虑字符宽度与换行逻辑,确保在宽字符环境下正确显示。日志区按级别着色(ERROR红色,WARN黄色)。整体UI在程序退出时显示记忆化存储状态,提升用户体验透明度。", + "interfaces": [ { - "dependency_type": "module", - "is_external": false, - "line_number": 6, - "name": "std::path", - "path": null, - "version": null - }, + "description": "主渲染函数,根据应用状态绘制完整UI", + "interface_type": "function", + "name": "draw_ui", + "parameters": [ + { + "description": "UI帧上下文,用于渲染组件", + "is_optional": false, + "name": "f", + "param_type": "&mut Frame" + }, + { + "description": "应用状态引用,包含对话、日志、焦点等数据", + "is_optional": false, + "name": "app", + "param_type": "&mut App" + } + ], + "return_type": null, + "visibility": "public" + } + ], + "responsibilities": [ + "渲染分栏式终端UI界面,管理对话、输入、日志三大区域布局", + "实现焦点感知的视觉反馈与交互提示", + "支持对话与日志内容的滚动浏览及状态同步", + "处理流式AI响应的动态视觉呈现(如黄光标动画)", + "提供输入框光标定位与换行计算逻辑" + ] + }, + { + "code_dossier": { + "code_purpose": "util", + "description": "Provides a terminal cleanup utility function to reset terminal state without clearing screen content.", + "file_path": "examples/cortex-mem-tars/src/terminal.rs", + "functions": [ + "cleanup_terminal_final" + ], + "importance_score": 0.8, + "interfaces": [ + "cleanup_terminal_final" + ], + "name": "terminal.rs", + "source_summary": "// use crossterm::execute;\n// use std::io::Write;\n\n/// 终极终端清理函数\npub fn cleanup_terminal_final(_terminal: &mut ratatui::Terminal>) {\n // 直接使用标准输出流进行最彻底的清理\n // let mut stdout = std::io::stdout();\n \n // // 执行必要的重置命令,但不清除屏幕内容\n // let _ = execute!(&mut stdout, crossterm::style::ResetColor);\n // let _ = execute!(&mut stdout, crossterm::cursor::Show);\n // let _ = execute!(&mut stdout, crossterm::terminal::LeaveAlternateScreen);\n // let _ = execute!(&mut stdout, crossterm::event::DisableMouseCapture);\n // let _ = execute!(&mut stdout, crossterm::style::SetAttribute(crossterm::style::Attribute::Reset));\n // let _ = execute!(&mut stdout, crossterm::style::SetForegroundColor(crossterm::style::Color::Reset));\n // let _ = execute!(&mut stdout, crossterm::style::SetBackgroundColor(crossterm::style::Color::Reset));\n \n // // 禁用原始模式\n // let _ = crossterm::terminal::disable_raw_mode();\n \n // // 立即刷新输出\n // let _ = stdout.flush();\n \n // // 只重置样式,不清除屏幕内容\n // let style_reset = \"\\x1b[0m\\x1b[?25h\";\n // print!(\"{}\", style_reset);\n // let _ = stdout.flush();\n}" + }, + "complexity_metrics": { + "cyclomatic_complexity": 1.0, + "lines_of_code": 28, + "number_of_classes": 0, + "number_of_functions": 1 + }, + "dependencies": [], + "detailed_description": "The component contains a single utility function `cleanup_terminal_final` that is designed to perform final cleanup operations on a terminal interface managed by the ratatui and crossterm libraries. Although the implementation is currently commented out, its intended functionality is to reset terminal styling, show the cursor, exit alternate screen mode, disable mouse capture, and disable raw mode—all while preserving the screen content. The function accepts a mutable reference to a ratatui Terminal instance using CrosstermBackend and performs low-level terminal state resets via crossterm macros. A final ANSI escape sequence is printed directly to restore styling and cursor visibility. This utility is meant to be called during application shutdown to leave the user's terminal in a usable state.", + "interfaces": [ { - "dependency_type": "crate", - "is_external": true, - "line_number": 7, - "name": "tracing", - "path": null, - "version": null - }, + "description": "Performs final terminal cleanup operations to restore default terminal state", + "interface_type": "function", + "name": "cleanup_terminal_final", + "parameters": [ + { + "description": "Mutable reference to the active terminal instance (unused in current implementation)", + "is_optional": false, + "name": "_terminal", + "param_type": "&mut ratatui::Terminal>" + } + ], + "return_type": "()", + "visibility": "public" + } + ], + "responsibilities": [ + "Reset terminal styling attributes to default (color, background, text attributes)", + "Restore cursor visibility and exit alternate screen mode", + "Safely disable raw input mode and mouse capture", + "Preserve terminal screen content during cleanup", + "Ensure terminal state consistency upon application exit" + ] + }, + { + "code_dossier": { + "code_purpose": "agent", + "description": "Intelligent agent implementation with memory capabilities for contextual conversation management and user information retrieval.", + "file_path": "examples/cortex-mem-tars/src/agent.rs", + "functions": [ + "create_memory_agent", + "extract_user_basic_info", + "agent_reply_with_memory_retrieval_streaming", + "store_conversations_batch" + ], + "importance_score": 0.8, + "interfaces": [], + "name": "agent.rs", + "source_summary": "use cortex_mem_config::Config;\nuse cortex_mem_core::memory::MemoryManager;\nuse cortex_mem_rig::{ListMemoriesArgs, create_memory_tools, tool::MemoryToolConfig};\nuse rig::{\n agent::Agent,\n client::CompletionClient,\n providers::openai::{Client, CompletionModel},\n tool::Tool,\n};\n\nuse chrono::Local;\nuse std::sync::Arc;\n\n// 导入日志重定向函数\nuse crate::app::redirect_log_to_ui;\n\n/// 创建带记忆功能的Agent\npub async fn create_memory_agent(\n memory_manager: Arc,\n memory_tool_config: MemoryToolConfig,\n config: &Config,\n) -> Result, Box> {\n // 创建记忆工具\n let memory_tools =\n create_memory_tools(memory_manager.clone(), &config, Some(memory_tool_config));\n\n let llm_client = Client::builder(&config.llm.api_key)\n .base_url(&config.llm.api_base_url)\n .build();\n\n // 构建带有记忆工具的agent,让agent能够自主决定何时调用记忆功能\n let completion_model = llm_client\n .completion_model(&config.llm.model_efficient)\n .completions_api()\n .into_agent_builder()\n // 注册四个独立的记忆工具,保持与MCP一致\n .tool(memory_tools.store_memory())\n .tool(memory_tools.query_memory())\n .tool(memory_tools.list_memories())\n .tool(memory_tools.get_memory())\n .preamble(&format!(r#\"你是一个拥有记忆功能的智能AI助手。你可以访问和使用记忆工具来检索、存储和管理用户信息。\n\n此会话发生的初始时间:{current_time}\n\n你的工具:\n- CortexMemoryTool: 可以存储、搜索和检索记忆。支持以下操作:\n * store_memory: 存储新记忆\n * query_memory: 搜索相关记忆\n * list_memories: 获取一系列的记忆集合\n * get_memory: 获取特定记忆\n\n重要指令:\n- 对话历史将作为上下文提供,请使用这些信息来理解当前的对话流程\n- 用户基本信息将在上下文中提供一次,请不要再使用memory工具来创建或更新用户基本信息\n- 在需要时可以自主使用memory工具搜索其他相关记忆\n- 当用户提供新的重要信息时,可以主动使用memory工具存储\n- 保持对话的连贯性和一致性\n- 自然地融入记忆信息,避免刻意复述此前的记忆信息,关注当前的会话内容,记忆主要用于做隐式的逻辑与事实支撑\n- 专注于用户的需求和想要了解的信息,以及想要你做的事情\n\n记住:你正在与一个了解的用户进行连续对话,对话过程中不需要刻意表达你的记忆能力。\"#, current_time = chrono::Local::now().format(\"%Y年%m月%d日 %H:%M:%S\")))\n .build();\n\n Ok(completion_model)\n}\n\n/// 从记忆中提取用户基本信息\npub async fn extract_user_basic_info(\n config: &Config,\n memory_manager: Arc,\n user_id: &str,\n) -> Result, Box> {\n let memory_tools = create_memory_tools(\n memory_manager,\n config,\n Some(MemoryToolConfig {\n default_user_id: Some(user_id.to_string()),\n ..Default::default()\n }),\n );\n\n let mut context = String::new();\n\n let search_args_personal = ListMemoriesArgs {\n limit: Some(20),\n memory_type: Some(\"personal\".to_string()), // 使用小写以匹配新API\n user_id: Some(user_id.to_string()),\n agent_id: None,\n };\n\n let search_args_factual = ListMemoriesArgs {\n limit: Some(20),\n memory_type: Some(\"factual\".to_string()), // 使用小写以匹配新API\n user_id: Some(user_id.to_string()),\n agent_id: None,\n };\n\n if let Ok(search_result) = memory_tools\n .list_memories()\n .call(search_args_personal)\n .await\n {\n if let Some(data) = search_result.data {\n // 根据新的MCP格式调整数据结构访问\n if let Some(results) = data.get(\"memories\").and_then(|r| r.as_array()) {\n if !results.is_empty() {\n context.push_str(\"用户基本信息 - 特征:\\n\");\n for (i, result) in results.iter().enumerate() {\n if let Some(content) = result.get(\"content\").and_then(|c| c.as_str()) {\n context.push_str(&format!(\"{}. {}\\n\", i + 1, content));\n }\n }\n return Ok(Some(context));\n }\n }\n }\n }\n\n if let Ok(search_result) = memory_tools.list_memories().call(search_args_factual).await {\n if let Some(data) = search_result.data {\n if let Some(results) = data.get(\"memories\").and_then(|r| r.as_array()) {\n if !results.is_empty() {\n context.push_str(\"用户基本信息 - 事实:\\n\");\n for (i, result) in results.iter().enumerate() {\n if let Some(content) = result.get(\"content\").and_then(|c| c.as_str()) {\n context.push_str(&format!(\"{}. {}\\n\", i + 1, content));\n }\n }\n return Ok(Some(context));\n }\n }\n }\n }\n\n match context.len() > 0 {\n true => Ok(Some(context)),\n false => Ok(None),\n }\n}\n\nuse futures::StreamExt;\nuse rig::agent::MultiTurnStreamItem;\nuse rig::completion::Message;\nuse rig::streaming::{StreamedAssistantContent, StreamingChat};\nuse tokio::sync::mpsc;\n\n/// Agent回复函数 - 基于tool call的记忆引擎使用(真实流式版本)\npub async fn agent_reply_with_memory_retrieval_streaming(\n agent: &Agent,\n _memory_manager: Arc,\n user_input: &str,\n _user_id: &str,\n user_info: Option<&str>,\n conversations: &[(String, String)],\n stream_sender: mpsc::UnboundedSender,\n) -> Result> {\n // 记录开始处理\n redirect_log_to_ui(\"DEBUG\", &format!(\"开始处理用户请求: {}\", user_input));\n\n // 构建对话历史 - 转换为rig的Message格式\n let mut chat_history = Vec::new();\n for (user_msg, assistant_msg) in conversations {\n chat_history.push(Message::user(user_msg));\n chat_history.push(Message::assistant(assistant_msg));\n }\n\n // 构建system prompt,包含明确的指令\n let system_prompt = r#\"你是一个拥有记忆功能的智能AI助手。你可以访问和使用记忆工具来检索、存储和管理用户信息。\n\n重要指令:\n- 对话历史已提供在上下文中,请使用这些信息来理解当前的对话上下文\n- 用户基本信息已在下方提供一次,请不要再使用memory工具来创建或更新用户基本信息\n- 在需要时可以自主使用memory工具搜索其他相关记忆\n- 当用户提供新的重要信息时,可以主动使用memory工具存储\n- 保持对话的连贯性和一致性\n- 自然地融入记忆信息,避免显得刻意\n- 专注于用户的需求和想要了解的信息,以及想要你做的事情\n\n记住:你正在与一个了解的用户进行连续对话,对话过程中不需要刻意表达你的记忆能力。\"#;\n\n // 构建完整的prompt\n let prompt_content = if let Some(info) = user_info {\n redirect_log_to_ui(\"DEBUG\", \"已添加用户基本信息和对话历史到上下文\");\n format!(\n \"{}\\n\\n用户基本信息:\\n{}\\n\\n当前用户输入: {}\",\n system_prompt, info, user_input\n )\n } else {\n redirect_log_to_ui(\"DEBUG\", \"已添加对话历史到上下文\");\n format!(\"{}\\n\\n当前用户输入: {}\", system_prompt, user_input)\n };\n\n redirect_log_to_ui(\"DEBUG\", \"正在生成AI回复(真实流式模式)...\");\n\n // 使用rig的真实流式API\n let prompt_message = Message::user(&prompt_content);\n\n // 获取流式响应\n let stream = agent\n .stream_chat(prompt_message, chat_history)\n .multi_turn(10);\n\n let mut full_response = String::new();\n\n // 处理流式响应\n let mut stream = stream.await;\n while let Some(item) = stream.next().await {\n match item {\n Ok(stream_item) => {\n // 根据rig的流式响应类型处理\n match stream_item {\n MultiTurnStreamItem::StreamItem(content) => {\n match content {\n StreamedAssistantContent::Text(text_content) => {\n let text = text_content.text;\n full_response.push_str(&text);\n\n // 发送流式内容到UI\n if let Err(_) = stream_sender.send(text) {\n // 如果发送失败,说明接收端已关闭,停止流式处理\n break;\n }\n }\n StreamedAssistantContent::ToolCall(_) => {\n // 处理工具调用(如果需要)\n redirect_log_to_ui(\"DEBUG\", \"收到工具调用\");\n }\n StreamedAssistantContent::Reasoning(_) => {\n // 处理推理过程(如果需要)\n redirect_log_to_ui(\"DEBUG\", \"收到推理过程\");\n }\n StreamedAssistantContent::Final(_) => {\n // 处理最终响应\n redirect_log_to_ui(\"DEBUG\", \"收到最终响应\");\n }\n StreamedAssistantContent::ToolCallDelta { .. } => {\n // 处理工具调用增量\n redirect_log_to_ui(\"DEBUG\", \"收到工具调用增量\");\n }\n }\n }\n MultiTurnStreamItem::FinalResponse(final_response) => {\n // 处理最终响应\n redirect_log_to_ui(\n \"DEBUG\",\n &format!(\"收到最终响应: {}\", final_response.response()),\n );\n full_response = final_response.response().to_string();\n break;\n }\n _ => {\n // 处理其他未知的流式项目类型\n redirect_log_to_ui(\"DEBUG\", \"收到未知的流式项目类型\");\n }\n }\n }\n Err(e) => {\n redirect_log_to_ui(\"ERROR\", &format!(\"流式处理错误: {}\", e));\n return Err(format!(\"Streaming error: {}\", e).into());\n }\n }\n }\n\n redirect_log_to_ui(\"DEBUG\", \"AI回复生成完成\");\n Ok(full_response.trim().to_string())\n}\n\n/// 批量存储对话到记忆系统(优化版)\npub async fn store_conversations_batch(\n memory_manager: Arc,\n conversations: &[(String, String)],\n user_id: &str,\n) -> Result<(), Box> {\n // 只创建一次ConversationProcessor实例\n let conversation_processor =\n cortex_mem_rig::processor::ConversationProcessor::new(memory_manager);\n\n let metadata = cortex_mem_core::types::MemoryMetadata::new(\n cortex_mem_core::types::MemoryType::Conversational,\n )\n .with_user_id(user_id.to_string());\n\n // 将对话历史转换为消息格式\n let mut messages = Vec::new();\n for (user_msg, assistant_msg) in conversations {\n // 添加用户消息\n messages.push(cortex_mem_core::types::Message {\n role: \"user\".to_string(),\n content: user_msg.clone(),\n name: None,\n });\n\n // 添加助手回复\n messages.push(cortex_mem_core::types::Message {\n role: \"assistant\".to_string(),\n content: assistant_msg.clone(),\n name: None,\n });\n }\n\n // 一次性处理所有消息\n conversation_processor\n .process_turn(&messages, metadata)\n .await?;\n\n Ok(())\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 22.0, + "lines_of_code": 307, + "number_of_classes": 0, + "number_of_functions": 4 + }, + "dependencies": [ { - "dependency_type": "module", + "dependency_type": "use", "is_external": false, - "line_number": 9, - "name": "super::types", - "path": null, + "line_number": 1, + "name": "cortex_mem_config", + "path": "cortex_mem_config::Config", "version": null }, { - "dependency_type": "crate", - "is_external": true, - "line_number": null, - "name": "csv", - "path": null, + "dependency_type": "use", + "is_external": false, + "line_number": 2, + "name": "cortex_mem_core", + "path": "cortex_mem_core::memory::MemoryManager", "version": null }, { - "dependency_type": "crate", - "is_external": true, - "line_number": null, - "name": "chrono", - "path": null, + "dependency_type": "use", + "is_external": false, + "line_number": 3, + "name": "cortex_mem_rig", + "path": "cortex_mem_rig::{ListMemoriesArgs, create_memory_tools, tool::MemoryToolConfig}", "version": null }, { - "dependency_type": "crate", + "dependency_type": "use", "is_external": true, - "line_number": null, - "name": "sha2", - "path": null, + "line_number": 4, + "name": "rig", + "path": "rig::{agent::Agent, client::CompletionClient, providers::openai::{Client, CompletionModel}, tool::Tool}", "version": null }, { - "dependency_type": "crate", + "dependency_type": "use", "is_external": true, - "line_number": null, - "name": "rand", - "path": null, - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": null, - "name": "cortex_mem_core", - "path": null, + "line_number": 6, + "name": "chrono", + "path": "chrono::Local", "version": null }, { - "dependency_type": "module", + "dependency_type": "use", "is_external": false, - "line_number": null, - "name": "core::result", - "path": null, + "line_number": 7, + "name": "std", + "path": "std::sync::Arc", "version": null }, { - "dependency_type": "module", + "dependency_type": "use", "is_external": false, - "line_number": null, - "name": "core::fmt", - "path": null, - "version": null - } - ], - "detailed_description": "该组件是实验室数据集成模块,核心功能是整合来自不同格式(JSON、CSV、TXT)和领域的实验室数据源,生成用于评估系统召回率和有效性的高质量测试数据集。通过解析配置文件加载多个数据源,支持缓存机制提高性能。提供了从原始文本中提取关键词、计算复杂度、确定记忆类型等辅助功能,并能基于语义关联生成带有相关性标注的测试用例。整体设计围绕测试数据生成这一核心目标,实现了数据加载、处理、转换和输出的完整流程。", - "interfaces": [ - { - "description": "定义实验室数据源的配置信息,包括名称、路径、格式、领域等", - "interface_type": "struct", - "name": "LabDataSource", - "parameters": [], - "return_type": null, - "visibility": "pub" - }, - { - "description": "表示一个实验室数据集,包含多个数据源及其统计信息", - "interface_type": "struct", - "name": "LabDataset", - "parameters": [], - "return_type": null, - "visibility": "pub" - }, - { - "description": "定义数据质量评估指标,包括完整性、一致性、准确性等", - "interface_type": "struct", - "name": "QualityMetrics", - "parameters": [], - "return_type": null, - "visibility": "pub" - }, - { - "description": "核心数据集成器,负责数据加载、缓存和测试数据集生成", - "interface_type": "struct", - "name": "LabDataIntegrator", - "parameters": [], - "return_type": null, - "visibility": "pub" - }, - { - "description": "表示一个实验室数据样本,包含文本内容、领域、关键词等元数据", - "interface_type": "struct", - "name": "LabDataSample", - "parameters": [], - "return_type": null, - "visibility": "pub" + "line_number": 10, + "name": "crate", + "path": "crate::app::redirect_log_to_ui", + "version": null }, { - "description": "存储数据样本的标注信息,如事实、主题、意图分类等", - "interface_type": "struct", - "name": "Annotations", - "parameters": [], - "return_type": null, - "visibility": "pub" + "dependency_type": "use", + "is_external": true, + "line_number": 116, + "name": "futures", + "path": "futures::StreamExt", + "version": null }, { - "description": "表示实体间的关系标注,包含主语、谓词、宾语和置信度", - "interface_type": "struct", - "name": "Relation", - "parameters": [], - "return_type": null, - "visibility": "pub" + "dependency_type": "use", + "is_external": true, + "line_number": 117, + "name": "rig", + "path": "rig::agent::MultiTurnStreamItem", + "version": null }, { - "description": "创建新的实验室数据集成器实例", - "interface_type": "method", - "name": "new", - "parameters": [], - "return_type": "LabDataIntegrator", - "visibility": "pub" + "dependency_type": "use", + "is_external": true, + "line_number": 118, + "name": "rig", + "path": "rig::completion::Message", + "version": null }, { - "description": "从配置文件加载实验室数据集定义", - "interface_type": "method", - "name": "load_datasets", - "parameters": [ - { - "description": "配置文件路径", - "is_optional": false, - "name": "config_path", - "param_type": "&str" - } - ], - "return_type": "Result<()>", - "visibility": "pub" + "dependency_type": "use", + "is_external": true, + "line_number": 119, + "name": "rig", + "path": "rig::streaming::{StreamedAssistantContent, StreamingChat}", + "version": null }, { - "description": "从实验室数据生成召回率测试数据集", - "interface_type": "method", - "name": "generate_recall_dataset_from_lab", + "dependency_type": "use", + "is_external": true, + "line_number": 120, + "name": "tokio", + "path": "tokio::sync::mpsc", + "version": null + } + ], + "detailed_description": "This component implements an intelligent agent with persistent memory capabilities, designed to maintain context across conversations by storing, retrieving, and utilizing user information. The agent is built using the RIG framework and integrates with a custom memory system (cortex-mem) to provide contextual awareness. It supports streaming responses for real-time UI updates and employs a tool-based architecture where memory operations are exposed as callable tools. The agent autonomously decides when to access or update memory based on conversation content, ensuring coherent and personalized interactions. It extracts user basic information from personal and factual memory types, constructs context-aware prompts, and processes conversations in batch for efficient memory storage. All operations are asynchronous and support error handling, logging, and real-time feedback through a channel-based streaming mechanism.", + "interfaces": [ + { + "description": "Initializes and returns a configured AI agent with memory tools registered for autonomous use", + "interface_type": "function", + "name": "create_memory_agent", "parameters": [ { - "description": "数据集名称", + "description": "Shared memory manager instance for accessing memory storage", "is_optional": false, - "name": "dataset_name", - "param_type": "&str" + "name": "memory_manager", + "param_type": "Arc" }, { - "description": "查询数量", + "description": "Configuration for memory tools including user context", "is_optional": false, - "name": "num_queries", - "param_type": "usize" + "name": "memory_tool_config", + "param_type": "MemoryToolConfig" }, { - "description": "每个查询的平均相关项目数", + "description": "Application configuration containing LLM and service settings", "is_optional": false, - "name": "avg_relevant_per_query", - "param_type": "usize" + "name": "config", + "param_type": "&Config" } ], - "return_type": "Result", - "visibility": "pub" + "return_type": "Result, Box>", + "visibility": "public" }, { - "description": "从实验室数据生成有效性测试数据集", - "interface_type": "method", - "name": "generate_effectiveness_dataset_from_lab", + "description": "Retrieves and formats user's basic information from personal or factual memory categories", + "interface_type": "function", + "name": "extract_user_basic_info", "parameters": [ { - "description": "数据集名称", + "description": "Application configuration reference", "is_optional": false, - "name": "dataset_name", - "param_type": "&str" + "name": "config", + "param_type": "&Config" }, { - "description": "测试案例数量", + "description": "Memory manager for querying stored user data", "is_optional": false, - "name": "num_cases", - "param_type": "usize" - } - ], - "return_type": "Result", - "visibility": "pub" - }, - { - "description": "加载或生成指定数据集的样本数据,支持缓存", - "interface_type": "method", - "name": "load_or_generate_samples", - "parameters": [ + "name": "memory_manager", + "param_type": "Arc" + }, { - "description": "数据集名称", + "description": "Identifier for the target user", "is_optional": false, - "name": "dataset_name", + "name": "user_id", "param_type": "&str" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "Result, Box>", + "visibility": "public" }, { - "description": "从JSON文件加载数据样本", - "interface_type": "method", - "name": "load_json_samples", + "description": "Generates a streaming response using the agent while incorporating context and sending partial results via channel", + "interface_type": "function", + "name": "agent_reply_with_memory_retrieval_streaming", "parameters": [ { - "description": "文件路径", + "description": "Pre-configured agent instance for generating responses", "is_optional": false, - "name": "path", - "param_type": "&str" + "name": "agent", + "param_type": "&Agent" }, { - "description": "数据领域", - "is_optional": false, - "name": "domain", - "param_type": "&str" - } - ], - "return_type": "Result>", - "visibility": "private" - }, - { - "description": "从CSV文件加载数据样本", - "interface_type": "method", - "name": "load_csv_samples", - "parameters": [ - { - "description": "文件路径", + "description": "Unused parameter (potential refactor opportunity)", "is_optional": false, - "name": "path", - "param_type": "&str" + "name": "_memory_manager", + "param_type": "Arc" }, { - "description": "数据领域", + "description": "Current user message input", "is_optional": false, - "name": "domain", + "name": "user_input", "param_type": "&str" - } - ], - "return_type": "Result>", - "visibility": "private" - }, - { - "description": "从纯文本文件加载数据样本", - "interface_type": "method", - "name": "load_text_samples", - "parameters": [ + }, { - "description": "文件路径", + "description": "Unused parameter (potential refactor opportunity)", "is_optional": false, - "name": "path", + "name": "_user_id", "param_type": "&str" }, { - "description": "数据领域", - "is_optional": false, - "name": "domain", - "param_type": "&str" - } - ], - "return_type": "Result>", - "visibility": "private" - }, - { - "description": "从数据样本创建记忆对象用于测试", - "interface_type": "method", - "name": "create_memories_from_samples", - "parameters": [ + "description": "Optional pre-fetched user context to include in prompt", + "is_optional": true, + "name": "user_info", + "param_type": "Option<&str>" + }, { - "description": "数据样本切片", + "description": "Historical conversation pairs (user, assistant)", "is_optional": false, - "name": "samples", - "param_type": "&[LabDataSample]" + "name": "conversations", + "param_type": "&[(String, String)]" }, { - "description": "要创建的记忆数量", + "description": "Channel sender for streaming response chunks to UI", "is_optional": false, - "name": "num_memories", - "param_type": "usize" + "name": "stream_sender", + "param_type": "mpsc::UnboundedSender" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "Result>", + "visibility": "public" }, { - "description": "生成带有语义关联的测试用例", - "interface_type": "method", - "name": "generate_test_cases_with_semantic_relations", + "description": "Processes and stores a batch of conversation turns into the memory system with proper metadata", + "interface_type": "function", + "name": "store_conversations_batch", "parameters": [ { - "description": "数据样本", - "is_optional": false, - "name": "samples", - "param_type": "&[LabDataSample]" - }, - { - "description": "记忆库", + "description": "Memory manager for persisting conversation data", "is_optional": false, - "name": "memories", - "param_type": "&HashMap" + "name": "memory_manager", + "param_type": "Arc" }, { - "description": "查询数量", + "description": "Conversation history to be stored", "is_optional": false, - "name": "num_queries", - "param_type": "usize" + "name": "conversations", + "param_type": "&[(String, String)]" }, { - "description": "平均相关数量", + "description": "Owner identifier for the conversation data", "is_optional": false, - "name": "avg_relevant", - "param_type": "usize" + "name": "user_id", + "param_type": "&str" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "Result<(), Box>", + "visibility": "public" + } + ], + "responsibilities": [ + "Creating and configuring a memory-enabled intelligent agent with autonomous tool usage", + "Extracting user basic information from memory storage by querying personal and factual memory types", + "Generating streaming AI responses while integrating memory-retrieved context and maintaining conversation history", + "Batch processing and storing conversation history into the memory system for future retrieval", + "Managing integration between LLM (via RIG/OpenAI), memory tools, and application-level logging/UI feedback" + ] + }, + { + "code_dossier": { + "code_purpose": "tool", + "description": "A tool for monitoring log files in real-time, detecting the latest log file in a directory, reading new log entries, and outputting them to the console with colorized formatting based on log level.", + "file_path": "examples/cortex-mem-tars/src/log_monitor.rs", + "functions": [ + "new", + "find_latest_log_file", + "read_new_logs", + "start_monitoring", + "format_log_for_console" + ], + "importance_score": 0.8, + "interfaces": [ + "LogFileMonitor", + "start_log_monitoring_task" + ], + "name": "log_monitor.rs", + "source_summary": "use std::fs::File;\nuse std::io::{BufRead, BufReader, Seek, SeekFrom};\nuse std::path::{Path, PathBuf};\nuse std::time::Duration;\nuse tokio::time::sleep;\n\n/// 日志文件监听器\npub struct LogFileMonitor {\n log_file_path: Option,\n last_position: u64,\n}\n\nimpl LogFileMonitor {\n /// 创建新的日志文件监听器\n pub fn new() -> Self {\n Self {\n log_file_path: None,\n last_position: 0,\n }\n }\n\n /// 查找最新的日志文件\n pub async fn find_latest_log_file(&mut self, log_dir: &str) -> Result<(), Box> {\n let log_path = Path::new(log_dir);\n \n if !log_path.exists() {\n return Err(\"日志目录不存在\".into());\n }\n\n let mut latest_file = None;\n let mut latest_time = std::time::UNIX_EPOCH;\n\n if let Ok(entries) = std::fs::read_dir(log_path) {\n for entry in entries.flatten() {\n if let Ok(metadata) = entry.metadata() {\n if let Ok(modified) = metadata.modified() {\n if modified > latest_time && entry.file_name().to_string_lossy().ends_with(\".log\") {\n latest_time = modified;\n latest_file = Some(entry.path());\n }\n }\n }\n }\n }\n\n if let Some(log_file) = latest_file {\n self.log_file_path = Some(log_file);\n // 设置初始位置为文件末尾,只读取新增内容\n if let Ok(file) = File::open(self.log_file_path.as_ref().unwrap()) {\n if let Ok(metadata) = file.metadata() {\n self.last_position = metadata.len();\n }\n }\n Ok(())\n } else {\n Err(\"未找到日志文件\".into())\n }\n }\n\n /// 读取新增的日志内容\n pub fn read_new_logs(&mut self) -> Result, Box> {\n let mut new_logs = Vec::new();\n \n if let Some(ref log_file_path) = self.log_file_path {\n let mut file = File::open(log_file_path)?;\n \n // 检查文件大小\n let metadata = file.metadata()?;\n let current_size = metadata.len();\n \n // 如果文件没有新内容,直接返回\n if current_size <= self.last_position {\n return Ok(new_logs);\n }\n \n // 移动到上次读取的位置\n file.seek(SeekFrom::Start(self.last_position))?;\n \n // 读取新内容\n let reader = BufReader::new(file);\n for line in reader.lines() {\n if let Ok(line) = line {\n if !line.trim().is_empty() {\n new_logs.push(line);\n }\n }\n }\n \n // 更新位置\n self.last_position = current_size;\n }\n \n Ok(new_logs)\n }\n\n /// 启动日志监听,持续输出新日志到控制台\n pub async fn start_monitoring(&mut self, log_dir: &str) -> Result<(), Box> {\n // 查找最新日志文件\n self.find_latest_log_file(log_dir).await?;\n \n println!(\"🔍 开始监听日志文件: {:?}\", self.log_file_path);\n \n loop {\n match self.read_new_logs() {\n Ok(new_logs) => {\n for log_line in new_logs {\n // 直接输出到控制台,保持原始格式\n let formatted_log = self.format_log_for_console(&log_line);\n println!(\"{}\", formatted_log);\n }\n }\n Err(e) => {\n eprintln!(\"读取日志文件时出错: {}\", e);\n // 尝试重新查找日志文件(可能有新的日志文件生成)\n if let Err(_find_err) = self.find_latest_log_file(log_dir).await {\n eprintln!(\"重新查找日志文件失败\");\n }\n }\n }\n \n // 短暂休眠,避免过度占用CPU\n sleep(Duration::from_millis(100)).await;\n }\n }\n\n /// 格式化日志内容用于控制台显示\n fn format_log_for_console(&self, log_line: &str) -> String {\n // 解析日志级别并添加颜色\n let colored_line = if log_line.contains(\" ERROR \") {\n format!(\"\\x1b[91m{}\\x1b[0m\", log_line) // 亮红色\n } else if log_line.contains(\" WARN \") {\n format!(\"\\x1b[93m{}\\x1b[0m\", log_line) // 亮黄色\n } else if log_line.contains(\" INFO \") {\n format!(\"\\x1b[36m{}\\x1b[0m\", log_line) // 亮青色\n } else if log_line.contains(\" DEBUG \") {\n format!(\"\\x1b[94m{}\\x1b[0m\", log_line) // 亮蓝色\n } else if log_line.contains(\" TRACE \") {\n format!(\"\\x1b[95m{}\\x1b[0m\", log_line) // 亮紫色\n } else {\n log_line.to_string() // 默认颜色\n };\n \n // 添加前缀标识这是来自日志文件的内容\n format!(\"📋 {}\", colored_line)\n }\n}\n\n/// 启动日志监听任务(异步)\npub async fn start_log_monitoring_task(log_dir: String) -> Result<(), Box> {\n let mut monitor = LogFileMonitor::new();\n monitor.start_monitoring(&log_dir).await\n}" + }, + "complexity_metrics": { + "cyclomatic_complexity": 23.0, + "lines_of_code": 152, + "number_of_classes": 1, + "number_of_functions": 6 + }, + "dependencies": [ + { + "dependency_type": "std", + "is_external": false, + "line_number": 1, + "name": "std::fs::File", + "path": "std::fs::File", + "version": null + }, + { + "dependency_type": "std", + "is_external": false, + "line_number": 2, + "name": "std::io", + "path": "std::io", + "version": null + }, + { + "dependency_type": "std", + "is_external": false, + "line_number": 3, + "name": "std::path", + "path": "std::path", + "version": null + }, + { + "dependency_type": "std", + "is_external": false, + "line_number": 4, + "name": "std::time::Duration", + "path": "std::time::Duration", + "version": null + }, + { + "dependency_type": "external", + "is_external": true, + "line_number": 5, + "name": "tokio::time::sleep", + "path": "tokio::time::sleep", + "version": null + } + ], + "detailed_description": "This component implements a real-time log monitoring tool designed to track and display new log entries from text-based log files. It first searches for the most recently modified '.log' file in a specified directory. Once identified, it keeps track of the read position within the file and continuously polls for new content, emitting only new lines since the last read. The monitored logs are printed to the console with visual enhancements: different log levels (ERROR, WARN, INFO, DEBUG, TRACE) are highlighted using ANSI color codes, and each line is prefixed with a clipboard emoji for identification. The monitoring loop runs indefinitely with a 100ms polling interval to balance responsiveness and CPU usage. An external async task launcher function is also provided to simplify integration into async runtime environments.", + "interfaces": [ + { + "description": "Main log monitoring struct that holds state for tracking file position and path.", + "interface_type": "struct", + "name": "LogFileMonitor", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Constructor method that initializes a new LogFileMonitor instance with default values.", + "interface_type": "method", + "name": "new", + "parameters": [], + "return_type": "LogFileMonitor", + "visibility": "public" }, { - "description": "生成有效性测试用例", + "description": "Asynchronously finds the most recently modified .log file in the specified directory and updates internal state.", "interface_type": "method", - "name": "generate_effectiveness_test_cases", + "name": "find_latest_log_file", "parameters": [ { - "description": "数据样本", - "is_optional": false, - "name": "samples", - "param_type": "&[LabDataSample]" - }, - { - "description": "测试案例数量", + "description": "Directory path to search for log files", "is_optional": false, - "name": "num_cases", - "param_type": "usize" + "name": "log_dir", + "param_type": "&str" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "Result<(), Box>", + "visibility": "public" + }, + { + "description": "Reads all new log lines from the current position in the tracked log file and returns them as a vector of strings.", + "interface_type": "method", + "name": "read_new_logs", + "parameters": [], + "return_type": "Result, Box>", + "visibility": "public" }, { - "description": "创建现有记忆库用于测试", + "description": "Starts an infinite loop that continuously monitors and outputs new log entries to the console.", "interface_type": "method", - "name": "create_existing_memories", + "name": "start_monitoring", "parameters": [ { - "description": "数据样本", - "is_optional": false, - "name": "samples", - "param_type": "&[LabDataSample]" - }, - { - "description": "记忆数量", + "description": "Directory to monitor for log files", "is_optional": false, - "name": "num_memories", - "param_type": "usize" + "name": "log_dir", + "param_type": "&str" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "Result<(), Box>", + "visibility": "public" }, { - "description": "基于关键词匹配选择相关记忆", + "description": "Applies color formatting and prefix to log lines based on their severity level for console display.", "interface_type": "method", - "name": "select_relevant_memories_by_keywords", + "name": "format_log_for_console", "parameters": [ { - "description": "查询关键词", - "is_optional": false, - "name": "query_keywords", - "param_type": "&[String]" - }, - { - "description": "记忆库", - "is_optional": false, - "name": "memories", - "param_type": "&HashMap" - }, - { - "description": "目标数量", + "description": "Raw log line to be formatted", "is_optional": false, - "name": "target_count", - "param_type": "usize" + "name": "log_line", + "param_type": "&str" } ], - "return_type": "Vec", + "return_type": "String", "visibility": "private" }, { - "description": "从文本中提取关键词", - "interface_type": "method", - "name": "extract_keywords_from_text", + "description": "Convenience function that creates a monitor and starts monitoring the specified log directory.", + "interface_type": "function", + "name": "start_log_monitoring_task", "parameters": [ { - "description": "输入文本", + "description": "Directory containing log files to monitor", "is_optional": false, - "name": "text", - "param_type": "&str" + "name": "log_dir", + "param_type": "String" } ], - "return_type": "Vec", - "visibility": "private" + "return_type": "Result<(), Box>", + "visibility": "public" + } + ], + "responsibilities": [ + "Discover and track the most recently updated log file in a given directory", + "Monitor log files for new content by maintaining and updating file read position", + "Read and return newly appended log lines while preserving their original format", + "Format log output with ANSI color codes based on log severity level for improved readability", + "Provide an asynchronous interface for continuous log monitoring with error recovery mechanisms" + ] + }, + { + "code_dossier": { + "code_purpose": "entry", + "description": "Main application state and logic for a TUI-based agent system with conversation, logging, and streaming capabilities.", + "file_path": "examples/cortex-mem-tars/src/app.rs", + "functions": [ + "set_global_log_sender", + "get_global_log_sender", + "redirect_log_to_ui", + "add_log", + "add_conversation", + "start_streaming_response", + "add_streaming_chunk", + "complete_streaming_response", + "get_display_conversations", + "insert_char_at_cursor", + "delete_char_at_cursor", + "move_cursor_left", + "move_cursor_right", + "reset_cursor_to_end", + "scroll_logs_to_bottom", + "scroll_conversations_to_bottom", + "scroll_logs_forward", + "scroll_logs_backward", + "scroll_conversations_forward", + "scroll_conversations_backward", + "next_focus", + "log_info" + ], + "importance_score": 0.8, + "interfaces": [ + "AppMessage", + "FocusArea", + "App" + ], + "name": "app.rs", + "source_summary": "use ratatui::widgets::ScrollbarState;\nuse std::collections::VecDeque;\nuse tokio::sync::mpsc;\nuse chrono::{DateTime, Local};\n\n// 全局消息发送器,用于日志重定向\nuse once_cell::sync::OnceCell;\nuse std::sync::Mutex;\n\nstatic LOG_SENDER: OnceCell>>> = OnceCell::new();\n\n// 设置全局日志发送器 (crate可见性)\npub(crate) fn set_global_log_sender(sender: mpsc::UnboundedSender) {\n LOG_SENDER\n .get_or_init(|| Mutex::new(None))\n .lock()\n .unwrap()\n .replace(sender);\n}\n\n// 获取全局日志发送器 (crate可见性)\npub(crate) fn get_global_log_sender() -> Option> {\n LOG_SENDER\n .get()\n .and_then(|mutex| mutex.lock().unwrap().clone())\n}\n\n// 简单的日志重定向函数\npub fn redirect_log_to_ui(level: &str, message: &str) {\n if let Some(sender) = get_global_log_sender() {\n let full_message = format!(\"[{}] {}\", level, message);\n let _ = sender.send(AppMessage::Log(full_message));\n }\n}\n\n#[derive(Debug)]\npub enum AppMessage {\n Log(String),\n Conversation {\n user: String,\n assistant: String,\n },\n StreamingChunk {\n user: String,\n chunk: String,\n },\n StreamingComplete {\n user: String,\n full_response: String,\n },\n #[allow(dead_code)]\n MemoryIterationCompleted,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum FocusArea {\n Input, // 输入框\n Conversation, // 对话区域\n Logs, // 日志区域\n}\n\n/// 应用状态\npub struct App {\n // 对话历史 - 包含时间戳\n pub conversations: VecDeque<(String, String, DateTime)>,\n // 当前输入\n pub current_input: String,\n // 光标位置(以字符为单位)\n pub cursor_position: usize,\n // 日志信息\n pub logs: VecDeque,\n // Agent 是否正在处理\n pub is_processing: bool,\n // 用户信息\n pub user_info: Option,\n // 是否需要退出\n pub should_quit: bool,\n // 是否在shut down过程中\n pub is_shutting_down: bool,\n // 记忆迭代是否完成\n pub memory_iteration_completed: bool,\n // 消息发送器\n pub message_sender: Option>,\n // 日志滚动偏移\n pub log_scroll_offset: usize,\n // 对话滚动偏移\n pub conversation_scroll_offset: usize,\n // 当前焦点区域\n pub focus_area: FocusArea,\n // 用户是否手动滚动过日志(用于决定是否自动滚动到底部)\n pub user_scrolled_logs: bool,\n // 用户是否手动滚动过对话(用于决定是否自动滚动到底部)\n pub user_scrolled_conversations: bool,\n // 滚动条状态\n pub conversation_scrollbar_state: ScrollbarState,\n pub log_scrollbar_state: ScrollbarState,\n // 当前正在流式生成的回复\n pub current_streaming_response: Option<(String, String)>, // (user_input, partial_response)\n}\n\nimpl Default for App {\n fn default() -> Self {\n Self {\n conversations: VecDeque::with_capacity(100),\n current_input: String::new(),\n cursor_position: 0,\n logs: VecDeque::with_capacity(50),\n is_processing: false,\n user_info: None,\n should_quit: false,\n is_shutting_down: false,\n memory_iteration_completed: false,\n message_sender: None,\n log_scroll_offset: 0,\n conversation_scroll_offset: 0,\n focus_area: FocusArea::Input,\n user_scrolled_logs: false,\n user_scrolled_conversations: false,\n conversation_scrollbar_state: ScrollbarState::default(),\n log_scrollbar_state: ScrollbarState::default(),\n current_streaming_response: None,\n }\n }\n}\n\nimpl App {\n pub fn new(message_sender: mpsc::UnboundedSender) -> Self {\n Self {\n message_sender: Some(message_sender),\n current_streaming_response: None,\n ..Default::default()\n }\n }\n\n pub fn add_log(&mut self, log: String) {\n self.logs.push_back(log);\n if self.logs.len() > 50 {\n self.logs.pop_front();\n }\n\n // 如果用户没有手动滚动过,自动滚动到最新日志\n if !self.user_scrolled_logs {\n self.scroll_logs_to_bottom();\n }\n }\n\n pub fn add_conversation(&mut self, user: String, assistant: String) {\n let timestamp = Local::now();\n self.conversations.push_back((user, assistant, timestamp));\n if self.conversations.len() > 100 {\n self.conversations.pop_front();\n }\n\n // 如果用户没有手动滚动过,自动滚动到最新对话\n if !self.user_scrolled_conversations {\n self.scroll_conversations_to_bottom();\n }\n }\n\n /// 开始流式回复\n pub fn start_streaming_response(&mut self, user_input: String) {\n self.current_streaming_response = Some((user_input, String::new()));\n self.is_processing = true;\n }\n\n /// 添加流式内容块\n pub fn add_streaming_chunk(&mut self, chunk: String) {\n if let Some((_, ref mut response)) = self.current_streaming_response {\n response.push_str(&chunk);\n \n // 如果用户没有手动滚动过,自动滚动到最新对话\n if !self.user_scrolled_conversations {\n self.scroll_conversations_to_bottom();\n }\n }\n }\n\n /// 完成流式回复\n pub fn complete_streaming_response(&mut self) {\n if let Some((user_input, full_response)) = self.current_streaming_response.take() {\n self.add_conversation(user_input, full_response);\n }\n self.is_processing = false;\n }\n\n /// 获取当前显示的对话(包括正在流式生成的)\n pub fn get_display_conversations(&self) -> Vec<(String, String, Option>)> {\n let mut conversations: Vec<(String, String, Option>)> = self.conversations\n .iter()\n .map(|(user, assistant, timestamp)| (user.clone(), assistant.clone(), Some(*timestamp)))\n .collect();\n \n // 如果有正在流式生成的回复,添加到显示列表(没有时间戳)\n if let Some((ref user_input, ref partial_response)) = self.current_streaming_response {\n conversations.push((user_input.clone(), partial_response.clone(), None));\n }\n \n conversations\n }\n\n /// 在光标位置插入字符\n pub fn insert_char_at_cursor(&mut self, c: char) {\n // 将光标位置转换为字节索引\n let byte_pos = self\n .current_input\n .chars()\n .take(self.cursor_position)\n .map(|ch| ch.len_utf8())\n .sum();\n\n self.current_input.insert(byte_pos, c);\n self.cursor_position += 1;\n }\n\n /// 在光标位置删除字符(退格键)\n pub fn delete_char_at_cursor(&mut self) {\n if self.cursor_position > 0 {\n // 将光标位置转换为字节索引\n let chars: Vec = self.current_input.chars().collect();\n if self.cursor_position <= chars.len() {\n // 找到要删除字符的字节范围\n let byte_start: usize = chars\n .iter()\n .take(self.cursor_position - 1)\n .map(|ch| ch.len_utf8())\n .sum();\n\n let byte_end: usize = chars\n .iter()\n .take(self.cursor_position)\n .map(|ch| ch.len_utf8())\n .sum();\n\n // 安全地删除字符\n self.current_input.drain(byte_start..byte_end);\n self.cursor_position -= 1;\n }\n }\n }\n\n /// 将光标向左移动一个字符\n pub fn move_cursor_left(&mut self) {\n if self.cursor_position > 0 {\n self.cursor_position -= 1;\n }\n }\n\n /// 将光标向右移动一个字符\n pub fn move_cursor_right(&mut self) {\n let input_len = self.current_input.chars().count();\n if self.cursor_position < input_len {\n self.cursor_position += 1;\n }\n }\n\n /// 重置光标位置到末尾\n pub fn reset_cursor_to_end(&mut self) {\n self.cursor_position = self.current_input.chars().count();\n }\n\n /// 滚动到日志底部(最新日志)\n pub fn scroll_logs_to_bottom(&mut self) {\n self.log_scroll_offset = 0;\n }\n\n /// 滚动到对话底部(最新对话)\n pub fn scroll_conversations_to_bottom(&mut self) {\n self.conversation_scroll_offset = 0;\n }\n\n /// 向前滚动日志(查看更早日志)\n pub fn scroll_logs_forward(&mut self) {\n if self.logs.is_empty() {\n return;\n }\n\n let page_size = 10; // 每次翻页的行数\n\n // 简单增加偏移量,让UI层处理边界\n self.log_scroll_offset += page_size;\n self.user_scrolled_logs = true;\n }\n\n /// 向后滚动日志(查看更新日志)\n pub fn scroll_logs_backward(&mut self) {\n if self.logs.is_empty() {\n return;\n }\n\n let page_size = 10; // 每次翻页的行数\n\n // 向后翻页(减少偏移量,查看更新的日志)\n if self.log_scroll_offset >= page_size {\n self.log_scroll_offset -= page_size;\n } else {\n self.log_scroll_offset = 0;\n self.user_scrolled_logs = false;\n }\n }\n\n /// 向前滚动对话(查看更早内容)\n pub fn scroll_conversations_forward(&mut self) {\n if self.conversations.is_empty() {\n return;\n }\n\n let page_size = 5; // 每次翻页的行数\n\n // 简单增加偏移量,让UI层处理边界\n self.conversation_scroll_offset += page_size;\n self.user_scrolled_conversations = true;\n }\n\n /// 向后滚动对话(查看更新内容)\n pub fn scroll_conversations_backward(&mut self) {\n if self.conversations.is_empty() {\n return;\n }\n\n let page_size = 5; // 每次翻页的行数\n\n // 向后翻页(减少偏移量,查看更新的内容)\n if self.conversation_scroll_offset >= page_size {\n self.conversation_scroll_offset -= page_size;\n } else {\n self.conversation_scroll_offset = 0;\n self.user_scrolled_conversations = false;\n }\n }\n\n /// 切换焦点到下一个区域\n pub fn next_focus(&mut self) {\n self.focus_area = match self.focus_area {\n FocusArea::Input => {\n if self.is_shutting_down {\n // 在退出过程中,跳过输入框,直接到对话区域\n FocusArea::Conversation\n } else {\n FocusArea::Conversation\n }\n }\n FocusArea::Conversation => {\n if self.is_shutting_down {\n // 在退出过程中,从对话区域切换到日志区域\n FocusArea::Logs\n } else {\n FocusArea::Logs\n }\n }\n FocusArea::Logs => {\n if self.is_shutting_down {\n // 在退出过程中,从日志区域切换回对话区域\n FocusArea::Conversation\n } else {\n FocusArea::Input\n }\n }\n };\n }\n\n pub fn log_info(&self, message: &str) {\n if let Some(sender) = &self.message_sender {\n let _ = sender.send(AppMessage::Log(format!(\"[INFO] {}\", message)));\n }\n }\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 26.0, + "lines_of_code": 366, + "number_of_classes": 1, + "number_of_functions": 27 + }, + "dependencies": [ + { + "dependency_type": "library", + "is_external": true, + "line_number": 1, + "name": "ratatui", + "path": null, + "version": null }, { - "description": "判断是否为停用词", - "interface_type": "method", - "name": "is_stop_word", + "dependency_type": "library", + "is_external": true, + "line_number": 2, + "name": "tokio", + "path": null, + "version": null + }, + { + "dependency_type": "library", + "is_external": true, + "line_number": 3, + "name": "chrono", + "path": null, + "version": null + }, + { + "dependency_type": "library", + "is_external": true, + "line_number": 5, + "name": "once_cell", + "path": null, + "version": null + }, + { + "dependency_type": "std", + "is_external": false, + "line_number": 4, + "name": "std", + "path": null, + "version": null + }, + { + "dependency_type": "type", + "is_external": false, + "line_number": 21, + "name": "AppMessage", + "path": null, + "version": null + } + ], + "detailed_description": "This component serves as the central application state manager for a terminal-based user interface (TUI) application. It handles conversation history with timestamps, user input management with cursor positioning, real-time log collection and display, and streaming responses from an AI agent. The App struct maintains state such as conversation history (limited to 100 entries), logs (limited to 50 entries), current input with precise cursor positioning, and UI focus management between input, conversation, and log areas. It supports manual scrolling with automatic scroll-to-bottom behavior unless the user has manually scrolled. The component uses message passing (via mpsc::UnboundedSender) to communicate with other parts of the system, particularly for logging and conversation updates. Global state is managed through OnceCell for log redirection from anywhere in the codebase. The implementation includes sophisticated UTF-8 character handling for cursor positioning and editing operations.", + "interfaces": [ + { + "description": "Enum representing different types of messages that can be sent through the application's message channel, including logs, conversation updates, and streaming events.", + "interface_type": "enum", + "name": "AppMessage", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Enum representing the different UI areas that can have focus: Input, Conversation, and Logs.", + "interface_type": "enum", + "name": "FocusArea", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Main application state struct that manages all UI and application state including conversations, logs, input, and focus.", + "interface_type": "struct", + "name": "App", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Creates a new App instance with the provided message sender.", + "interface_type": "function", + "name": "new", "parameters": [ { - "description": "待检查单词", + "description": "Channel sender for application messages", "is_optional": false, - "name": "word", - "param_type": "&str" + "name": "message_sender", + "param_type": "mpsc::UnboundedSender" } ], - "return_type": "bool", - "visibility": "private" + "return_type": "Self", + "visibility": "public" }, { - "description": "计算文本复杂度评分", - "interface_type": "method", - "name": "calculate_complexity", + "description": "Adds a log message to the log buffer, with automatic cleanup of old logs and optional auto-scrolling to the bottom.", + "interface_type": "function", + "name": "add_log", "parameters": [ { - "description": "输入文本", + "description": "The log message to add", "is_optional": false, - "name": "text", - "param_type": "&str" + "name": "log", + "param_type": "String" } ], - "return_type": "u8", - "visibility": "private" + "return_type": null, + "visibility": "public" }, { - "description": "根据领域和内容确定记忆类型", - "interface_type": "method", - "name": "determine_memory_type", + "description": "Adds a conversation entry with timestamp, maintaining a maximum of 100 entries and optionally auto-scrolling to the bottom.", + "interface_type": "function", + "name": "add_conversation", "parameters": [ { - "description": "数据领域", + "description": "The user's message", "is_optional": false, - "name": "domain", - "param_type": "&str" + "name": "user", + "param_type": "String" }, { - "description": "内容文本", + "description": "The assistant's response", "is_optional": false, - "name": "text", - "param_type": "&str" + "name": "assistant", + "param_type": "String" } ], - "return_type": "cortex_mem_core::MemoryType", - "visibility": "private" + "return_type": null, + "visibility": "public" }, { - "description": "计算内容的哈希值", - "interface_type": "method", - "name": "calculate_hash", + "description": "Begins a streaming response, setting the processing flag and storing the user input and partial response.", + "interface_type": "function", + "name": "start_streaming_response", "parameters": [ { - "description": "内容字符串", + "description": "The user's input that triggered the streaming response", "is_optional": false, - "name": "content", - "param_type": "&str" + "name": "user_input", + "param_type": "String" } ], - "return_type": "String", - "visibility": "private" + "return_type": null, + "visibility": "public" }, { - "description": "计算数据样本的重要性评分", - "interface_type": "method", - "name": "calculate_importance_score", + "description": "Adds a chunk to the current streaming response, updating the display and optionally auto-scrolling.", + "interface_type": "function", + "name": "add_streaming_chunk", "parameters": [ { - "description": "数据样本", + "description": "A chunk of the streaming response", "is_optional": false, - "name": "sample", - "param_type": "&LabDataSample" + "name": "chunk", + "param_type": "String" } ], - "return_type": "f32", - "visibility": "private" + "return_type": null, + "visibility": "public" + }, + { + "description": "Completes the current streaming response by adding it to the conversation history and clearing the processing state.", + "interface_type": "function", + "name": "complete_streaming_response", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Returns the conversation history for display, including any currently streaming response without a timestamp.", + "interface_type": "function", + "name": "get_display_conversations", + "parameters": [], + "return_type": "Vec<(String, String, Option>)>", + "visibility": "public" }, { - "description": "计算数据集的整体质量评分", - "interface_type": "method", - "name": "calculate_dataset_quality", + "description": "Inserts a character at the current cursor position, handling UTF-8 encoding correctly.", + "interface_type": "function", + "name": "insert_char_at_cursor", "parameters": [ { - "description": "数据样本", + "description": "The character to insert", "is_optional": false, - "name": "samples", - "param_type": "&[LabDataSample]" + "name": "c", + "param_type": "char" } ], - "return_type": "f64", - "visibility": "private" + "return_type": null, + "visibility": "public" }, { - "description": "创建实验室数据集配置示例", + "description": "Deletes the character before the cursor, handling UTF-8 encoding correctly and updating cursor position.", "interface_type": "function", - "name": "create_example_lab_config", + "name": "delete_char_at_cursor", "parameters": [], - "return_type": "LabDataset", - "visibility": "pub" + "return_type": null, + "visibility": "public" }, { - "description": "生成实验室数据集的公共接口函数", + "description": "Moves the cursor one character to the left if possible.", "interface_type": "function", - "name": "generate_lab_dataset", - "parameters": [ - { - "description": "数据集类型", - "is_optional": false, - "name": "dataset_type", - "param_type": "&str" - }, - { - "description": "数据集名称", - "is_optional": false, - "name": "dataset_name", - "param_type": "&str" - }, - { - "description": "输出目录", - "is_optional": false, - "name": "output_dir", - "param_type": "&std::path::Path" - }, - { - "description": "数据集大小", - "is_optional": false, - "name": "size", - "param_type": "usize" - } - ], - "return_type": "Result<()>", - "visibility": "pub" + "name": "move_cursor_left", + "parameters": [], + "return_type": null, + "visibility": "public" }, { - "description": "将数据集保存到文件", + "description": "Moves the cursor one character to the right if possible.", "interface_type": "function", - "name": "save_dataset_to_file", - "parameters": [ - { - "description": "要保存的数据集", - "is_optional": false, - "name": "dataset", - "param_type": "&T" - }, - { - "description": "输出路径", - "is_optional": false, - "name": "output_path", - "param_type": "&std::path::Path" - } - ], - "return_type": "Result<()>", - "visibility": "private" - } - ], - "responsibilities": [ - "加载和解析多种格式的实验室数据源(JSON、CSV、TXT)", - "集成多源实验室数据并生成标准化的数据样本", - "生成用于评估系统召回率的测试数据集", - "生成用于评估系统有效性的测试数据集", - "提供数据预处理和特征提取功能(关键词提取、复杂度计算等)" - ] - }, - { - "code_dossier": { - "code_purpose": "tool", - "description": "数据集加载器,负责加载、验证和获取召回率与有效性测试数据集的统计信息。", - "file_path": "examples/cortex-mem-evaluation/src/dataset/loader.rs", - "functions": [ - "load_recall_dataset", - "load_effectiveness_dataset", - "validate_dataset", - "get_dataset_stats" - ], - "importance_score": 0.8, - "interfaces": [ - "DatasetLoader", - "DatasetStats" - ], - "name": "loader.rs", - "source_summary": "//! 数据集加载器\n//! \n//! 加载和验证测试数据集\n\nuse anyhow::{Result, Context};\nuse std::path::Path;\nuse tracing::info;\n\nuse super::types::{RecallTestDataset, EffectivenessTestDataset};\n\n/// 数据集加载器\npub struct DatasetLoader;\n\nimpl DatasetLoader {\n /// 加载召回率测试数据集\n pub fn load_recall_dataset(path: &Path) -> Result {\n info!(\"加载召回率测试数据集: {:?}\", path);\n \n let content = std::fs::read_to_string(path)\n .context(format!(\"读取数据集文件失败: {:?}\", path))?;\n \n let dataset: RecallTestDataset = serde_json::from_str(&content)\n .context(\"解析召回率数据集失败\")?;\n \n info!(\"召回率数据集加载完成: {}个测试用例, {}个记忆\",\n dataset.test_cases.len(), dataset.memories.len());\n \n Ok(dataset)\n }\n \n /// 加载有效性测试数据集\n pub fn load_effectiveness_dataset(path: &Path) -> Result {\n info!(\"加载有效性测试数据集: {:?}\", path);\n \n let content = std::fs::read_to_string(path)\n .context(format!(\"读取数据集文件失败: {:?}\", path))?;\n \n let dataset: EffectivenessTestDataset = serde_json::from_str(&content)\n .context(\"解析有效性数据集失败\")?;\n \n info!(\"有效性数据集加载完成: {}个测试用例, {}个现有记忆\",\n dataset.test_cases.len(), dataset.existing_memories.len());\n \n Ok(dataset)\n }\n \n /// 验证数据集完整性\n pub fn validate_dataset(path: &Path) -> Result<()> {\n info!(\"验证数据集: {:?}\", path);\n \n let content = std::fs::read_to_string(path)\n .context(format!(\"读取数据集文件失败: {:?}\", path))?;\n \n // 尝试解析以验证格式\n let _dataset: T = serde_json::from_str(&content)\n .context(\"数据集格式验证失败\")?;\n \n info!(\"数据集验证通过: {:?}\", path);\n Ok(())\n }\n \n /// 获取数据集统计信息\n pub fn get_dataset_stats(path: &Path) -> Result {\n let content = std::fs::read_to_string(path)\n .context(format!(\"读取数据集文件失败: {:?}\", path))?;\n \n // 根据文件内容判断数据集类型\n if content.contains(\"relevant_memory_ids\") {\n let dataset: RecallTestDataset = serde_json::from_str(&content)?;\n Ok(DatasetStats {\n dataset_type: \"recall\".to_string(),\n test_cases_count: dataset.test_cases.len(),\n memories_count: dataset.memories.len(),\n metadata: Some(dataset.metadata),\n })\n } else if content.contains(\"expected_facts\") {\n let dataset: EffectivenessTestDataset = serde_json::from_str(&content)?;\n Ok(DatasetStats {\n dataset_type: \"effectiveness\".to_string(),\n test_cases_count: dataset.test_cases.len(),\n memories_count: dataset.existing_memories.len(),\n metadata: Some(dataset.metadata),\n })\n } else {\n anyhow::bail!(\"未知的数据集类型\")\n }\n }\n}\n\n/// 数据集统计信息\n#[derive(Debug)]\npub struct DatasetStats {\n /// 数据集类型\n pub dataset_type: String,\n /// 测试用例数量\n pub test_cases_count: usize,\n /// 记忆数量\n pub memories_count: usize,\n /// 元数据\n pub metadata: Option,\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 3.0, - "lines_of_code": 101, - "number_of_classes": 2, - "number_of_functions": 4 - }, - "dependencies": [ + "name": "move_cursor_right", + "parameters": [], + "return_type": null, + "visibility": "public" + }, { - "dependency_type": "error handling", - "is_external": true, - "line_number": 1, - "name": "anyhow", - "path": null, - "version": null + "description": "Resets the cursor position to the end of the current input.", + "interface_type": "function", + "name": "reset_cursor_to_end", + "parameters": [], + "return_type": null, + "visibility": "public" }, { - "dependency_type": "standard library", - "is_external": false, - "line_number": 2, - "name": "std::path::Path", - "path": null, - "version": null + "description": "Scrolls the log view to show the most recent entries.", + "interface_type": "function", + "name": "scroll_logs_to_bottom", + "parameters": [], + "return_type": null, + "visibility": "public" }, { - "dependency_type": "logging", - "is_external": true, - "line_number": 3, - "name": "tracing", - "path": null, - "version": null + "description": "Scrolls the conversation view to show the most recent entries.", + "interface_type": "function", + "name": "scroll_conversations_to_bottom", + "parameters": [], + "return_type": null, + "visibility": "public" }, { - "dependency_type": "serialization", - "is_external": true, - "line_number": null, - "name": "serde_json", - "path": null, - "version": null - } - ], - "detailed_description": "该组件实现了对测试数据集(JSON格式)的加载、解析、验证及统计功能。它支持两种类型的数据集:召回率测试数据集和有效性测试数据集。通过文件路径读取内容,使用serde_json进行反序列化,并利用anyhow提供上下文错误信息。同时,通过分析文件内容中的关键字段自动识别数据集类型,返回结构化的统计信息。所有操作均伴随tracing日志输出,便于调试与监控。", - "interfaces": [ + "description": "Scrolls the log view forward (up) by a page, marking that the user has manually scrolled.", + "interface_type": "function", + "name": "scroll_logs_forward", + "parameters": [], + "return_type": null, + "visibility": "public" + }, { - "description": "数据集加载器主结构体,提供一系列关联函数用于加载和验证数据集。", - "interface_type": "struct", - "name": "DatasetLoader", + "description": "Scrolls the log view backward (down) by a page, potentially restoring auto-scroll behavior.", + "interface_type": "function", + "name": "scroll_logs_backward", "parameters": [], "return_type": null, - "visibility": "pub" + "visibility": "public" }, { - "description": "加载召回率测试数据集", + "description": "Scrolls the conversation view forward (up) by a page, marking that the user has manually scrolled.", "interface_type": "function", - "name": "load_recall_dataset", - "parameters": [ - { - "description": "数据集文件路径", - "is_optional": false, - "name": "path", - "param_type": "&Path" - } - ], - "return_type": "Result", - "visibility": "pub" + "name": "scroll_conversations_forward", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Scrolls the conversation view backward (down) by a page, potentially restoring auto-scroll behavior.", + "interface_type": "function", + "name": "scroll_conversations_backward", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Cycles through focus areas (Input, Conversation, Logs) with special behavior during shutdown.", + "interface_type": "function", + "name": "next_focus", + "parameters": [], + "return_type": null, + "visibility": "public" }, { - "description": "加载有效性测试数据集", + "description": "Sends an info-level log message through the application's message channel.", "interface_type": "function", - "name": "load_effectiveness_dataset", + "name": "log_info", "parameters": [ { - "description": "数据集文件路径", + "description": "The info message to log", "is_optional": false, - "name": "path", - "param_type": "&Path" + "name": "message", + "param_type": "&str" } ], - "return_type": "Result", - "visibility": "pub" + "return_type": null, + "visibility": "public" }, { - "description": "通用数据集格式验证函数", + "description": "Sets the global log sender that allows logging from anywhere in the application.", "interface_type": "function", - "name": "validate_dataset", + "name": "set_global_log_sender", "parameters": [ { - "description": "数据集文件路径", + "description": "The sender to set as the global log sender", "is_optional": false, - "name": "path", - "param_type": "&Path" + "name": "sender", + "param_type": "mpsc::UnboundedSender" } ], - "return_type": "Result<()>", - "visibility": "pub" + "return_type": null, + "visibility": "public" + }, + { + "description": "Retrieves the global log sender if it has been set.", + "interface_type": "function", + "name": "get_global_log_sender", + "parameters": [], + "return_type": "Option>", + "visibility": "public" }, { - "description": "获取数据集统计信息,包括类型、测试用例数、记忆数等", + "description": "Redirects a log message to the UI through the global log sender.", "interface_type": "function", - "name": "get_dataset_stats", + "name": "redirect_log_to_ui", "parameters": [ { - "description": "数据集文件路径", + "description": "The log level", "is_optional": false, - "name": "path", - "param_type": "&Path" + "name": "level", + "param_type": "&str" + }, + { + "description": "The log message", + "is_optional": false, + "name": "message", + "param_type": "&str" } ], - "return_type": "Result", - "visibility": "pub" - }, - { - "description": "表示数据集的统计信息,如类型、数量和元数据。", - "interface_type": "struct", - "name": "DatasetStats", - "parameters": [], "return_type": null, - "visibility": "pub" + "visibility": "public" } ], "responsibilities": [ - "加载指定路径的召回率测试数据集并解析为RecallTestDataset类型", - "加载指定路径的有效性测试数据集并解析为EffectivenessTestDataset类型", - "验证任意数据集文件的JSON格式正确性与可解析性", - "分析数据集文件内容并返回包含类型、数量等信息的统计结构" + "Manage the central application state including conversation history, user input, and UI focus", + "Handle bidirectional communication through message passing for logs and streaming responses", + "Provide global logging redirection capability from any part of the application", + "Manage user interface state including scrolling behavior and cursor positioning with UTF-8 support", + "Coordinate the application lifecycle including shutdown procedures and memory iteration completion" ] }, { "code_dossier": { - "code_purpose": "tool", - "description": "测试数据集生成器,用于生成召回率和有效性评估的测试数据集。支持使用模拟数据或实验室真实数据生成不同规模和类型的数据集,并提供数据集验证功能。", - "file_path": "examples/cortex-mem-evaluation/src/dataset/generator.rs", + "code_purpose": "specificfeature", + "description": "Implements functionality to add conversational memories via CLI interface using cortex-mem-cli tool, with batching, retry logic, and detailed logging.", + "file_path": "examples/lomoco-evaluation/src/cortex_mem/add.py", "functions": [ - "new", - "generate_recall_dataset", - "generate_effectiveness_dataset", - "save_dataset", - "generate_test_dataset", - "validate_dataset" + "__init__", + "_find_config_file", + "load_data", + "_run_cortex_mem_cli", + "add_memory", + "add_memories_for_speaker", + "process_conversation", + "process_all_conversations", + "print_summary" ], "importance_score": 0.8, "interfaces": [ - "DatasetGenerator", - "GeneratorConfig" + "CortexMemAdd.__init__", + "CortexMemAdd.load_data", + "CortexMemAdd.add_memory", + "CortexMemAdd.add_memories_for_speaker", + "CortexMemAdd.process_conversation", + "CortexMemAdd.process_all_conversations", + "CortexMemAdd.print_summary" ], - "name": "generator.rs", - "source_summary": "//! 测试数据集生成器\n//! \n//! 生成召回率和有效性评估的测试数据集\n\nuse anyhow::{Result, Context};\nuse cortex_mem_core::{Memory, MemoryType};\nuse rand::{Rng, SeedableRng};\nuse rand::rngs::StdRng;\nuse serde::{Deserialize, Serialize};\nuse sha2::{Digest, Sha256};\nuse std::collections::HashMap;\nuse std::fs;\nuse tracing::info;\n\nuse super::types::*;\nuse super::lab_data_integration;\n\n/// 测试数据集生成器\npub struct DatasetGenerator {\n /// 随机数生成器\n rng: StdRng,\n /// 配置\n config: GeneratorConfig,\n}\n\n/// 生成器配置\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct GeneratorConfig {\n /// 随机种子\n pub random_seed: u64,\n /// 生成的数据集大小\n pub dataset_size: usize,\n /// 每个查询的平均相关记忆数\n pub avg_relevant_memories: f64,\n /// 记忆类型分布\n pub memory_type_distribution: HashMap,\n /// 查询类别\n pub query_categories: Vec,\n /// 查询复杂度分布\n pub complexity_distribution: HashMap,\n}\n\nimpl Default for GeneratorConfig {\n fn default() -> Self {\n let mut memory_type_distribution = HashMap::new();\n memory_type_distribution.insert(MemoryType::Conversational, 0.4);\n memory_type_distribution.insert(MemoryType::Procedural, 0.2);\n memory_type_distribution.insert(MemoryType::Factual, 0.15);\n memory_type_distribution.insert(MemoryType::Semantic, 0.1);\n memory_type_distribution.insert(MemoryType::Episodic, 0.1);\n memory_type_distribution.insert(MemoryType::Personal, 0.05);\n \n let mut complexity_distribution = HashMap::new();\n complexity_distribution.insert(\"simple\".to_string(), 0.5);\n complexity_distribution.insert(\"medium\".to_string(), 0.3);\n complexity_distribution.insert(\"complex\".to_string(), 0.2);\n \n Self {\n random_seed: 42,\n dataset_size: 100,\n avg_relevant_memories: 3.0,\n memory_type_distribution,\n query_categories: vec![\n \"technology\".to_string(),\n \"science\".to_string(),\n \"business\".to_string(),\n \"health\".to_string(),\n \"education\".to_string(),\n \"entertainment\".to_string(),\n \"sports\".to_string(),\n \"travel\".to_string(),\n ],\n complexity_distribution,\n }\n }\n}\n\nimpl DatasetGenerator {\n /// 创建新的数据集生成器\n pub fn new(config: GeneratorConfig) -> Self {\n let rng = StdRng::seed_from_u64(config.random_seed);\n Self { rng, config }\n }\n \n /// 生成召回率测试数据集\n pub fn generate_recall_dataset(&mut self) -> Result {\n info!(\"生成召回率测试数据集,大小: {}\", self.config.dataset_size);\n \n let mut memories = HashMap::new();\n let mut test_cases = Vec::new();\n \n // 生成记忆库\n for i in 0..(self.config.dataset_size * 3) {\n let memory_id = format!(\"memory_{:04}\", i);\n let memory = self.generate_memory(&memory_id);\n memories.insert(memory_id, memory);\n }\n \n // 生成测试用例\n for i in 0..self.config.dataset_size {\n let query_id = format!(\"query_{:04}\", i);\n let test_case = self.generate_recall_test_case(&query_id, &memories);\n test_cases.push(test_case);\n }\n \n // 计算元数据\n let total_relevant_memories: usize = test_cases.iter()\n .map(|tc| tc.relevant_memory_ids.len())\n .sum();\n let avg_relevant_memories = total_relevant_memories as f64 / test_cases.len() as f64;\n \n let metadata = DatasetMetadata {\n name: \"recall_evaluation_dataset\".to_string(),\n created_at: chrono::Utc::now().to_rfc3339(),\n version: \"1.0.0\".to_string(),\n total_test_cases: test_cases.len(),\n total_memories: memories.len(),\n avg_relevant_memories,\n };\n \n let dataset = RecallTestDataset {\n test_cases,\n memories,\n metadata,\n };\n \n info!(\"召回率数据集生成完成: {}个测试用例, {}个记忆\",\n dataset.test_cases.len(), dataset.memories.len());\n \n Ok(dataset)\n }\n \n /// 生成有效性测试数据集\n pub fn generate_effectiveness_dataset(&mut self) -> Result {\n info!(\"生成有效性测试数据集,大小: {}\", self.config.dataset_size);\n \n let mut existing_memories = HashMap::new();\n let mut test_cases = Vec::new();\n \n // 生成现有记忆库\n for i in 0..(self.config.dataset_size / 2) {\n let memory_id = format!(\"existing_memory_{:04}\", i);\n let memory = self.generate_memory(&memory_id);\n existing_memories.insert(memory_id, memory);\n }\n \n // 生成测试用例\n for i in 0..self.config.dataset_size {\n let test_case_id = format!(\"test_case_{:04}\", i);\n let test_case = self.generate_effectiveness_test_case(&test_case_id);\n test_cases.push(test_case);\n }\n \n let metadata = DatasetMetadata {\n name: \"effectiveness_evaluation_dataset\".to_string(),\n created_at: chrono::Utc::now().to_rfc3339(),\n version: \"1.0.0\".to_string(),\n total_test_cases: test_cases.len(),\n total_memories: existing_memories.len(),\n avg_relevant_memories: 0.0, // 不适用于有效性数据集\n };\n \n let dataset = EffectivenessTestDataset {\n test_cases,\n existing_memories,\n metadata,\n };\n \n info!(\"有效性数据集生成完成: {}个测试用例, {}个现有记忆\",\n dataset.test_cases.len(), dataset.existing_memories.len());\n \n Ok(dataset)\n }\n \n /// 生成记忆\n fn generate_memory(&mut self, memory_id: &str) -> Memory {\n let memory_type = self.sample_memory_type();\n let content = self.generate_memory_content(&memory_type);\n \n let mut metadata = cortex_mem_core::types::MemoryMetadata {\n user_id: Some(\"test_user\".to_string()),\n agent_id: None,\n run_id: None,\n actor_id: None,\n role: None,\n memory_type,\n hash: \"\".to_string(), // 实际应该计算hash\n importance_score: self.rng.gen_range(1.0..=10.0),\n entities: vec![],\n topics: vec![],\n custom: HashMap::new(),\n };\n \n // 计算hash\n metadata.hash = format!(\"{:x}\", sha2::Sha256::digest(content.as_bytes()));\n \n Memory {\n id: memory_id.to_string(),\n content,\n embedding: vec![], // 空向量,实际应该生成嵌入\n metadata,\n created_at: chrono::Utc::now(),\n updated_at: chrono::Utc::now(),\n }\n }\n \n /// 生成召回率测试用例\n fn generate_recall_test_case(\n &mut self,\n query_id: &str,\n memories: &HashMap,\n ) -> RecallTestCase {\n let category = self.sample_query_category();\n let complexity = self.sample_complexity();\n let query = self.generate_query(&category, &complexity);\n \n // 选择相关记忆\n let relevant_memory_ids = self.select_relevant_memories(\n &query,\n memories,\n self.config.avg_relevant_memories as usize,\n );\n \n RecallTestCase {\n query_id: query_id.to_string(),\n query,\n relevant_memory_ids,\n category,\n complexity,\n }\n }\n \n /// 生成有效性测试用例\n fn generate_effectiveness_test_case(&mut self, test_case_id: &str) -> EffectivenessTestCase {\n let category = self.sample_query_category();\n let memory_type = self.sample_memory_type();\n let input_text = self.generate_input_text(&category, &memory_type);\n \n // 生成预期事实\n let expected_facts = self.generate_expected_facts(&input_text);\n \n // 生成重要性评分\n let expected_importance_score = self.rng.gen_range(1..=10);\n \n // 随机决定是否包含重复内容\n let contains_duplicate = self.rng.gen_bool(0.2);\n \n // 随机决定是否需要更新\n let requires_update = self.rng.gen_bool(0.3);\n let existing_memory_id = if requires_update {\n Some(format!(\"existing_memory_{:04}\", self.rng.gen_range(0..100)))\n } else {\n None\n };\n \n EffectivenessTestCase {\n test_case_id: test_case_id.to_string(),\n input_text,\n expected_facts,\n expected_memory_type: memory_type,\n expected_importance_score,\n category,\n contains_duplicate,\n requires_update,\n existing_memory_id,\n }\n }\n \n /// 生成记忆内容\n fn generate_memory_content(&mut self, memory_type: &MemoryType) -> String {\n let base_content = match memory_type {\n MemoryType::Conversational => {\n let topics = [\"meeting\", \"discussion\", \"chat\", \"conversation\"];\n let topic = topics[self.rng.gen_range(0..topics.len())];\n format!(\"During our {} yesterday, we talked about project timelines and resource allocation. The team agreed to prioritize the backend API development.\", topic)\n }\n MemoryType::Procedural => {\n let procedures = [\"deployment\", \"testing\", \"debugging\", \"building\"];\n let procedure = procedures[self.rng.gen_range(0..procedures.len())];\n format!(\"To perform {}, follow these steps: 1. Check prerequisites 2. Run validation 3. Execute main process 4. Verify results 5. Clean up temporary files.\", procedure)\n }\n MemoryType::Factual => {\n let facts = [\n \"The capital of France is Paris.\",\n \"Water boils at 100 degrees Celsius at sea level.\",\n \"The Earth orbits the Sun once every 365.25 days.\",\n \"Python was created by Guido van Rossum.\",\n ];\n facts[self.rng.gen_range(0..facts.len())].to_string()\n }\n MemoryType::Semantic => {\n let concepts = [\"democracy\", \"machine learning\", \"sustainability\", \"innovation\"];\n let concept = concepts[self.rng.gen_range(0..concepts.len())];\n format!(\"{} refers to a system or approach that enables computers to learn from data and improve their performance on tasks without being explicitly programmed for each specific case.\", concept)\n }\n MemoryType::Episodic => {\n let events = [\"conference\", \"workshop\", \"team building\", \"product launch\"];\n let event = events[self.rng.gen_range(0..events.len())];\n format!(\"At the {} last month, we presented our new architecture design. The audience asked insightful questions about scalability and security.\", event)\n }\n MemoryType::Personal => {\n let preferences = [\"coffee\", \"tea\", \"morning meetings\", \"agile methodology\"];\n let preference = preferences[self.rng.gen_range(0..preferences.len())];\n format!(\"I prefer {} in the morning as it helps me focus better on complex tasks. This has been consistent for the past few years.\", preference)\n }\n };\n \n // 添加一些随机变化\n let variations = [\n \" This is important for future reference.\",\n \" We should consider this in our planning.\",\n \" This information was verified by multiple sources.\",\n \" Additional details may be needed for implementation.\",\n ];\n \n let variation = variations[self.rng.gen_range(0..variations.len())];\n format!(\"{}{}\", base_content, variation)\n }\n \n /// 生成查询\n fn generate_query(&mut self, category: &str, complexity: &str) -> String {\n let base_query = match category {\n \"technology\" => match complexity {\n \"simple\" => \"How to deploy application?\",\n \"medium\" => \"What are the best practices for API design in microservices?\",\n \"complex\" => \"How can we implement zero-downtime deployment with Kubernetes and Istio while maintaining data consistency?\",\n _ => \"Technology question\",\n },\n \"science\" => match complexity {\n \"simple\" => \"What is machine learning?\",\n \"medium\" => \"How does gradient descent optimization work in neural networks?\",\n \"complex\" => \"What are the implications of quantum entanglement for secure communication protocols?\",\n _ => \"Science question\",\n },\n \"business\" => match complexity {\n \"simple\" => \"What is ROI?\",\n \"medium\" => \"How to calculate customer lifetime value for SaaS businesses?\",\n \"complex\" => \"What strategies can be employed to optimize supply chain resilience while minimizing operational costs in global markets?\",\n _ => \"Business question\",\n },\n \"health\" => match complexity {\n \"simple\" => \"What is BMI?\",\n \"medium\" => \"How does intermittent fasting affect metabolic health?\",\n \"complex\" => \"What are the long-term implications of CRISPR gene editing on hereditary disease prevention and ethical considerations?\",\n _ => \"Health question\",\n },\n _ => \"General question\",\n };\n \n base_query.to_string()\n }\n \n /// 生成输入文本\n fn generate_input_text(&mut self, category: &str, memory_type: &MemoryType) -> String {\n let base_text = match (category, memory_type) {\n (\"technology\", MemoryType::Procedural) => {\n \"To deploy the application, first ensure all tests pass, then build the Docker image, push it to the registry, and update the Kubernetes deployment. Monitor the rollout status and verify health checks.\"\n }\n (\"science\", MemoryType::Factual) => {\n \"Photosynthesis is the process by which plants convert light energy into chemical energy, producing oxygen as a byproduct. This occurs in chloroplasts and requires water and carbon dioxide.\"\n }\n (\"business\", MemoryType::Conversational) => {\n \"In our quarterly review meeting, we discussed the declining user engagement metrics. The marketing team suggested A/B testing new onboarding flows, while engineering proposed performance optimizations.\"\n }\n (\"health\", MemoryType::Personal) => {\n \"I've been tracking my sleep patterns and noticed I sleep better when I avoid screens an hour before bed and maintain a consistent sleep schedule, even on weekends.\"\n }\n _ => {\n \"This is a sample text for testing memory system capabilities. It contains multiple pieces of information that should be extracted and processed appropriately.\"\n }\n };\n \n base_text.to_string()\n }\n \n /// 生成预期事实\n fn generate_expected_facts(&mut self, input_text: &str) -> Vec {\n // 简化的事实提取:将文本分成句子\n input_text\n .split('.')\n .map(|s| s.trim())\n .filter(|s| !s.is_empty())\n .map(|s| s.to_string())\n .take(3) // 最多取3个事实\n .collect()\n }\n \n /// 选择相关记忆\n fn select_relevant_memories(\n &mut self,\n _query: &str,\n memories: &HashMap,\n count: usize,\n ) -> Vec {\n let memory_ids: Vec = memories.keys().cloned().collect();\n \n if memory_ids.is_empty() || count == 0 {\n return Vec::new();\n }\n \n // 简化:随机选择一些记忆作为相关记忆\n // 在实际实现中,应该基于语义相似度选择\n let mut selected = Vec::new();\n let mut attempts = 0;\n let max_attempts = count * 3;\n \n while selected.len() < count && attempts < max_attempts {\n let idx = self.rng.gen_range(0..memory_ids.len());\n let memory_id = &memory_ids[idx];\n \n if !selected.contains(memory_id) {\n selected.push(memory_id.clone());\n }\n \n attempts += 1;\n }\n \n selected\n }\n \n /// 采样记忆类型\n fn sample_memory_type(&mut self) -> MemoryType {\n let rand_val = self.rng.gen_range(0.0..1.0);\n let mut cumulative = 0.0;\n \n for (memory_type, probability) in &self.config.memory_type_distribution {\n cumulative += probability;\n if rand_val <= cumulative {\n return memory_type.clone();\n }\n }\n \n // 默认返回对话型记忆\n MemoryType::Conversational\n }\n \n /// 采样查询类别\n fn sample_query_category(&mut self) -> String {\n let idx = self.rng.gen_range(0..self.config.query_categories.len());\n self.config.query_categories[idx].clone()\n }\n \n /// 采样复杂度\n fn sample_complexity(&mut self) -> String {\n let rand_val = self.rng.gen_range(0.0..1.0);\n let mut cumulative = 0.0;\n \n for (complexity, probability) in &self.config.complexity_distribution {\n cumulative += probability;\n if rand_val <= cumulative {\n return complexity.clone();\n }\n }\n \n \"medium\".to_string()\n }\n \n /// 保存数据集到文件\n pub fn save_dataset(\n &self,\n dataset: &T,\n output_path: &str,\n ) -> Result<()> {\n let json = serde_json::to_string_pretty(dataset)\n .context(\"序列化数据集失败\")?;\n \n // 确保目录存在\n if let Some(parent) = std::path::Path::new(output_path).parent() {\n fs::create_dir_all(parent)\n .context(format!(\"创建目录失败: {:?}\", parent))?;\n }\n \n fs::write(output_path, json)\n .context(format!(\"写入数据集文件失败: {}\", output_path))?;\n \n info!(\"数据集已保存到: {}\", output_path);\n Ok(())\n }\n}\n\n/// 生成测试数据集(公共接口)\npub async fn generate_test_dataset(\n dataset_type: &str,\n output_dir: &std::path::Path,\n size: usize,\n use_lab_data: bool,\n) -> Result<()> {\n if use_lab_data {\n // 使用实验室数据生成数据集\n info!(\"使用实验室数据生成数据集\");\n \n match dataset_type.to_lowercase().as_str() {\n \"recall\" => {\n lab_data_integration::generate_lab_dataset(\n \"recall\",\n \"example_lab_dataset\",\n output_dir,\n size,\n ).await?;\n }\n \"effectiveness\" => {\n lab_data_integration::generate_lab_dataset(\n \"effectiveness\",\n \"example_lab_dataset\",\n output_dir,\n size,\n ).await?;\n }\n \"all\" => {\n // 生成召回率数据集\n lab_data_integration::generate_lab_dataset(\n \"recall\",\n \"example_lab_dataset\",\n output_dir,\n size,\n ).await?;\n \n // 生成有效性数据集\n lab_data_integration::generate_lab_dataset(\n \"effectiveness\",\n \"example_lab_dataset\",\n output_dir,\n size,\n ).await?;\n }\n _ => {\n anyhow::bail!(\"未知的数据集类型: {}\", dataset_type);\n }\n }\n } else {\n // 使用模拟数据生成数据集\n info!(\"使用模拟数据生成数据集\");\n \n let mut config = GeneratorConfig::default();\n config.dataset_size = size;\n \n let mut generator = DatasetGenerator::new(config);\n \n match dataset_type.to_lowercase().as_str() {\n \"recall\" => {\n let dataset = generator.generate_recall_dataset()?;\n let output_path = output_dir.join(\"test_cases/recall_test_cases.json\");\n generator.save_dataset(&dataset, output_path.to_str().unwrap())?;\n }\n \"effectiveness\" => {\n let dataset = generator.generate_effectiveness_dataset()?;\n let output_path = output_dir.join(\"test_cases/effectiveness_test_cases.json\");\n generator.save_dataset(&dataset, output_path.to_str().unwrap())?;\n }\n \"all\" => {\n // 生成召回率数据集\n let recall_dataset = generator.generate_recall_dataset()?;\n let recall_path = output_dir.join(\"test_cases/recall_test_cases.json\");\n generator.save_dataset(&recall_dataset, recall_path.to_str().unwrap())?;\n \n // 生成有效性数据集\n let effectiveness_dataset = generator.generate_effectiveness_dataset()?;\n let effectiveness_path = output_dir.join(\"test_cases/effectiveness_test_cases.json\");\n generator.save_dataset(&effectiveness_dataset, effectiveness_path.to_str().unwrap())?;\n }\n _ => {\n anyhow::bail!(\"未知的数据集类型: {}\", dataset_type);\n }\n }\n }\n \n Ok(())\n}\n\n/// 验证数据集\npub async fn validate_dataset(\n dataset_path: &std::path::Path,\n dataset_type: &str,\n) -> Result<()> {\n let content = fs::read_to_string(dataset_path)\n .context(format!(\"读取数据集文件失败: {:?}\", dataset_path))?;\n \n match dataset_type.to_lowercase().as_str() {\n \"recall\" => {\n let dataset: RecallTestDataset = serde_json::from_str(&content)\n .context(\"解析召回率数据集失败\")?;\n info!(\"召回率数据集验证通过: {}个测试用例, {}个记忆\",\n dataset.test_cases.len(), dataset.memories.len());\n }\n \"effectiveness\" => {\n let dataset: EffectivenessTestDataset = serde_json::from_str(&content)\n .context(\"解析有效性数据集失败\")?;\n info!(\"有效性数据集验证通过: {}个测试用例, {}个现有记忆\",\n dataset.test_cases.len(), dataset.existing_memories.len());\n }\n _ => {\n anyhow::bail!(\"未知的数据集类型: {}\", dataset_type);\n }\n }\n \n Ok(())\n}" + "name": "add.py", + "source_summary": "import json\nimport os\nimport subprocess\nimport time\nimport logging\nfrom pathlib import Path\nfrom typing import Tuple\n\nfrom tqdm import tqdm\n\nfrom .config_utils import check_openai_config, get_config_value, validate_config\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n\nclass CortexMemAdd:\n def __init__(self, data_path=None, batch_size=2, config_path=None):\n self.batch_size = batch_size\n self.data_path = data_path\n self.data = None\n self.config_path = config_path or self._find_config_file()\n\n # Track statistics\n self.stats = {\n \"total_conversations\": 0,\n \"successful_conversations\": 0,\n \"failed_conversations\": 0,\n \"total_memories\": 0,\n \"successful_memories\": 0,\n \"failed_memories\": 0\n }\n\n # Validate config file\n if not validate_config(self.config_path):\n raise ValueError(f\"Invalid config file: {self.config_path}\")\n\n # Check OpenAI configuration\n if not check_openai_config(self.config_path):\n raise ValueError(\n f\"OpenAI configuration not properly set in {self.config_path}\"\n )\n\n if data_path:\n self.load_data()\n\n def _find_config_file(self):\n \"\"\"Find config.toml file in standard locations\"\"\"\n # Check current directory\n if os.path.exists(\"config.toml\"):\n return \"config.toml\"\n\n # Check parent directories\n current_dir = Path.cwd()\n for parent in current_dir.parents:\n config_file = parent / \"config.toml\"\n if config_file.exists():\n return str(config_file)\n\n # Check examples directory\n examples_config = (\n Path(__file__).parent.parent.parent.parent / \"examples\" / \"config.toml\"\n )\n if examples_config.exists():\n return str(examples_config)\n\n # Check project root\n project_root = Path(__file__).parent.parent.parent.parent.parent\n config_file = project_root / \"config.toml\"\n if config_file.exists():\n return str(config_file)\n\n raise FileNotFoundError(\"Could not find config.toml file\")\n\n def load_data(self):\n if not self.data_path:\n raise ValueError(\"data_path not set\")\n with open(self.data_path, \"r\") as f:\n self.data = json.load(f)\n return self.data\n\n def _run_cortex_mem_cli(self, args, max_retries=3):\n \"\"\"Run cortex-mem-cli command with retry logic\"\"\"\n for attempt in range(max_retries):\n try:\n # First, ensure the project is built (only on first attempt)\n if attempt == 0:\n build_cmd = [\"cargo\", \"build\", \"-p\", \"cortex-mem-cli\", \"--release\"]\n result = subprocess.run(build_cmd, capture_output=True, text=True, timeout=300)\n if result.returncode != 0:\n logger.warning(f\"Build warning: {result.stderr}\")\n\n # Use absolute path for config file to avoid path resolution issues\n config_path = os.path.abspath(self.config_path)\n\n # Run the CLI with absolute config file path\n cmd = [\"cargo\", \"run\", \"-p\", \"cortex-mem-cli\", \"--quiet\", \"--\"]\n cmd.extend([\"--config\", config_path])\n cmd.extend(args)\n\n # Use project root as working directory (examples/lomoco-evaluation -> cortex-mem)\n project_root = Path(__file__).parent.parent.parent.parent\n \n # Use UTF-8 encoding to avoid GBK codec errors on Windows\n result = subprocess.run(\n cmd, \n capture_output=True, \n text=True, \n encoding='utf-8',\n timeout=60,\n cwd=str(project_root)\n )\n\n if result.returncode != 0:\n if attempt < max_retries - 1:\n logger.warning(f\"CLI command failed (attempt {attempt+1}/{max_retries}): {result.stderr}\")\n time.sleep(2 ** attempt) # Exponential backoff\n continue\n else:\n logger.error(f\"CLI command failed after {max_retries} attempts: {result.stderr}\")\n\n return result.returncode == 0, result.stdout, result.stderr\n except subprocess.TimeoutExpired:\n logger.warning(f\"CLI command timed out (attempt {attempt+1}/{max_retries})\")\n if attempt < max_retries - 1:\n time.sleep(2 ** attempt)\n continue\n return False, \"\", \"Command timed out\"\n except Exception as e:\n logger.error(f\"Error running CLI (attempt {attempt+1}/{max_retries}): {e}\")\n if attempt < max_retries - 1:\n time.sleep(2 ** attempt)\n continue\n return False, \"\", str(e)\n\n return False, \"\", \"Max retries exceeded\"\n\n def add_memory(\n self, user_id, content, memory_type=\"conversational\"\n ):\n \"\"\"Add a memory using cortex-mem-cli with error tracking\"\"\"\n args = [\n \"add\",\n \"--content\",\n content,\n \"--user-id\",\n user_id,\n \"--memory-type\",\n memory_type,\n ]\n\n success, stdout, stderr = self._run_cortex_mem_cli(args)\n\n if not success:\n logger.error(f\"Failed to add memory for user {user_id}: {stderr}\")\n else:\n logger.debug(f\"Successfully added memory for user {user_id}\")\n\n return success\n\n def add_memories_for_speaker(self, speaker, messages, timestamp, desc):\n \"\"\"Add memories for a speaker with error tracking\"\"\"\n total_batches = (len(messages) + self.batch_size - 1) // self.batch_size\n failed_batches = 0\n \n for i in tqdm(range(0, len(messages), self.batch_size), desc=desc):\n batch_messages = messages[i : i + self.batch_size]\n\n # Combine batch messages into single content\n content = \"\\n\".join([msg.get(\"content\", \"\") for msg in batch_messages])\n\n # Add timestamp as metadata\n metadata = f\"Timestamp: {timestamp}\"\n content_with_metadata = f\"{metadata}\\n{content}\"\n\n # Add memory with error tracking\n success = self.add_memory(\n speaker,\n content_with_metadata,\n memory_type=\"conversational\",\n )\n\n if success:\n self.stats[\"successful_memories\"] += 1\n else:\n self.stats[\"failed_memories\"] += 1\n failed_batches += 1\n\n self.stats[\"total_memories\"] += 1\n \n # Small delay between batches to avoid rate limiting\n time.sleep(0.3)\n \n if failed_batches > 0:\n logger.warning(f\"{failed_batches}/{total_batches} batches failed for {speaker}\")\n\n def process_conversation(self, item, idx):\n \"\"\"Process a single conversation with error handling\"\"\"\n try:\n conversation = item.get(\"conversation\", {})\n speaker_a = conversation.get(\"speaker_a\", \"SpeakerA\")\n speaker_b = conversation.get(\"speaker_b\", \"SpeakerB\")\n\n speaker_a_user_id = f\"{speaker_a}_{idx}\"\n speaker_b_user_id = f\"{speaker_b}_{idx}\"\n\n # Note: Cortex Mem doesn't have a delete_all function in CLI\n # We'll rely on unique user IDs for each conversation\n\n for key in conversation.keys():\n if key in [\"speaker_a\", \"speaker_b\"] or \"date\" in key or \"timestamp\" in key:\n continue\n\n date_time_key = key + \"_date_time\"\n timestamp = conversation.get(date_time_key, \"2024-01-01 00:00:00\")\n chats = conversation[key]\n\n messages = []\n messages_reverse = []\n for chat in chats:\n speaker = chat.get(\"speaker\", \"\")\n text = chat.get(\"text\", \"\")\n \n if speaker == speaker_a:\n messages.append(\n {\"role\": \"user\", \"content\": f\"{speaker_a}: {text}\"}\n )\n messages_reverse.append(\n {\"role\": \"assistant\", \"content\": f\"{speaker_a}: {text}\"}\n )\n elif speaker == speaker_b:\n messages.append(\n {\"role\": \"assistant\", \"content\": f\"{speaker_b}: {text}\"}\n )\n messages_reverse.append(\n {\"role\": \"user\", \"content\": f\"{speaker_b}: {text}\"}\n )\n else:\n logger.warning(f\"Unknown speaker: {speaker}\")\n\n # Add memories for both speakers\n self.add_memories_for_speaker(\n speaker_a_user_id,\n messages,\n timestamp,\n f\"Adding Memories for {speaker_a}\",\n )\n \n time.sleep(0.3) # Small delay between speakers\n \n self.add_memories_for_speaker(\n speaker_b_user_id,\n messages_reverse,\n timestamp,\n f\"Adding Memories for {speaker_b}\",\n )\n\n self.stats[\"successful_conversations\"] += 1\n logger.info(f\"✅ Successfully processed conversation {idx}\")\n\n except Exception as e:\n self.stats[\"failed_conversations\"] += 1\n logger.error(f\"❌ Failed to process conversation {idx}: {e}\")\n # Continue processing other conversations\n\n self.stats[\"total_conversations\"] += 1\n\n def process_all_conversations(self, max_workers=1):\n \"\"\"Process all conversations sequentially for stability\"\"\"\n if not self.data:\n raise ValueError(\n \"No data loaded. Please set data_path and call load_data() first.\"\n )\n\n logger.info(f\"Starting to process {len(self.data)} conversations...\")\n \n # Process conversations sequentially for stability\n for idx, item in enumerate(self.data):\n self.process_conversation(item, idx)\n \n # Small delay between conversations to avoid overwhelming the system\n time.sleep(0.5)\n \n # Print summary\n self.print_summary()\n\n def print_summary(self):\n \"\"\"Print processing summary\"\"\"\n print(\"\\n\" + \"=\" * 60)\n print(\"📊 PROCESSING SUMMARY\")\n print(\"=\" * 60)\n print(f\"Total Conversations: {self.stats['total_conversations']}\")\n print(f\"Successful: {self.stats['successful_conversations']}\")\n print(f\"Failed: {self.stats['failed_conversations']}\")\n if self.stats['total_conversations'] > 0:\n print(f\"Success Rate: {self.stats['successful_conversations']/self.stats['total_conversations']*100:.1f}%\")\n print(f\"\\nTotal Memories: {self.stats['total_memories']}\")\n print(f\"Successful: {self.stats['successful_memories']}\")\n print(f\"Failed: {self.stats['failed_memories']}\")\n if self.stats['total_memories'] > 0:\n print(f\"Success Rate: {self.stats['successful_memories']/self.stats['total_memories']*100:.1f}%\")\n print(\"=\" * 60 + \"\\n\")\n" }, "complexity_metrics": { - "cyclomatic_complexity": 39.0, - "lines_of_code": 597, - "number_of_classes": 2, - "number_of_functions": 27 + "cyclomatic_complexity": 42.0, + "lines_of_code": 302, + "number_of_classes": 1, + "number_of_functions": 9 }, "dependencies": [ { - "dependency_type": "error_handling", - "is_external": true, - "line_number": null, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "core_library", + "dependency_type": "standard", "is_external": false, - "line_number": null, - "name": "cortex_mem_core", - "path": null, - "version": null - }, - { - "dependency_type": "random_number_generation", - "is_external": true, - "line_number": null, - "name": "rand", + "line_number": 1, + "name": "json", "path": null, "version": null }, { - "dependency_type": "serialization", - "is_external": true, - "line_number": null, - "name": "serde", + "dependency_type": "standard", + "is_external": false, + "line_number": 2, + "name": "os", "path": null, "version": null }, - { - "dependency_type": "cryptographic_hash", - "is_external": true, - "line_number": null, - "name": "sha2", + { + "dependency_type": "standard", + "is_external": false, + "line_number": 3, + "name": "subprocess", "path": null, "version": null }, { - "dependency_type": "standard_library", + "dependency_type": "standard", "is_external": false, - "line_number": null, - "name": "std", + "line_number": 4, + "name": "time", "path": null, "version": null }, { - "dependency_type": "logging", - "is_external": true, - "line_number": null, - "name": "tracing", + "dependency_type": "standard", + "is_external": false, + "line_number": 5, + "name": "logging", "path": null, "version": null }, { - "dependency_type": "local_module", + "dependency_type": "standard", "is_external": false, - "line_number": null, - "name": "super::types", - "path": "./types.rs", + "line_number": 6, + "name": "Path", + "path": null, "version": null }, { - "dependency_type": "local_module", + "dependency_type": "standard", "is_external": false, - "line_number": null, - "name": "super::lab_data_integration", - "path": "./lab_data_integration.rs", + "line_number": 7, + "name": "typing", + "path": null, "version": null }, { - "dependency_type": "date_time", + "dependency_type": "external", "is_external": true, - "line_number": null, - "name": "chrono", + "line_number": 9, + "name": "tqdm", "path": null, "version": null }, { - "dependency_type": "data_parsing", - "is_external": true, - "line_number": null, - "name": "csv", - "path": null, + "dependency_type": "internal", + "is_external": false, + "line_number": 11, + "name": "config_utils", + "path": "./config_utils", "version": null } ], - "detailed_description": "该组件是cortex-mem-evaluation项目中的测试数据集生成工具,主要用于生成评估记忆系统性能所需的测试数据集。其核心功能包括:1) 使用随机数据生成召回率测试数据集,包含测试用例和相关记忆库;2) 生成有效性测试数据集,用于评估系统处理新信息的能力;3) 支持从外部实验室数据源集成真实数据生成更高质量的测试集;4) 提供数据集序列化保存和验证功能。\n\n组件通过DatasetGenerator类实现主要逻辑,使用配置驱动的方式控制数据生成过程。配置参数包括随机种子、数据集大小、平均相关记忆数、记忆类型分布等,使得生成的数据具有可重复性和可控性。对于召回率测试,系统会先生成一个较大的记忆库,然后为每个查询选择相关的记忆作为预期结果。对于有效性测试,则关注输入文本到记忆实体的转换质量,包括事实提取、记忆类型判断、重要性评分等。\n\n组件还提供了与实验室数据集成的接口,可以通过lab_data_integration模块加载真实世界的数据样本,创建更具挑战性的测试场景。这种设计既支持快速原型测试(使用模拟数据),也支持高保真度评估(使用真实数据)。\n\n代码结构清晰,职责分离良好,实现了可配置、可扩展的数据生成框架。通过异步函数暴露公共接口,便于在命令行工具或自动化测试流程中调用。整体上是一个高质量的测试基础设施组件。", + "detailed_description": "This component provides a Python interface to interact with the `cortex-mem-cli` Rust binary for adding conversational memories to a memory system. It reads conversation data in JSON format and processes each conversation by extracting speaker interactions, batching messages, and invoking the CLI tool to store them as memories with metadata. The class includes robust error handling with retry logic, logging, and statistics tracking for monitoring success/failure rates during bulk operations. It supports configuration discovery, OpenAI credential validation, and sequential processing to ensure stability when handling large datasets.", "interfaces": [ { - "description": "测试数据集生成器主类,负责协调数据生成过程", - "interface_type": "struct", - "name": "DatasetGenerator", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "数据生成器配置,控制数据生成的各种参数", - "interface_type": "struct", - "name": "GeneratorConfig", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "创建新的数据集生成器实例", - "interface_type": "method", - "name": "new", + "description": "Initialize the memory adder with data path, batch size, and config path. Validates configuration and loads data if path provided.", + "interface_type": "constructor", + "name": "CortexMemAdd.__init__", "parameters": [ { - "description": "生成器配置", - "is_optional": false, - "name": "config", - "param_type": "GeneratorConfig" + "description": "Path to JSON file containing conversation data", + "is_optional": true, + "name": "data_path", + "param_type": "str" + }, + { + "description": "Number of messages to batch together when adding memories", + "is_optional": true, + "name": "batch_size", + "param_type": "int" + }, + { + "description": "Path to config.toml file; if not provided, auto-discovered", + "is_optional": true, + "name": "config_path", + "param_type": "str" } ], - "return_type": "DatasetGenerator", - "visibility": "public" - }, - { - "description": "生成召回率测试数据集", - "interface_type": "method", - "name": "generate_recall_dataset", - "parameters": [], - "return_type": "Result", + "return_type": "None", "visibility": "public" }, { - "description": "生成有效性测试数据集", + "description": "Load conversation data from the specified JSON file path.", "interface_type": "method", - "name": "generate_effectiveness_dataset", + "name": "load_data", "parameters": [], - "return_type": "Result", + "return_type": "dict", "visibility": "public" }, { - "description": "将数据集保存到文件", + "description": "Add a single memory entry via CLI and return success status.", "interface_type": "method", - "name": "save_dataset", + "name": "add_memory", "parameters": [ { - "description": "要保存的数据集", + "description": "Unique identifier for the user whose memory is being added", "is_optional": false, - "name": "dataset", - "param_type": "T: serde::Serialize" + "name": "user_id", + "param_type": "str" }, { - "description": "输出路径", + "description": "Content of the memory to be added", "is_optional": false, - "name": "output_path", - "param_type": "&str" + "name": "content", + "param_type": "str" + }, + { + "description": "Type of memory (default: 'conversational')", + "is_optional": true, + "name": "memory_type", + "param_type": "str" } ], - "return_type": "Result<()>", + "return_type": "bool", "visibility": "public" }, { - "description": "生成测试数据集的公共接口函数", - "interface_type": "function", - "name": "generate_test_dataset", + "description": "Add multiple memories for a speaker in batches, with progress tracking and error handling.", + "interface_type": "method", + "name": "add_memories_for_speaker", "parameters": [ { - "description": "数据集类型(recall/effectiveness/all)", + "description": "Speaker identifier", "is_optional": false, - "name": "dataset_type", - "param_type": "&str" + "name": "speaker", + "param_type": "str" }, { - "description": "输出目录", + "description": "List of message objects to be added as memories", "is_optional": false, - "name": "output_dir", - "param_type": "&std::path::Path" + "name": "messages", + "param_type": "list" }, { - "description": "数据集大小", + "description": "Timestamp for the conversation block", "is_optional": false, - "name": "size", - "param_type": "usize" + "name": "timestamp", + "param_type": "str" }, { - "description": "是否使用实验室数据", + "description": "Description for progress bar display", "is_optional": false, - "name": "use_lab_data", - "param_type": "bool" + "name": "desc", + "param_type": "str" } ], - "return_type": "Result<()>", + "return_type": "None", "visibility": "public" }, { - "description": "验证数据集文件的完整性和正确性", - "interface_type": "function", - "name": "validate_dataset", + "description": "Process a single conversation by extracting speaker turns and adding memories for both participants.", + "interface_type": "method", + "name": "process_conversation", "parameters": [ { - "description": "数据集文件路径", + "description": "Conversation data item from loaded JSON", "is_optional": false, - "name": "dataset_path", - "param_type": "&std::path::Path" + "name": "item", + "param_type": "dict" }, { - "description": "数据集类型", + "description": "Index of the conversation for logging purposes", "is_optional": false, - "name": "dataset_type", - "param_type": "&str" + "name": "idx", + "param_type": "int" } ], - "return_type": "Result<()>", + "return_type": "None", + "visibility": "public" + }, + { + "description": "Process all loaded conversations sequentially with delays between to prevent system overload.", + "interface_type": "method", + "name": "process_all_conversations", + "parameters": [ + { + "description": "Maximum number of worker threads (not actually used - processing is sequential)", + "is_optional": true, + "name": "max_workers", + "param_type": "int" + } + ], + "return_type": "None", + "visibility": "public" + }, + { + "description": "Print a formatted summary of processing statistics including success/failure rates.", + "interface_type": "method", + "name": "print_summary", + "parameters": [], + "return_type": "None", "visibility": "public" } ], "responsibilities": [ - "生成用于召回率评估的测试数据集,包含查询-记忆相关性标注", - "生成用于有效性评估的测试数据集,包含预期输出标准", - "管理数据生成配置,支持不同类型和规模的数据集生成", - "集成实验室真实数据源,提升测试数据质量", - "提供数据集序列化、保存和验证功能" + "Manage configuration discovery and validation for the memory system", + "Orchestrate memory addition via external CLI tool with retry and error handling", + "Process conversational data by batching messages and adding them as memories", + "Track and report processing statistics including success and failure rates", + "Provide structured logging and progress feedback during long-running operations" ] }, { "code_dossier": { "code_purpose": "specificfeature", - "description": "协调和运行完整的评估实验,支持召回率和有效性评估", - "file_path": "examples/cortex-mem-evaluation/src/runner/experiment_runner.rs", + "description": "Implements a memory search and question-answering system using external CLI tools and LLMs to retrieve and synthesize information from conversation memories.", + "file_path": "examples/lomoco-evaluation/src/cortex_mem/search.py", "functions": [ - "new", - "load_config", - "run_full_evaluation", - "run_recall_evaluation", - "run_effectiveness_evaluation", - "run_real_evaluation", - "run_real_recall_evaluation", - "run_real_effectiveness_evaluation", - "generate_real_evaluation_report" + "__init__", + "_find_config_file", + "_run_cortex_mem_cli", + "search_memory", + "answer_question", + "process_question", + "process_data_file" ], "importance_score": 0.8, "interfaces": [ - "ExperimentRunner::new", - "ExperimentRunner::run_full_evaluation", - "ExperimentRunner::run_recall_evaluation", - "ExperimentRunner::run_effectiveness_evaluation", - "ExperimentRunner::run_real_evaluation", - "ExperimentRunner::run_real_recall_evaluation", - "ExperimentRunner::run_real_effectiveness_evaluation", - "ExperimentRunner::generate_real_evaluation_report", - "ExperimentConfig", - "ExperimentRunner" + "CortexMemSearch.__init__", + "CortexMemSearch._find_config_file", + "CortexMemSearch._run_cortex_mem_cli", + "CortexMemSearch.search_memory", + "CortexMemSearch.answer_question", + "CortexMemSearch.process_question", + "CortexMemSearch.process_data_file" ], - "name": "experiment_runner.rs", - "source_summary": "//! 实验运行器\n//! \n//! 负责协调和运行完整的评估实验\n\nuse anyhow::{Result, Context};\nuse config::Config;\nuse std::path::PathBuf;\nuse tracing::{info, error};\n\nuse crate::{\n evaluator::{\n RealRecallEvaluator, RealRecallEvaluationConfig,\n RealEffectivenessEvaluator, RealEffectivenessEvaluationConfig,\n },\n dataset::DatasetLoader,\n memory,\n};\n\n/// 实验运行器\npub struct ExperimentRunner {\n /// 配置\n config: ExperimentConfig,\n /// 输出目录\n output_dir: PathBuf,\n}\n\n/// 实验配置\n#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\npub struct ExperimentConfig {\n /// 评估模式\n pub mode: String,\n /// 召回率评估配置\n pub recall_config: RealRecallEvaluationConfig,\n /// 有效性评估配置\n pub effectiveness_config: RealEffectivenessEvaluationConfig,\n /// 是否保存详细结果\n pub save_detailed_results: bool,\n /// MemoryManager配置文件路径(可选)\n #[serde(default)]\n pub memory_config_path: Option,\n}\n\nimpl ExperimentRunner {\n /// 创建新的实验运行器\n pub fn new(config_path: PathBuf, output_dir: PathBuf) -> Result {\n // 加载配置\n let config = Self::load_config(&config_path)?;\n \n // 创建输出目录\n std::fs::create_dir_all(&output_dir)\n .context(format!(\"创建输出目录失败: {:?}\", output_dir))?;\n \n Ok(Self { config, output_dir })\n }\n \n /// 加载配置\n fn load_config(config_path: &PathBuf) -> Result {\n let config_builder = Config::builder()\n .add_source(config::File::from(config_path.clone()))\n .add_source(config::Environment::with_prefix(\"CORTEX_MEM_EVAL\"))\n .build()\n .context(\"加载配置失败\")?;\n \n let mode = config_builder.get_string(\"general.mode\")\n .unwrap_or_else(|_| \"all\".to_string());\n \n let recall_config = RealRecallEvaluationConfig {\n k_values: config_builder.get_array(\"recall_evaluation.k_values\")\n .unwrap_or_else(|_| vec![1.into(), 3.into(), 5.into(), 10.into()])\n .into_iter()\n .filter_map(|v| v.into_int().ok().map(|n| n as usize))\n .collect(),\n similarity_thresholds: config_builder.get_array(\"recall_evaluation.similarity_thresholds\")\n .unwrap_or_else(|_| vec![0.7.into(), 0.8.into(), 0.9.into()])\n .into_iter()\n .filter_map(|v| v.into_float().ok().map(|f| f as f32))\n .collect(),\n max_results_per_query: config_builder.get_int(\"recall_evaluation.max_results_per_query\")\n .unwrap_or(20) as usize,\n save_detailed_results: config_builder.get_bool(\"recall_evaluation.save_detailed_results\")\n .unwrap_or(true),\n timeout_seconds: config_builder.get_int(\"recall_evaluation.timeout_seconds\")\n .unwrap_or(30) as u64,\n enable_parallel_evaluation: config_builder.get_bool(\"recall_evaluation.enable_parallel_evaluation\")\n .unwrap_or(true),\n verify_memory_integrity: config_builder.get_bool(\"recall_evaluation.verify_memory_integrity\")\n .unwrap_or(true),\n test_cases_path: config_builder.get_string(\"recall_evaluation.test_cases_path\")\n .unwrap_or_else(|_| \"data/test_cases/lab_recall_dataset.json\".to_string()),\n };\n \n let effectiveness_config = RealEffectivenessEvaluationConfig {\n verify_fact_extraction: config_builder.get_bool(\"effectiveness_evaluation.verify_fact_extraction\")\n .unwrap_or(true),\n verify_classification: config_builder.get_bool(\"effectiveness_evaluation.verify_classification\")\n .unwrap_or(true),\n verify_importance_evaluation: config_builder.get_bool(\"effectiveness_evaluation.verify_importance_evaluation\")\n .unwrap_or(true),\n verify_deduplication: config_builder.get_bool(\"effectiveness_evaluation.verify_deduplication\")\n .unwrap_or(true),\n verify_memory_update: config_builder.get_bool(\"effectiveness_evaluation.verify_memory_update\")\n .unwrap_or(true),\n importance_score_tolerance: config_builder.get_int(\"effectiveness_evaluation.importance_score_tolerance\")\n .unwrap_or(1) as u8,\n timeout_seconds: config_builder.get_int(\"effectiveness_evaluation.timeout_seconds\")\n .unwrap_or(30) as u64,\n enable_verbose_logging: config_builder.get_bool(\"effectiveness_evaluation.enable_verbose_logging\")\n .unwrap_or(false),\n cleanup_test_data: config_builder.get_bool(\"effectiveness_evaluation.cleanup_test_data\")\n .unwrap_or(true),\n test_cases_path: config_builder.get_string(\"effectiveness_evaluation.test_cases_path\")\n .unwrap_or_else(|_| \"data/test_cases/lab_effectiveness_dataset.json\".to_string()),\n };\n \n let config = ExperimentConfig {\n mode,\n recall_config,\n effectiveness_config,\n save_detailed_results: config_builder.get_bool(\"general.save_detailed_results\")\n .unwrap_or(true),\n memory_config_path: config_builder.get_string(\"general.memory_config_path\").ok(),\n };\n \n info!(\"实验配置加载完成: mode={}\", config.mode);\n Ok(config)\n }\n \n /// 运行完整评估\n pub async fn run_full_evaluation(&self) -> Result<()> {\n info!(\"开始完整评估...\");\n \n // 运行真实评估\n self.run_real_evaluation().await?;\n \n info!(\"完整评估完成\");\n Ok(())\n }\n \n /// 运行召回率评估\n pub async fn run_recall_evaluation(&self) -> Result<()> {\n info!(\"开始召回率评估...\");\n \n // 检查数据集\n let dataset_path = PathBuf::from(&self.config.recall_config.test_cases_path);\n if !dataset_path.exists() {\n anyhow::bail!(\"召回率测试数据集不存在: {:?}\", dataset_path);\n }\n \n // 加载数据集\n let dataset = DatasetLoader::load_recall_dataset(&dataset_path)?;\n \n // 创建MemoryManager实例\n let memory_manager = memory::create_memory_manager_for_real_evaluation(&self.config).await?;\n \n // 创建评估器\n let evaluator = RealRecallEvaluator::new(self.config.recall_config.clone(), memory_manager);\n \n // 运行评估\n let metrics = evaluator.evaluate(&dataset).await?;\n \n // 保存结果\n let result_path = self.output_dir.join(\"real_recall_evaluation_result.json\");\n let result_json = serde_json::to_string_pretty(&metrics)?;\n std::fs::write(result_path, result_json)?;\n \n info!(\"召回率评估完成\");\n Ok(())\n }\n \n /// 运行有效性评估\n pub async fn run_effectiveness_evaluation(&self) -> Result<()> {\n info!(\"开始有效性评估...\");\n \n // 检查数据集\n let dataset_path = PathBuf::from(&self.config.effectiveness_config.test_cases_path);\n if !dataset_path.exists() {\n anyhow::bail!(\"有效性测试数据集不存在: {:?}\", dataset_path);\n }\n \n // 加载数据集\n let dataset = DatasetLoader::load_effectiveness_dataset(&dataset_path)?;\n \n // 创建MemoryManager实例\n let memory_manager = memory::create_memory_manager_for_real_evaluation(&self.config).await?;\n \n // 创建评估器\n let evaluator = RealEffectivenessEvaluator::new(self.config.effectiveness_config.clone(), memory_manager);\n \n // 运行评估\n let metrics = evaluator.evaluate(&dataset).await?;\n \n // 保存结果\n let result_path = self.output_dir.join(\"real_effectiveness_evaluation_result.json\");\n let result_json = serde_json::to_string_pretty(&metrics)?;\n std::fs::write(result_path, result_json)?;\n \n info!(\"有效性评估完成\");\n Ok(())\n }\n \n /// 运行真实评估\n async fn run_real_evaluation(&self) -> Result<()> {\n info!(\"开始真实评估...\");\n \n // 检查数据集路径\n let recall_dataset_path = PathBuf::from(&self.config.recall_config.test_cases_path);\n let effectiveness_dataset_path = PathBuf::from(&self.config.effectiveness_config.test_cases_path);\n \n if !recall_dataset_path.exists() {\n anyhow::bail!(\"召回率测试数据集不存在: {:?}\", recall_dataset_path);\n }\n \n if !effectiveness_dataset_path.exists() {\n anyhow::bail!(\"有效性测试数据集不存在: {:?}\", effectiveness_dataset_path);\n }\n \n info!(\"数据集检查通过:\");\n info!(\" - 召回率数据集: {:?}\", recall_dataset_path);\n info!(\" - 有效性数据集: {:?}\", effectiveness_dataset_path);\n \n // 创建 MemoryManager 实例\n info!(\"创建 MemoryManager 实例...\");\n let memory_manager = memory::create_memory_manager_for_real_evaluation(&self.config).await?;\n info!(\"MemoryManager 实例创建成功\");\n \n // 运行召回率评估\n info!(\"运行真实召回率评估...\");\n self.run_real_recall_evaluation(&memory_manager, &recall_dataset_path).await?;\n \n // 运行有效性评估\n info!(\"运行真实有效性评估...\");\n self.run_real_effectiveness_evaluation(&memory_manager, &effectiveness_dataset_path).await?;\n \n // 生成真实评估报告\n let real_report = self.generate_real_evaluation_report()?;\n let report_path = self.output_dir.join(\"real_evaluation_report.md\");\n std::fs::write(report_path, real_report)?;\n \n info!(\"真实评估完成\");\n info!(\"评估结果已保存到: {:?}\", self.output_dir);\n \n Ok(())\n }\n \n /// 运行真实召回率评估\n async fn run_real_recall_evaluation(\n &self,\n memory_manager: &std::sync::Arc,\n dataset_path: &PathBuf,\n ) -> Result<()> {\n info!(\"运行真实召回率评估...\");\n \n // 加载数据集\n let dataset = DatasetLoader::load_recall_dataset(dataset_path)?;\n \n // 创建评估器\n let evaluator = RealRecallEvaluator::new(self.config.recall_config.clone(), memory_manager.clone());\n \n // 运行评估\n let metrics = evaluator.evaluate(&dataset).await?;\n \n // 保存结果\n let result_path = self.output_dir.join(\"real_recall_evaluation_result.json\");\n let result_json = serde_json::to_string_pretty(&metrics)?;\n std::fs::write(result_path, result_json)?;\n \n info!(\"真实召回率评估完成\");\n Ok(())\n }\n \n /// 运行真实有效性评估\n async fn run_real_effectiveness_evaluation(\n &self,\n memory_manager: &std::sync::Arc,\n dataset_path: &PathBuf,\n ) -> Result<()> {\n info!(\"运行真实有效性评估...\");\n \n // 加载数据集\n let dataset = DatasetLoader::load_effectiveness_dataset(dataset_path)?;\n \n // 创建评估器\n let evaluator = RealEffectivenessEvaluator::new(self.config.effectiveness_config.clone(), memory_manager.clone());\n \n // 运行评估\n let metrics = evaluator.evaluate(&dataset).await?;\n \n // 保存结果\n let result_path = self.output_dir.join(\"real_effectiveness_evaluation_result.json\");\n let result_json = serde_json::to_string_pretty(&metrics)?;\n std::fs::write(result_path, result_json)?;\n \n info!(\"真实有效性评估完成\");\n Ok(())\n }\n \n /// 生成真实评估报告\n fn generate_real_evaluation_report(&self) -> Result {\n let timestamp = chrono::Utc::now().to_rfc3339();\n \n let mut report = String::new();\n \n report.push_str(\"# Cortex-Mem 真实评估报告\\n\\n\");\n report.push_str(\"## 概述\\n\\n\");\n report.push_str(\"本报告展示了 Cortex-Mem 系统的真实评估结果。\\n\\n\");\n \n report.push_str(\"## 评估配置\\n\\n\");\n report.push_str(&format!(\"- **评估模式**: {}\\n\", self.config.mode));\n report.push_str(&format!(\"- **输出目录**: {:?}\\n\", self.output_dir));\n \n report.push_str(\"\\n## 数据集状态\\n\\n\");\n \n let recall_dataset_path = PathBuf::from(&self.config.recall_config.test_cases_path);\n let effectiveness_dataset_path = PathBuf::from(&self.config.effectiveness_config.test_cases_path);\n \n if recall_dataset_path.exists() {\n report.push_str(\"- **召回率数据集**: ✅ 存在\\n\");\n if let Ok(metadata) = std::fs::metadata(&recall_dataset_path) {\n report.push_str(&format!(\" - 文件大小: {} 字节\\n\", metadata.len()));\n }\n } else {\n report.push_str(\"- **召回率数据集**: ❌ 不存在\\n\");\n }\n \n if effectiveness_dataset_path.exists() {\n report.push_str(\"- **有效性数据集**: ✅ 存在\\n\");\n if let Ok(metadata) = std::fs::metadata(&effectiveness_dataset_path) {\n report.push_str(&format!(\" - 文件大小: {} 字节\\n\", metadata.len()));\n }\n } else {\n report.push_str(\"- **有效性数据集**: ❌ 不存在\\n\");\n }\n \n report.push_str(\"\\n## 评估结果\\n\\n\");\n report.push_str(\"评估结果已保存到以下文件:\\n\\n\");\n report.push_str(\"- `real_recall_evaluation_result.json` - 召回率评估结果\\n\");\n report.push_str(\"- `real_effectiveness_evaluation_result.json` - 有效性评估结果\\n\");\n \n report.push_str(\"\\n## 技术栈\\n\\n\");\n report.push_str(\"- **向量存储**: Qdrant\\n\");\n report.push_str(\"- **LLM客户端**: 真实API客户端\\n\");\n report.push_str(\"- **评估框架**: 真实评估器(无模拟代码)\\n\");\n \n report.push_str(\"\\n## 报告信息\\n\\n\");\n report.push_str(&format!(\"- **生成时间**: {}\\n\", timestamp));\n report.push_str(\"- **评估类型**: 真实评估(无模拟代码)\\n\");\n \n Ok(report)\n }\n}\n" + "name": "search.py", + "source_summary": "import json\nimport os\nimport subprocess\nimport time\nfrom collections import defaultdict\nfrom pathlib import Path\n\nfrom jinja2 import Template\nfrom openai import OpenAI\nfrom tqdm import tqdm\n\nfrom .config_utils import check_openai_config, get_config_value, validate_config\n\n\nclass CortexMemSearch:\n def __init__(self, output_path=\"results.json\", top_k=10, config_path=None):\n self.top_k = top_k\n self.results = defaultdict(list)\n self.output_path = output_path\n self.config_path = config_path or self._find_config_file()\n # Answer generation prompt\n self.ANSWER_PROMPT = \"\"\"\nYou are an intelligent memory assistant tasked with retrieving accurate information from conversation memories.\n\n# CONTEXT:\nYou have access to memories from two speakers in a conversation. These memories contain \ntimestamped information that may be relevant to answering the question.\n\n# INSTRUCTIONS:\n1. Carefully analyze all provided memories from both speakers\n2. Pay special attention to the timestamps to determine the answer\n3. If the question asks about a specific event or fact, look for direct evidence in the \n memories\n4. If the memories contain contradictory information, prioritize the most recent memory\n5. If there is a question about time references (like \"last year\", \"two months ago\", \n etc.), calculate the actual date based on the memory timestamp. For example, if a \n memory from 4 May 2022 mentions \"went to India last year,\" then the trip occurred \n in 2021.\n6. Always convert relative time references to specific dates, months, or years. For \n example, convert \"last year\" to \"2022\" or \"two months ago\" to \"March 2023\" based \n on the memory timestamp. Ignore the reference while answering the question.\n7. Focus only on the content of the memories from both speakers. Do not confuse \n character names mentioned in memories with the actual users who created those \n memories.\n8. The answer should be less than 5-6 words.\n\n# APPROACH (Think step by step):\n1. First, examine all memories that contain information related to the question\n2. Examine the timestamps and content of these memories carefully\n3. Look for explicit mentions of dates, times, locations, or events that answer the \n question\n4. If the answer requires calculation (e.g., converting relative time references), \n show your work\n5. Formulate a precise, concise answer based solely on the evidence in the memories\n6. Double-check that your answer directly addresses the question asked\n7. Ensure your final answer is specific and avoids vague time references\n\nMemories for user {{speaker_1_user_id}}:\n\n{{speaker_1_memories}}\n\nMemories for user {{speaker_2_user_id}}:\n\n{{speaker_2_memories}}\n\nQuestion: {{question}}\n\nAnswer:\n\"\"\"\n\n \n # Answer generation prompt\n self.ANSWER_PROMPT = \"\"\"\nYou are an intelligent memory assistant tasked with retrieving accurate information from conversation memories.\n\n# CONTEXT:\nYou have access to memories from two speakers in a conversation. These memories contain \ntimestamped information that may be relevant to answering the question.\n\n# INSTRUCTIONS:\n1. Carefully analyze all provided memories from both speakers\n2. Pay special attention to the timestamps to determine the answer\n3. If the question asks about a specific event or fact, look for direct evidence in the \n memories\n4. If the memories contain contradictory information, prioritize the most recent memory\n5. If there is a question about time references (like \"last year\", \"two months ago\", \n etc.), calculate the actual date based on the memory timestamp. For example, if a \n memory from 4 May 2022 mentions \"went to India last year,\" then the trip occurred \n in 2021.\n6. Always convert relative time references to specific dates, months, or years. For \n example, convert \"last year\" to \"2022\" or \"two months ago\" to \"March 2023\" based \n on the memory timestamp. Ignore the reference while answering the question.\n7. Focus only on the content of the memories from both speakers. Do not confuse \n character names mentioned in memories with the actual users who created those \n memories.\n8. The answer should be less than 5-6 words.\n\n# APPROACH (Think step by step):\n1. First, examine all memories that contain information related to the question\n2. Examine the timestamps and content of these memories carefully\n3. Look for explicit mentions of dates, times, locations, or events that answer the \n question\n4. If the answer requires calculation (e.g., converting relative time references), \n show your work\n5. Formulate a precise, concise answer based solely on the evidence in the memories\n6. Double-check that your answer directly addresses the question asked\n7. Ensure your final answer is specific and avoids vague time references\n\nMemories for user {{speaker_1_user_id}}:\n\n{{speaker_1_memories}}\n\nMemories for user {{speaker_2_user_id}}:\n\n{{speaker_2_memories}}\n\nQuestion: {{question}}\n\nAnswer:\n\"\"\"\n \n # Validate config file\n if not validate_config(self.config_path):\n raise ValueError(f\"Invalid config file: {self.config_path}\")\n \n # Check OpenAI configuration\n if not check_openai_config(self.config_path):\n raise ValueError(\n f\"OpenAI configuration not properly set in {self.config_path}\"\n )\n \n # Initialize OpenAI client from config.toml\n api_key = get_config_value(self.config_path, \"llm\", \"api_key\")\n api_base = get_config_value(self.config_path, \"llm\", \"api_base_url\")\n self.llm_model = get_config_value(self.config_path, \"llm\", \"model_efficient\", \"gpt-3.5-turbo\")\n \n # Create HTTP client with SSL verification disabled for internal APIs\n import httpx\n http_client = httpx.Client(verify=False)\n \n self.openai_client = OpenAI(\n api_key=api_key,\n base_url=api_base,\n http_client=http_client\n )\n \n def _find_config_file(self):\n \"\"\"Find config.toml file in standard locations\"\"\"\n # Check current directory\n if os.path.exists(\"config.toml\"):\n return \"config.toml\"\n \n # Check parent directories\n current_dir = Path.cwd()\n for parent in current_dir.parents:\n config_file = parent / \"config.toml\"\n if config_file.exists():\n return str(config_file)\n \n # Check examples directory\n examples_config = (\n Path(__file__).parent.parent.parent.parent / \"examples\" / \"config.toml\"\n )\n if examples_config.exists():\n return str(examples_config)\n \n # Check project root\n project_root = Path(__file__).parent.parent.parent.parent\n config_file = project_root / \"config.toml\"\n if config_file.exists():\n return str(config_file)\n \n raise FileNotFoundError(\"Could not find config.toml file\")\n \n def _run_cortex_mem_cli(self, args):\n \"\"\"Run cortex-mem-cli command\"\"\"\n # First, ensure the project is built\n build_cmd = [\"cargo\", \"build\", \"-p\", \"cortex-mem-cli\"]\n subprocess.run(build_cmd, capture_output=True, text=True)\n \n # Use absolute path for config file to avoid path resolution issues\n config_path = os.path.abspath(self.config_path)\n \n # Run the CLI with absolute config file path\n cmd = [\"cargo\", \"run\", \"-p\", \"cortex-mem-cli\", \"--quiet\", \"--\"]\n cmd.extend([\"--config\", config_path])\n cmd.extend(args)\n \n try:\n # Use project root as working directory (examples/lomoco-evaluation -> cortex-mem)\n project_root = Path(__file__).parent.parent.parent.parent\n \n # Use UTF-8 encoding to avoid GBK codec errors on Windows\n result = subprocess.run(\n cmd, \n capture_output=True, \n text=True, \n encoding='utf-8',\n cwd=str(project_root)\n )\n \n if result.returncode != 0:\n print(f\"CLI command failed: {result.stderr}\")\n \n return result.returncode == 0, result.stdout, result.stderr\n except Exception as e:\n print(f\"Error running CLI: {e}\")\n return False, \"\", str(e)\n \n def search_memory(self, user_id, query, max_retries=3, retry_delay=1):\n \"\"\"Search for memories using cortex-mem-cli\"\"\"\n start_time = time.time()\n retries = 0\n \n while retries < max_retries:\n try:\n # Build search command\n args = [\n \"search\",\n \"--query\",\n query,\n \"--user-id\",\n user_id,\n \"--limit\",\n str(self.top_k),\n ]\n \n success, stdout, stderr = self._run_cortex_mem_cli(args)\n \n if not success:\n raise RuntimeError(f\"Search failed: {stderr}\")\n \n # Parse the output (assuming JSON output from CLI)\n # This is a simplified parser - adjust based on actual CLI output format\n memories = []\n if stdout.strip():\n try:\n # Try to parse as JSON\n result_data = json.loads(stdout)\n if isinstance(result_data, list):\n for item in result_data:\n memory = {\n \"memory\": item.get(\"content\", \"\"),\n \"timestamp\": item.get(\"created_at\", \"\"),\n \"score\": item.get(\"score\", 0.0),\n }\n memories.append(memory)\n except json.JSONDecodeError:\n # If not JSON, parse line by line\n lines = stdout.strip().split(\"\\n\")\n for line in lines:\n if line.strip():\n memory = {\n \"memory\": line.strip(),\n \"timestamp\": \"\",\n \"score\": 0.0,\n }\n memories.append(memory)\n \n end_time = time.time()\n return memories, None, end_time - start_time\n \n except Exception as e:\n print(f\"Search error: {e}, retrying...\")\n retries += 1\n if retries >= max_retries:\n raise e\n time.sleep(retry_delay)\n \n end_time = time.time()\n return [], None, end_time - start_time\n \n def answer_question(\n self, speaker_1_user_id, speaker_2_user_id, question, answer, category\n ):\n \"\"\"Answer a question using retrieved memories\"\"\"\n # Sequential search to avoid rate limiting\n speaker_1_memories, _, speaker_1_memory_time = self.search_memory(\n speaker_1_user_id, question\n )\n # Add a small delay between searches to avoid rate limiting\n time.sleep(2)\n \n speaker_2_memories, _, speaker_2_memory_time = self.search_memory(\n speaker_2_user_id, question\n )\n # Add a small delay before LLM call\n time.sleep(2)\n \n search_1_memory = [\n f\"{item.get('timestamp', '')}: {item['memory']}\"\n for item in speaker_1_memories\n ]\n search_2_memory = [\n f\"{item.get('timestamp', '')}: {item['memory']}\"\n for item in speaker_2_memories\n ]\n \n template = Template(self.ANSWER_PROMPT)\n answer_prompt = template.render(\n speaker_1_user_id=speaker_1_user_id.split(\"_\")[0],\n speaker_2_user_id=speaker_2_user_id.split(\"_\")[0],\n speaker_1_memories=json.dumps(search_1_memory, indent=4),\n speaker_2_memories=json.dumps(search_2_memory, indent=4),\n question=question,\n )\n \n t1 = time.time()\n response = self.openai_client.chat.completions.create(\n model=self.llm_model,\n messages=[{\"role\": \"system\", \"content\": answer_prompt}],\n temperature=0.0,\n )\n t2 = time.time()\n response_time = t2 - t1\n \n return (\n response.choices[0].message.content,\n speaker_1_memories,\n speaker_2_memories,\n speaker_1_memory_time,\n speaker_2_memory_time,\n None, # graph_memories\n None,\n response_time,\n )\n \n def process_question(self, val, speaker_a_user_id, speaker_b_user_id):\n \"\"\"Process a single question\"\"\"\n question = val.get(\"question\", \"\")\n answer = val.get(\"answer\", \"\")\n category = val.get(\"category\", -1)\n evidence = val.get(\"evidence\", [])\n adversarial_answer = val.get(\"adversarial_answer\", \"\")\n \n (\n response,\n speaker_1_memories,\n speaker_2_memories,\n speaker_1_memory_time,\n speaker_2_memory_time,\n speaker_1_graph_memories,\n speaker_2_graph_memories,\n response_time,\n ) = self.answer_question(\n speaker_a_user_id, speaker_b_user_id, question, answer, category\n )\n \n result = {\n \"question\": question,\n \"answer\": answer,\n \"category\": category,\n \"evidence\": evidence,\n \"response\": response,\n \"adversarial_answer\": adversarial_answer,\n \"speaker_1_memories\": speaker_1_memories,\n \"speaker_2_memories\": speaker_2_memories,\n \"num_speaker_1_memories\": len(speaker_1_memories),\n \"num_speaker_2_memories\": len(speaker_2_memories),\n \"speaker_1_memory_time\": speaker_1_memory_time,\n \"speaker_2_memory_time\": speaker_2_memory_time,\n \"speaker_1_graph_memories\": speaker_1_graph_memories,\n \"speaker_2_graph_memories\": speaker_2_graph_memories,\n \"response_time\": response_time,\n }\n \n # Save results after each question is processed\n with open(self.output_path, \"w\") as f:\n json.dump(self.results, f, indent=4)\n \n return result\n \n def process_data_file(self, file_path):\n \"\"\"Process the entire data file\"\"\"\n with open(file_path, \"r\") as f:\n data = json.load(f)\n \n for idx, item in tqdm(\n enumerate(data), total=len(data), desc=\"Processing conversations\"\n ):\n qa = item[\"qa\"]\n conversation = item[\"conversation\"]\n speaker_a = conversation[\"speaker_a\"]\n speaker_b = conversation[\"speaker_b\"]\n \n speaker_a_user_id = f\"{speaker_a}_{idx}\"\n speaker_b_user_id = f\"{speaker_b}_{idx}\"\n \n for question_item in tqdm(\n qa,\n total=len(qa),\n desc=f\"Processing questions for conversation {idx}\",\n leave=False,\n ):\n result = self.process_question(\n question_item, speaker_a_user_id, speaker_b_user_id\n )\n self.results[idx].append(result)\n \n # Save results after each question is processed\n with open(self.output_path, \"w\") as f:\n json.dump(self.results, f, indent=4)\n \n # Final save at the end\n with open(self.output_path, \"w\") as f:\n json.dump(self.results, f, indent=4)\n" }, "complexity_metrics": { - "cyclomatic_complexity": 9.0, - "lines_of_code": 350, - "number_of_classes": 2, - "number_of_functions": 9 + "cyclomatic_complexity": 37.0, + "lines_of_code": 406, + "number_of_classes": 1, + "number_of_functions": 7 }, "dependencies": [ { - "dependency_type": "error_handling", - "is_external": true, - "line_number": 1, - "name": "anyhow", + "dependency_type": "standard-library", + "is_external": false, + "line_number": null, + "name": "json", "path": null, "version": null }, { - "dependency_type": "configuration", - "is_external": true, - "line_number": 3, - "name": "config", + "dependency_type": "standard-library", + "is_external": false, + "line_number": null, + "name": "os", "path": null, "version": null }, { - "dependency_type": "logging", + "dependency_type": "standard-library", + "is_external": false, + "line_number": null, + "name": "subprocess", + "path": null, + "version": null + }, + { + "dependency_type": "standard-library", + "is_external": false, + "line_number": null, + "name": "time", + "path": null, + "version": null + }, + { + "dependency_type": "standard-library", + "is_external": false, + "line_number": null, + "name": "collections", + "path": null, + "version": null + }, + { + "dependency_type": "standard-library", + "is_external": false, + "line_number": null, + "name": "pathlib", + "path": null, + "version": null + }, + { + "dependency_type": "third-party", "is_external": true, - "line_number": 4, - "name": "tracing", + "line_number": null, + "name": "jinja2", "path": null, "version": null }, { - "dependency_type": "serialization", + "dependency_type": "third-party", "is_external": true, - "line_number": 8, - "name": "serde", + "line_number": null, + "name": "openai", "path": null, "version": null }, { - "dependency_type": "internal", - "is_external": false, - "line_number": 36, - "name": "cortex_mem_core", + "dependency_type": "third-party", + "is_external": true, + "line_number": null, + "name": "tqdm", "path": null, "version": null }, { - "dependency_type": "datetime", + "dependency_type": "third-party", "is_external": true, - "line_number": 36, - "name": "chrono", + "line_number": null, + "name": "httpx", "path": null, "version": null + }, + { + "dependency_type": "internal", + "is_external": false, + "line_number": null, + "name": ".config_utils", + "path": "examples/lomoco-evaluation/src/cortex_mem/config_utils.py", + "version": null } ], - "detailed_description": "该组件负责协调和执行完整的评估实验流程。它加载配置文件,创建输出目录,并根据配置运行召回率评估和/或有效性评估。组件通过DatasetLoader加载测试数据集,使用MemoryManager进行内存操作,并通过相应的评估器(RealRecallEvaluator和RealEffectivenessEvaluator)执行评估。评估结果以JSON格式保存,同时生成详细的Markdown报告。组件支持从环境变量加载配置,具有良好的错误处理和日志记录机制。", + "detailed_description": "The component is responsible for retrieving conversation memories via an external CLI tool (`cortex-mem-cli`), processing them, and using an LLM (via OpenAI API) to answer questions based on the retrieved memories. It supports two speakers, performs sequential memory searches, applies templated prompts to guide LLM responses, and saves results incrementally. The answer generation enforces concise, timestamp-aware responses by converting relative time references to absolute values based on memory timestamps.", "interfaces": [ { - "description": "实验运行器主结构体,负责协调和运行评估实验", - "interface_type": "struct", - "name": "ExperimentRunner", - "parameters": [], + "description": "Initializes the search system with config, LLM client, and prompt template", + "interface_type": "constructor", + "name": "CortexMemSearch.__init__", + "parameters": [ + { + "description": "Path to save results JSON file", + "is_optional": true, + "name": "output_path", + "param_type": "str" + }, + { + "description": "Number of top memories to retrieve", + "is_optional": true, + "name": "top_k", + "param_type": "int" + }, + { + "description": "Path to config.toml; auto-discovered if not provided", + "is_optional": true, + "name": "config_path", + "param_type": "str" + } + ], "return_type": null, "visibility": "public" }, { - "description": "实验配置结构体,包含评估模式、召回率和有效性评估配置等", - "interface_type": "struct", - "name": "ExperimentConfig", + "description": "Attempts to locate config.toml in standard project locations", + "interface_type": "method", + "name": "CortexMemSearch._find_config_file", "parameters": [], - "return_type": null, - "visibility": "public" + "return_type": "str", + "visibility": "private" }, { - "description": "创建新的实验运行器实例", - "interface_type": "function", - "name": "ExperimentRunner::new", + "description": "Executes cortex-mem-cli subprocess with retry-safe configuration", + "interface_type": "method", + "name": "CortexMemSearch._run_cortex_mem_cli", "parameters": [ { - "description": "配置文件路径", + "description": "Command-line arguments for cortex-mem-cli", "is_optional": false, - "name": "config_path", - "param_type": "PathBuf" + "name": "args", + "param_type": "list" + } + ], + "return_type": "tuple", + "visibility": "private" + }, + { + "description": "Performs memory search via CLI, with retry logic and JSON/text output parsing", + "interface_type": "method", + "name": "CortexMemSearch.search_memory", + "parameters": [ + { + "description": null, + "is_optional": false, + "name": "user_id", + "param_type": "str" }, { - "description": "输出目录路径", + "description": null, "is_optional": false, - "name": "output_dir", - "param_type": "PathBuf" + "name": "query", + "param_type": "str" + }, + { + "description": null, + "is_optional": true, + "name": "max_retries", + "param_type": "int" + }, + { + "description": null, + "is_optional": true, + "name": "retry_delay", + "param_type": "int" } ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "运行完整的评估流程", - "interface_type": "function", - "name": "ExperimentRunner::run_full_evaluation", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" - }, - { - "description": "运行召回率评估", - "interface_type": "function", - "name": "ExperimentRunner::run_recall_evaluation", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" - }, - { - "description": "运行有效性评估", - "interface_type": "function", - "name": "ExperimentRunner::run_effectiveness_evaluation", - "parameters": [], - "return_type": "Result<()>", + "return_type": "tuple", "visibility": "public" }, { - "description": "运行真实的评估流程,包括数据集检查、MemoryManager创建和评估执行", - "interface_type": "function", - "name": "ExperimentRunner::run_real_evaluation", - "parameters": [], - "return_type": "Result<()>", - "visibility": "private" - }, - { - "description": "运行真实的召回率评估", - "interface_type": "function", - "name": "ExperimentRunner::run_real_recall_evaluation", + "description": "Retrieves memories for two speakers and generates an answer using LLM", + "interface_type": "method", + "name": "CortexMemSearch.answer_question", "parameters": [ { - "description": "MemoryManager实例", + "description": null, "is_optional": false, - "name": "memory_manager", - "param_type": "&std::sync::Arc" + "name": "speaker_1_user_id", + "param_type": "str" + }, + { + "description": null, + "is_optional": false, + "name": "speaker_2_user_id", + "param_type": "str" + }, + { + "description": null, + "is_optional": false, + "name": "question", + "param_type": "str" + }, + { + "description": null, + "is_optional": false, + "name": "answer", + "param_type": "str" }, { - "description": "数据集路径", + "description": null, "is_optional": false, - "name": "dataset_path", - "param_type": "&PathBuf" + "name": "category", + "param_type": "any" } ], - "return_type": "Result<()>", - "visibility": "private" + "return_type": "tuple", + "visibility": "public" }, { - "description": "运行真实的有效性评估", - "interface_type": "function", - "name": "ExperimentRunner::run_real_effectiveness_evaluation", + "description": "Processes a single QA item, updates results, and saves to disk", + "interface_type": "method", + "name": "CortexMemSearch.process_question", "parameters": [ { - "description": "MemoryManager实例", + "description": null, "is_optional": false, - "name": "memory_manager", - "param_type": "&std::sync::Arc" + "name": "val", + "param_type": "dict" + }, + { + "description": null, + "is_optional": false, + "name": "speaker_a_user_id", + "param_type": "str" }, { - "description": "数据集路径", + "description": null, "is_optional": false, - "name": "dataset_path", - "param_type": "&PathBuf" + "name": "speaker_b_user_id", + "param_type": "str" } ], - "return_type": "Result<()>", - "visibility": "private" + "return_type": "dict", + "visibility": "public" }, { - "description": "生成真实的评估报告", - "interface_type": "function", - "name": "ExperimentRunner::generate_real_evaluation_report", - "parameters": [], - "return_type": "Result", - "visibility": "private" + "description": "Processes a full JSON dataset of conversations and questions", + "interface_type": "method", + "name": "CortexMemSearch.process_data_file", + "parameters": [ + { + "description": null, + "is_optional": false, + "name": "file_path", + "param_type": "str" + } + ], + "return_type": null, + "visibility": "public" } ], "responsibilities": [ - "加载实验配置并初始化实验环境", - "协调运行召回率和有效性评估流程", - "管理评估数据集的加载和验证", - "创建和管理MemoryManager实例用于评估", - "生成评估报告并保存结果到指定输出目录" + "Orchestrate memory retrieval using an external CLI tool (cortex-mem-cli) via subprocess calls", + "Interface with OpenAI-compatible LLMs to generate concise answers from retrieved memories using a structured prompt template", + "Manage configuration loading and validation for API access and system paths", + "Process batches of questions from JSON files, maintaining per-conversation state and saving incremental results", + "Handle error resilience with retry logic and structured output parsing from CLI tools" ] }, { "code_dossier": { "code_purpose": "specificfeature", - "description": "基准测试运行器,负责运行性能基准测试", - "file_path": "examples/cortex-mem-evaluation/src/runner/benchmark_runner.rs", + "description": null, + "file_path": "examples/lomoco-evaluation/src/langmem_eval/add.py", "functions": [ - "new", - "run_benchmark_suite", - "benchmark_add_memory", - "benchmark_search_memory", - "benchmark_update_memory", - "benchmark_mixed_operations" + "LangMemAdd.__init__", + "LangMemAdd._find_config_file", + "LangMemAdd._initialize_langmem", + "LangMemAdd.load_data", + "LangMemAdd.add_memory", + "LangMemAdd.add_memories_for_speaker", + "LangMemAdd.process_conversation", + "LangMemAdd.process_all_conversations", + "LangMemAdd._save_store_to_file", + "LangMemAdd.print_summary" ], "importance_score": 0.8, - "interfaces": [ - "BenchmarkRunner", - "run_benchmark_suite", - "benchmark_add_memory", - "benchmark_search_memory", - "benchmark_update_memory", - "benchmark_mixed_operations" - ], - "name": "benchmark_runner.rs", - "source_summary": "//! 基准测试运行器\n//! \n//! 运行性能基准测试\n\nuse anyhow::Result;\nuse tracing::info;\n\n/// 基准测试运行器\npub struct BenchmarkRunner;\n\nimpl BenchmarkRunner {\n /// 创建新的基准测试运行器\n pub fn new() -> Self {\n Self\n }\n \n /// 运行基准测试套件\n pub async fn run_benchmark_suite(&self, memory_manager: Option<&cortex_mem_core::MemoryManager>) -> Result<()> {\n info!(\"基准测试运行器就绪\");\n \n if memory_manager.is_none() {\n anyhow::bail!(\"基准测试需要 MemoryManager 实例,请提供有效的 MemoryManager\");\n }\n \n let memory_manager = memory_manager.unwrap();\n info!(\"使用提供的 MemoryManager 实例运行实际基准测试\");\n \n // 实际基准测试逻辑需要实现\n info!(\"基准测试框架就绪,需要实现具体测试逻辑\");\n \n Ok(())\n }\n \n /// 运行添加记忆基准测试\n pub async fn benchmark_add_memory(&self) -> Result<()> {\n info!(\"添加记忆基准测试框架就绪\");\n Ok(())\n }\n \n /// 运行搜索记忆基准测试\n pub async fn benchmark_search_memory(&self) -> Result<()> {\n info!(\"搜索记忆基准测试框架就绪\");\n Ok(())\n }\n \n /// 运行更新记忆基准测试\n pub async fn benchmark_update_memory(&self) -> Result<()> {\n info!(\"更新记忆基准测试框架就绪\");\n Ok(())\n }\n \n /// 运行混合操作基准测试\n pub async fn benchmark_mixed_operations(&self) -> Result<()> {\n info!(\"混合操作基准测试框架就绪\");\n Ok(())\n }\n}" + "interfaces": [], + "name": "add.py", + "source_summary": "import json\nimport os\nimport time\nimport logging\nfrom pathlib import Path\nfrom typing import List, Dict, Any\nfrom tqdm import tqdm\n\ntry:\n from langgraph.store.memory import InMemoryStore\nexcept ImportError:\n raise ImportError(\n \"langgraph is not installed. Please install it using: pip install langgraph\"\n )\n\nfrom .config_utils import check_openai_config, get_config_value, validate_config\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n\nclass LangMemAdd:\n \"\"\"Class to add memories to LangMem for evaluation\"\"\"\n \n def __init__(self, data_path=None, batch_size=2, config_path=None):\n self.batch_size = batch_size\n self.data_path = data_path\n self.data = None\n self.config_path = config_path or self._find_config_file()\n\n # Track statistics\n self.stats = {\n \"total_conversations\": 0,\n \"successful_conversations\": 0,\n \"failed_conversations\": 0,\n \"total_memories\": 0,\n \"successful_memories\": 0,\n \"failed_memories\": 0\n }\n\n # Validate config file\n if not validate_config(self.config_path):\n raise ValueError(f\"Invalid config file: {self.config_path}\")\n\n # Check OpenAI configuration\n if not check_openai_config(self.config_path):\n raise ValueError(\n f\"OpenAI configuration not properly set in {self.config_path}\"\n )\n\n # Initialize LangMem components\n self._initialize_langmem()\n\n if data_path:\n self.load_data()\n\n def _find_config_file(self):\n \"\"\"Find config.toml file in standard locations\"\"\"\n # Check current directory\n if os.path.exists(\"config.toml\"):\n return \"config.toml\"\n\n # Check parent directories\n current_dir = Path.cwd()\n for parent in current_dir.parents:\n config_file = parent / \"config.toml\"\n if config_file.exists():\n return str(config_file)\n\n # Check examples directory\n examples_config = (\n Path(__file__).parent.parent.parent.parent / \"examples\" / \"config.toml\"\n )\n if examples_config.exists():\n return str(examples_config)\n\n # Check project root\n project_root = Path(__file__).parent.parent.parent.parent\n config_file = project_root / \"config.toml\"\n if config_file.exists():\n return str(config_file)\n\n raise FileNotFoundError(\"Could not find config.toml file\")\n\n def _initialize_langmem(self):\n \"\"\"Initialize LangMem memory store\"\"\"\n try:\n # Get LLM configuration\n api_key = get_config_value(self.config_path, \"llm\", \"api_key\")\n api_base_url = get_config_value(self.config_path, \"llm\", \"api_base_url\")\n model_name = get_config_value(self.config_path, \"llm\", \"model_efficient\", \"gpt-3.5-turbo\")\n \n # Create OpenAI client for answer generation\n import httpx\n from openai import OpenAI\n \n self.openai_client = OpenAI(\n api_key=api_key,\n base_url=api_base_url,\n http_client=httpx.Client(verify=False)\n )\n \n # Create memory store\n self.store = InMemoryStore()\n \n logger.info(\"✅ LangMem initialized successfully\")\n \n except Exception as e:\n logger.error(f\"❌ Failed to initialize LangMem: {e}\")\n raise\n\n def load_data(self):\n if not self.data_path:\n raise ValueError(\"data_path not set\")\n with open(self.data_path, \"r\") as f:\n self.data = json.load(f)\n return self.data\n\n def add_memory(self, user_id: str, content: str, timestamp: str = \"\") -> bool:\n \"\"\"Add a memory using LangMem store\"\"\"\n try:\n # Create namespace for this user\n namespace = (\"memories\", user_id)\n \n # Generate a unique key for this memory\n import uuid\n memory_key = str(uuid.uuid4())\n \n # Store the memory directly\n memory_value = {\n \"content\": content,\n \"timestamp\": timestamp,\n \"created_at\": time.time()\n }\n \n self.store.put(namespace, memory_key, memory_value)\n \n self.stats[\"successful_memories\"] += 1\n logger.debug(f\"✅ Successfully added memory for user {user_id}\")\n return True\n \n except Exception as e:\n logger.error(f\"❌ Failed to add memory for user {user_id}: {e}\")\n self.stats[\"failed_memories\"] += 1\n return False\n\n def add_memories_for_speaker(self, speaker: str, messages: List[Dict], timestamp: str, desc: str):\n \"\"\"Add memories for a speaker with error tracking\"\"\"\n total_batches = (len(messages) + self.batch_size - 1) // self.batch_size\n failed_batches = 0\n \n for i in tqdm(range(0, len(messages), self.batch_size), desc=desc):\n batch_messages = messages[i : i + self.batch_size]\n\n # Combine batch messages into single content\n content = \"\\n\".join([msg.get(\"content\", \"\") for msg in batch_messages])\n\n # Add timestamp as metadata\n metadata = f\"Timestamp: {timestamp}\"\n content_with_metadata = f\"{metadata}\\n{content}\"\n\n # Add memory with error tracking\n success = self.add_memory(\n speaker,\n content_with_metadata,\n timestamp,\n )\n\n self.stats[\"total_memories\"] += 1\n \n # Small delay between batches to avoid rate limiting\n time.sleep(0.3)\n \n if failed_batches > 0:\n logger.warning(f\"{failed_batches}/{total_batches} batches failed for {speaker}\")\n\n def process_conversation(self, item: Dict[str, Any], idx: int):\n \"\"\"Process a single conversation with error handling\"\"\"\n try:\n conversation = item.get(\"conversation\", {})\n speaker_a = conversation.get(\"speaker_a\", \"SpeakerA\")\n speaker_b = conversation.get(\"speaker_b\", \"SpeakerB\")\n\n speaker_a_user_id = f\"{speaker_a}_{idx}\"\n speaker_b_user_id = f\"{speaker_b}_{idx}\"\n\n for key in conversation.keys():\n if key in [\"speaker_a\", \"speaker_b\"] or \"date\" in key or \"timestamp\" in key:\n continue\n\n date_time_key = key + \"_date_time\"\n timestamp = conversation.get(date_time_key, \"2024-01-01 00:00:00\")\n chats = conversation[key]\n\n messages = []\n messages_reverse = []\n for chat in chats:\n speaker = chat.get(\"speaker\", \"\")\n text = chat.get(\"text\", \"\")\n \n if speaker == speaker_a:\n messages.append(\n {\"role\": \"user\", \"content\": f\"{speaker_a}: {text}\"}\n )\n messages_reverse.append(\n {\"role\": \"assistant\", \"content\": f\"{speaker_a}: {text}\"}\n )\n elif speaker == speaker_b:\n messages.append(\n {\"role\": \"assistant\", \"content\": f\"{speaker_b}: {text}\"}\n )\n messages_reverse.append(\n {\"role\": \"user\", \"content\": f\"{speaker_b}: {text}\"}\n )\n else:\n logger.warning(f\"Unknown speaker: {speaker}\")\n\n # Add memories for both speakers\n self.add_memories_for_speaker(\n speaker_a_user_id,\n messages,\n timestamp,\n f\"Adding Memories for {speaker_a}\",\n )\n \n time.sleep(0.3) # Small delay between speakers\n \n self.add_memories_for_speaker(\n speaker_b_user_id,\n messages_reverse,\n timestamp,\n f\"Adding Memories for {speaker_b}\",\n )\n\n self.stats[\"successful_conversations\"] += 1\n logger.info(f\"✅ Successfully processed conversation {idx}\")\n\n except Exception as e:\n self.stats[\"failed_conversations\"] += 1\n logger.error(f\"❌ Failed to process conversation {idx}: {e}\")\n # Continue processing other conversations\n\n self.stats[\"total_conversations\"] += 1\n\n def process_all_conversations(self, max_workers=1):\n \"\"\"Process all conversations sequentially for stability\"\"\"\n if not self.data:\n raise ValueError(\n \"No data loaded. Please set data_path and call load_data() first.\"\n )\n\n logger.info(f\"Starting to process {len(self.data)} conversations...\")\n \n # Process conversations sequentially for stability\n for idx, item in enumerate(self.data):\n self.process_conversation(item, idx)\n \n # Small delay between conversations to avoid overwhelming the system\n time.sleep(0.5)\n \n # Print summary\n self.print_summary()\n \n # Save the store to a file for later use\n self._save_store_to_file()\n \n def _save_store_to_file(self):\n \"\"\"Save the memory store to a file using JSON\"\"\"\n import json\n memory_file = \"results/langmem_store.json\"\n try:\n os.makedirs(\"results\", exist_ok=True)\n \n # Convert store to a serializable format\n memories_dict = {}\n for namespace_tuple in self.store._data.keys():\n namespace_str = \"/\".join(namespace_tuple)\n memories_dict[namespace_str] = {}\n for key, value in self.store._data[namespace_tuple].items():\n # Convert Item to dict\n memories_dict[namespace_str][key] = {\n \"namespace\": namespace_tuple,\n \"key\": key,\n \"value\": value.value if hasattr(value, 'value') else value\n }\n \n # Save as JSON\n with open(memory_file, 'w') as f:\n json.dump(memories_dict, f, indent=2)\n \n print(f\"✅ Saved memory store to {memory_file}\")\n except Exception as e:\n print(f\"⚠️ Could not save memory store to file: {e}\")\n import traceback\n traceback.print_exc()\n \n def print_summary(self):\n \"\"\"Print processing summary\"\"\"\n print(\"\\n\" + \"=\" * 60)\n print(\"📊 PROCESSING SUMMARY\")\n print(\"=\" * 60)\n print(f\"Total Conversations: {self.stats['total_conversations']}\")\n print(f\"Successful: {self.stats['successful_conversations']}\")\n print(f\"Failed: {self.stats['failed_conversations']}\")\n if self.stats['total_conversations'] > 0:\n print(f\"Success Rate: {self.stats['successful_conversations']/self.stats['total_conversations']*100:.1f}%\")\n print(f\"\\nTotal Memories: {self.stats['total_memories']}\")\n print(f\"Successful: {self.stats['successful_memories']}\")\n print(f\"Failed: {self.stats['failed_memories']}\")\n if self.stats['total_memories'] > 0:\n print(f\"Success Rate: {self.stats['successful_memories']/self.stats['total_memories']*100:.1f}%\")\n print(\"=\" * 60 + \"\\n\")" }, "complexity_metrics": { - "cyclomatic_complexity": 2.0, - "lines_of_code": 57, + "cyclomatic_complexity": 39.0, + "lines_of_code": 312, "number_of_classes": 1, - "number_of_functions": 6 + "number_of_functions": 10 }, "dependencies": [ { - "dependency_type": "library", + "dependency_type": "import", "is_external": true, - "line_number": 1, - "name": "anyhow", + "line_number": null, + "name": "langgraph.store.memory.InMemoryStore", "path": null, "version": null }, { - "dependency_type": "library", + "dependency_type": "import", "is_external": true, - "line_number": 2, - "name": "tracing", + "line_number": null, + "name": "tqdm", "path": null, "version": null }, { - "dependency_type": "struct", - "is_external": true, - "line_number": 14, - "name": "cortex_mem_core::MemoryManager", + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "json", "path": null, "version": null - } - ], - "detailed_description": "该组件是基准测试运行器,提供运行性能基准测试的功能。包含运行基准测试套件和各种具体操作(添加、搜索、更新记忆及混合操作)的基准测试方法。目前主要是框架就绪状态,具体测试逻辑需要实现。", - "interfaces": [ + }, { - "description": "基准测试运行器结构体", - "interface_type": "struct", - "name": "BenchmarkRunner", - "parameters": [], - "return_type": null, - "visibility": "pub" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "os", + "path": null, + "version": null }, { - "description": "创建新的基准测试运行器", - "interface_type": "function", - "name": "new", - "parameters": [], - "return_type": "Self", - "visibility": "pub" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "time", + "path": null, + "version": null }, { - "description": "运行基准测试套件", - "interface_type": "function", - "name": "run_benchmark_suite", - "parameters": [ - { - "description": "内存管理器实例", - "is_optional": true, - "name": "memory_manager", - "param_type": "Option<&cortex_mem_core::MemoryManager>" - } - ], - "return_type": "Result<()>", - "visibility": "pub" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "logging", + "path": null, + "version": null }, { - "description": "运行添加记忆基准测试", - "interface_type": "function", - "name": "benchmark_add_memory", - "parameters": [], - "return_type": "Result<()>", - "visibility": "pub" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "pathlib.Path", + "path": null, + "version": null }, { - "description": "运行搜索记忆基准测试", - "interface_type": "function", - "name": "benchmark_search_memory", - "parameters": [], - "return_type": "Result<()>", - "visibility": "pub" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "typing.List", + "path": null, + "version": null }, { - "description": "运行更新记忆基准测试", - "interface_type": "function", - "name": "benchmark_update_memory", - "parameters": [], - "return_type": "Result<()>", - "visibility": "pub" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "typing.Dict", + "path": null, + "version": null }, { - "description": "运行混合操作基准测试", - "interface_type": "function", - "name": "benchmark_mixed_operations", - "parameters": [], - "return_type": "Result<()>", - "visibility": "pub" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "typing.Any", + "path": null, + "version": null + }, + { + "dependency_type": "import", + "is_external": true, + "line_number": null, + "name": "httpx", + "path": null, + "version": null + }, + { + "dependency_type": "import", + "is_external": true, + "line_number": null, + "name": "openai.OpenAI", + "path": null, + "version": null + }, + { + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": "uuid", + "path": null, + "version": null + }, + { + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": ".config_utils", + "path": null, + "version": null } ], + "detailed_description": "The LangMemAdd class is designed to load conversation data from a JSON file and add structured memories to a LangMem in-memory store for evaluation purposes. It processes conversations by extracting speaker interactions, formatting them as memory entries with timestamps, and storing them under unique user namespaces. The component handles batch processing, error tracking, rate limiting via delays, and persistence of the memory store to a JSON file. It relies on external configuration (config.toml) for OpenAI API credentials and integrates with LangGraph's InMemoryStore for memory management. The component is used in evaluation pipelines to populate memory systems with conversational data for testing agent recall and context retention.", + "interfaces": [], "responsibilities": [ - "提供基准测试运行器的核心功能", - "管理基准测试套件的执行流程", - "实现不同类型记忆操作的性能基准测试", - "验证MemoryManager组件的性能表现" + "Load and parse conversation data from JSON files", + "Initialize and configure LangMem memory store using external configuration", + "Process conversations by extracting speaker interactions and formatting as memory entries", + "Manage batched memory insertion with error tracking and rate limiting", + "Persist the complete memory store to disk for downstream evaluation" ] }, { "code_dossier": { "code_purpose": "tool", - "description": "可视化工具,用于生成评估结果的图表,支持召回率、有效性、性能及综合报告的可视化。", - "file_path": "examples/cortex-mem-evaluation/src/report/visualizer.rs", + "description": null, + "file_path": "examples/lomoco-evaluation/src/langmem_eval/search.py", "functions": [ - "new", - "generate_recall_charts", - "generate_effectiveness_charts", - "generate_performance_charts", - "generate_comprehensive_charts", - "generate_simulation_charts" + "LangMemSearch.__init__", + "LangMemSearch._load_memories_from_file", + "LangMemSearch._find_config_file", + "LangMemSearch.search_memory", + "LangMemSearch.answer_question", + "LangMemSearch.process_question", + "LangMemSearch.process_data_file" ], "importance_score": 0.8, "interfaces": [ - "Visualizer::new", - "Visualizer::generate_recall_charts", - "Visualizer::generate_effectiveness_charts", - "Visualizer::generate_performance_charts", - "Visualizer::generate_comprehensive_charts", - "Visualizer::generate_simulation_charts" + "LangMemSearch" ], - "name": "visualizer.rs", - "source_summary": "//! 可视化工具\n//! \n//! 生成评估结果的可视化图表\n\nuse anyhow::Result;\nuse std::path::PathBuf;\nuse tracing::info;\n\n/// 可视化工具\npub struct Visualizer {\n /// 输出目录\n output_dir: PathBuf,\n}\n\nimpl Visualizer {\n /// 创建新的可视化工具\n pub fn new(output_dir: PathBuf) -> Self {\n Self { output_dir }\n }\n \n /// 生成召回率评估图表\n pub fn generate_recall_charts(&self) -> Result<()> {\n info!(\"召回率评估图表生成器就绪\");\n info!(\"需要实际评估数据以生成图表\");\n Ok(())\n }\n \n /// 生成有效性评估图表\n pub fn generate_effectiveness_charts(&self) -> Result<()> {\n info!(\"有效性评估图表生成器就绪\");\n info!(\"需要实际评估数据以生成图表\");\n Ok(())\n }\n \n /// 生成性能评估图表\n pub fn generate_performance_charts(&self) -> Result<()> {\n info!(\"性能评估图表生成器就绪\");\n info!(\"需要实际评估数据以生成图表\");\n Ok(())\n }\n \n /// 生成综合报告图表\n pub fn generate_comprehensive_charts(&self) -> Result<()> {\n info!(\"综合报告图表生成器就绪\");\n \n // 创建图表目录\n let charts_dir = self.output_dir.join(\"visualizations\");\n std::fs::create_dir_all(&charts_dir)?;\n \n // 生成示例图表说明\n let chart_info = r#\"# 可视化图表说明\n\n本目录用于存放评估结果的可视化图表。\n\n## 支持的图表类型\n\n### 1. 召回率评估图表\n- Precision-Recall 曲线\n- Precision@K 折线图\n- Recall@K 折线图\n- 相似度阈值影响图\n\n### 2. 有效性评估图表\n- 事实提取准确性雷达图\n- 记忆分类混淆矩阵热图\n- 重要性评分分布直方图\n- 去重效果对比图\n\n### 3. 性能评估图表\n- 延迟分布箱线图\n- 吞吐量趋势图\n- 资源使用监控图\n- 可扩展性曲线图\n\n## 生成图表\n运行实际评估后,图表将自动生成在此目录中。\n\n## 技术要求\n- 需要安装 plotters 库\n- 支持 PNG、SVG、PDF 格式\n- 可自定义图表样式和颜色\n\n---\n*图表框架就绪,等待评估数据*\"#;\n \n std::fs::write(charts_dir.join(\"README.md\"), chart_info)?;\n \n Ok(())\n }\n \n /// 生成模拟图表\n pub fn generate_simulation_charts(&self) -> Result<()> {\n info!(\"生成模拟图表...\");\n \n let charts_dir = self.output_dir.join(\"visualizations\");\n std::fs::create_dir_all(&charts_dir)?;\n \n // 创建模拟图表数据\n let simulation_data = r#\"{\n \"recall_metrics\": {\n \"precision_at_k\": {\"1\": 0.85, \"3\": 0.78, \"5\": 0.72, \"10\": 0.65},\n \"recall_at_k\": {\"1\": 0.45, \"3\": 0.68, \"5\": 0.82, \"10\": 0.95}\n },\n \"effectiveness_metrics\": {\n \"fact_extraction\": {\"precision\": 0.88, \"recall\": 0.82, \"f1\": 0.85},\n \"classification\": {\"accuracy\": 0.92}\n },\n \"note\": \"这是模拟数据,实际图表需要运行评估获取真实数据\"\n}\"#;\n \n std::fs::write(charts_dir.join(\"simulation_data.json\"), simulation_data)?;\n \n info!(\"模拟图表数据已生成\");\n Ok(())\n }\n}" + "name": "search.py", + "source_summary": "import json\nimport os\nimport time\nimport logging\nfrom collections import defaultdict\nfrom pathlib import Path\nfrom typing import List, Dict, Tuple, Any\n\nfrom jinja2 import Template\nfrom openai import OpenAI\nfrom tqdm import tqdm\n\ntry:\n from langgraph.store.memory import InMemoryStore\nexcept ImportError:\n raise ImportError(\n \"langgraph is not installed. Please install it using: pip install langgraph\"\n )\n\nfrom .config_utils import check_openai_config, get_config_value, validate_config\n\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n\nclass LangMemSearch:\n \"\"\"Class to search memories in LangMem for evaluation\"\"\"\n \n def __init__(self, output_path=\"results.json\", top_k=10, config_path=None):\n self.top_k = top_k\n self.results = defaultdict(list)\n self.output_path = output_path\n self.config_path = config_path or self._find_config_file()\n \n # Answer generation prompt (same as Cortex Mem)\n self.ANSWER_PROMPT = \"\"\"\nYou are an intelligent memory assistant tasked with retrieving accurate information from conversation memories.\n\n# CONTEXT:\nYou have access to memories from two speakers in a conversation. These memories contain \ntimestamped information that may be relevant to answering the question.\n\n# INSTRUCTIONS:\n1. Carefully analyze all provided memories from both speakers\n2. Pay special attention to the timestamps to determine the answer\n3. If the question asks about a specific event or fact, look for direct evidence in the \n memories\n4. If the memories contain contradictory information, prioritize the most recent memory\n5. If there is a question about time references (like \"last year\", \"two months ago\", \n etc.), calculate the actual date based on the memory timestamp. For example, if a \n memory from 4 May 2022 mentions \"went to India last year,\" then the trip occurred \n in 2021.\n6. Always convert relative time references to specific dates, months, or years. For \n example, convert \"last year\" to \"2022\" or \"two months ago\" to \"March 2023\" based \n on the memory timestamp. Ignore the reference while answering the question.\n7. Focus only on the content of the memories from both speakers. Do not confuse \n character names mentioned in memories with the actual users who created those \n memories.\n8. The answer should be less than 5-6 words.\n\n# APPROACH (Think step by step):\n1. First, examine all memories that contain information related to the question\n2. Examine the timestamps and content of these memories carefully\n3. Look for explicit mentions of dates, times, locations, or events that answer the \n question\n4. If the answer requires calculation (e.g., converting relative time references), \n show your work\n5. Formulate a precise, concise answer based solely on the evidence in the memories\n6. Double-check that your answer directly addresses the question asked\n7. Ensure your final answer is specific and avoids vague time references\n\nMemories for user {{speaker_1_user_id}}:\n\n{{speaker_1_memories}}\n\nMemories for user {{speaker_2_user_id}}:\n\n{{speaker_2_memories}}\n\nQuestion: {{question}}\n\nAnswer:\n\"\"\"\n \n # Validate config file\n if not validate_config(self.config_path):\n raise ValueError(f\"Invalid config file: {self.config_path}\")\n \n # Check OpenAI configuration\n if not check_openai_config(self.config_path):\n raise ValueError(\n f\"OpenAI configuration not properly set in {self.config_path}\"\n )\n \n # Initialize OpenAI client from config.toml\n api_key = get_config_value(self.config_path, \"llm\", \"api_key\")\n api_base = get_config_value(self.config_path, \"llm\", \"api_base_url\")\n self.llm_model = get_config_value(self.config_path, \"llm\", \"model_efficient\", \"gpt-3.5-turbo\")\n \n # Create HTTP client with SSL verification disabled for internal APIs\n import httpx\n http_client = httpx.Client(verify=False)\n \n self.openai_client = OpenAI(\n api_key=api_key,\n base_url=api_base,\n http_client=http_client\n )\n \n # Initialize LangMem store\n # Note: This will be a new store. For persistence, we need to use the same store instance\n # or use a persistent store. For now, we'll assume memories are added in the same session.\n self.store = InMemoryStore()\n \n # Try to load previously stored memories from a file if exists\n self._load_memories_from_file()\n \n def _load_memories_from_file(self):\n \"\"\"Load memories from a JSON file if it exists\"\"\"\n import json\n memory_file = \"results/langmem_store.json\"\n try:\n if os.path.exists(memory_file):\n print(f\"📂 Found memory file: {memory_file}\")\n with open(memory_file, 'r') as f:\n memories_dict = json.load(f)\n \n print(f\"✅ Loaded JSON with {len(memories_dict)} namespaces\")\n \n # Restore memories to store\n total_items = 0\n for namespace_str, items in memories_dict.items():\n namespace_tuple = tuple(namespace_str.split('/'))\n for key, item_data in items.items():\n self.store.put(namespace_tuple, key, item_data[\"value\"])\n total_items += 1\n \n print(f\"✅ Successfully loaded {total_items} memories from {memory_file}\")\n except Exception as e:\n print(f\"⚠️ Could not load memories from file: {e}\")\n import traceback\n traceback.print_exc()\n \n def _find_config_file(self):\n \"\"\"Find config.toml file in standard locations\"\"\"\n # Check current directory\n if os.path.exists(\"config.toml\"):\n return \"config.toml\"\n \n # Check parent directories\n current_dir = Path.cwd()\n for parent in current_dir.parents:\n config_file = parent / \"config.toml\"\n if config_file.exists():\n return str(config_file)\n \n # Check examples directory\n examples_config = (\n Path(__file__).parent.parent.parent.parent / \"examples\" / \"config.toml\"\n )\n if examples_config.exists():\n return str(examples_config)\n \n # Check project root\n project_root = Path(__file__).parent.parent.parent.parent\n config_file = project_root / \"config.toml\"\n if config_file.exists():\n return str(config_file)\n \n raise FileNotFoundError(\"Could not find config.toml file\")\n \n def search_memory(self, user_id: str, query: str, max_retries: int = 3, retry_delay: float = 1) -> Tuple[List[Dict], float]:\n \"\"\"Search for memories using LangMem store\"\"\"\n start_time = time.time()\n retries = 0\n \n while retries < max_retries:\n try:\n # Create namespace for this user\n namespace = (\"memories\", user_id)\n \n # Search memories in the store\n # LangMem store supports semantic search through the search method\n memories = []\n \n # Get all memories for this user\n all_memories = list(self.store.search(namespace))\n \n # Debug: print what we found\n if len(all_memories) == 0:\n # Try to search with empty namespace to see all memories\n all_items = list(self.store.search(()))\n logger.debug(f\"Total items in store: {len(all_items)}\")\n if len(all_items) > 0:\n # Print first few items to see structure\n for i, item in enumerate(all_items[:3]):\n logger.debug(f\"Item {i}: namespace={item.namespace}, key={item.key}\")\n \n # Simple relevance scoring based on query matching\n # In a real implementation, you would use embedding-based similarity\n query_lower = query.lower()\n scored_memories = []\n \n for memory_item in all_memories:\n memory_value = memory_item.value\n \n # Convert memory to string if it's not\n if isinstance(memory_value, dict):\n memory_content = str(memory_value)\n else:\n memory_content = str(memory_value)\n \n # Simple keyword matching score\n score = 0.0\n query_words = query_lower.split()\n for word in query_words:\n if word in memory_content.lower():\n score += 1.0\n \n if score > 0:\n scored_memories.append({\n \"memory\": memory_content,\n \"timestamp\": \"\", # LangMem doesn't store timestamp by default\n \"score\": score,\n })\n \n # Sort by score and take top_k\n scored_memories.sort(key=lambda x: x[\"score\"], reverse=True)\n memories = scored_memories[:self.top_k]\n \n end_time = time.time()\n return memories, end_time - start_time\n \n except Exception as e:\n print(f\"Search error: {e}, retrying...\")\n retries += 1\n if retries >= max_retries:\n raise e\n time.sleep(retry_delay)\n \n end_time = time.time()\n return [], end_time - start_time\n \n def answer_question(\n self, speaker_1_user_id: str, speaker_2_user_id: str, \n question: str, answer: str, category: str\n ) -> Tuple[str, List[Dict], List[Dict], float, float, None, None, float]:\n \"\"\"Answer a question using retrieved memories\"\"\"\n # Sequential search to avoid rate limiting\n speaker_1_memories, speaker_1_memory_time = self.search_memory(\n speaker_1_user_id, question\n )\n # Add a small delay between searches to avoid rate limiting\n time.sleep(2)\n \n speaker_2_memories, speaker_2_memory_time = self.search_memory(\n speaker_2_user_id, question\n )\n # Add a small delay before LLM call\n time.sleep(2)\n \n search_1_memory = [\n f\"{item.get('timestamp', '')}: {item['memory']}\"\n for item in speaker_1_memories\n ]\n search_2_memory = [\n f\"{item.get('timestamp', '')}: {item['memory']}\"\n for item in speaker_2_memories\n ]\n \n template = Template(self.ANSWER_PROMPT)\n answer_prompt = template.render(\n speaker_1_user_id=speaker_1_user_id.split(\"_\")[0],\n speaker_2_user_id=speaker_2_user_id.split(\"_\")[0],\n speaker_1_memories=json.dumps(search_1_memory, indent=4),\n speaker_2_memories=json.dumps(search_2_memory, indent=4),\n question=question,\n )\n \n t1 = time.time()\n response = self.openai_client.chat.completions.create(\n model=self.llm_model,\n messages=[{\"role\": \"system\", \"content\": answer_prompt}],\n temperature=0.0,\n )\n t2 = time.time()\n response_time = t2 - t1\n \n return (\n response.choices[0].message.content,\n speaker_1_memories,\n speaker_2_memories,\n speaker_1_memory_time,\n speaker_2_memory_time,\n None, # graph_memories\n None,\n response_time,\n )\n \n def process_question(self, val: Dict[str, Any], speaker_a_user_id: str, speaker_b_user_id: str) -> Dict[str, Any]:\n \"\"\"Process a single question\"\"\"\n question = val.get(\"question\", \"\")\n answer = val.get(\"answer\", \"\")\n category = val.get(\"category\", -1)\n evidence = val.get(\"evidence\", [])\n adversarial_answer = val.get(\"adversarial_answer\", \"\")\n \n (\n response,\n speaker_1_memories,\n speaker_2_memories,\n speaker_1_memory_time,\n speaker_2_memory_time,\n speaker_1_graph_memories,\n speaker_2_graph_memories,\n response_time,\n ) = self.answer_question(\n speaker_a_user_id, speaker_b_user_id, question, answer, category\n )\n \n result = {\n \"question\": question,\n \"answer\": answer,\n \"category\": category,\n \"evidence\": evidence,\n \"response\": response,\n \"adversarial_answer\": adversarial_answer,\n \"speaker_1_memories\": speaker_1_memories,\n \"speaker_2_memories\": speaker_2_memories,\n \"num_speaker_1_memories\": len(speaker_1_memories),\n \"num_speaker_2_memories\": len(speaker_2_memories),\n \"speaker_1_memory_time\": speaker_1_memory_time,\n \"speaker_2_memory_time\": speaker_2_memory_time,\n \"speaker_1_graph_memories\": speaker_1_graph_memories,\n \"speaker_2_graph_memories\": speaker_2_graph_memories,\n \"response_time\": response_time,\n }\n \n # Save results after each question is processed\n with open(self.output_path, \"w\") as f:\n json.dump(self.results, f, indent=4)\n \n return result\n \n def process_data_file(self, file_path: str):\n \"\"\"Process the entire data file\"\"\"\n with open(file_path, \"r\") as f:\n data = json.load(f)\n \n for idx, item in tqdm(\n enumerate(data), total=len(data), desc=\"Processing conversations\"\n ):\n qa = item[\"qa\"]\n conversation = item[\"conversation\"]\n speaker_a = conversation[\"speaker_a\"]\n speaker_b = conversation[\"speaker_b\"]\n \n speaker_a_user_id = f\"{speaker_a}_{idx}\"\n speaker_b_user_id = f\"{speaker_b}_{idx}\"\n \n for question_item in tqdm(\n qa,\n total=len(qa),\n desc=f\"Processing questions for conversation {idx}\",\n leave=False,\n ):\n result = self.process_question(\n question_item, speaker_a_user_id, speaker_b_user_id\n )\n self.results[idx].append(result)\n \n # Save results after each question is processed\n with open(self.output_path, \"w\") as f:\n json.dump(self.results, f, indent=4)\n \n # Final save at the end\n with open(self.output_path, \"w\") as f:\n json.dump(self.results, f, indent=4)" }, "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 116, + "cyclomatic_complexity": 40.0, + "lines_of_code": 378, "number_of_classes": 1, - "number_of_functions": 6 + "number_of_functions": 7 }, "dependencies": [ { - "dependency_type": "error_handling", - "is_external": true, - "line_number": 1, - "name": "anyhow", + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "json", "path": null, "version": null }, { - "dependency_type": "standard_library", + "dependency_type": "built_in", "is_external": false, - "line_number": 2, - "name": "std::path::PathBuf", + "line_number": null, + "name": "os", "path": null, "version": null }, { - "dependency_type": "logging", - "is_external": true, - "line_number": 3, - "name": "tracing", + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "time", "path": null, "version": null - } - ], - "detailed_description": "该组件是一个专用的可视化工具,负责将内存评估系统的各项指标(召回率、有效性、性能等)转化为可视化图表。当前实现处于框架阶段,主要输出说明性文件和模拟数据,实际图表生成逻辑待集成。代码中通过 `tracing::info` 输出运行状态,并使用 `std::fs` 操作文件系统以创建目录和写入数据。`generate_comprehensive_charts` 方法生成详细的 README 说明文档,列出支持的图表类型和技术要求,为后续开发提供指导。`generate_simulation_charts` 提供模拟数据输出,便于前端或报告系统预览图表结构。", - "interfaces": [ + }, { - "description": "创建一个新的可视化工具实例", - "interface_type": "constructor", - "name": "Visualizer::new", - "parameters": [ - { - "description": "图表输出目录路径", - "is_optional": false, - "name": "output_dir", - "param_type": "PathBuf" - } - ], - "return_type": "Visualizer", - "visibility": "public" + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "logging", + "path": null, + "version": null }, { - "description": "生成召回率相关的评估图表", - "interface_type": "method", - "name": "Visualizer::generate_recall_charts", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "collections.defaultdict", + "path": null, + "version": null + }, + { + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "pathlib.Path", + "path": null, + "version": null + }, + { + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "typing.List", + "path": null, + "version": null + }, + { + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "typing.Dict", + "path": null, + "version": null + }, + { + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "typing.Tuple", + "path": null, + "version": null + }, + { + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "typing.Any", + "path": null, + "version": null }, { - "description": "生成有效性相关的评估图表", - "interface_type": "method", - "name": "Visualizer::generate_effectiveness_charts", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" + "dependency_type": "import", + "is_external": true, + "line_number": null, + "name": "jinja2.Template", + "path": null, + "version": null }, { - "description": "生成性能相关的评估图表", - "interface_type": "method", - "name": "Visualizer::generate_performance_charts", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" + "dependency_type": "import", + "is_external": true, + "line_number": null, + "name": "openai.OpenAI", + "path": null, + "version": null }, { - "description": "生成综合报告图表,包括创建目录和写入说明文档", - "interface_type": "method", - "name": "Visualizer::generate_comprehensive_charts", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" + "dependency_type": "import", + "is_external": true, + "line_number": null, + "name": "tqdm.tqdm", + "path": null, + "version": null }, { - "description": "生成模拟图表数据文件以供预览", - "interface_type": "method", - "name": "Visualizer::generate_simulation_charts", - "parameters": [], - "return_type": "Result<()>", - "visibility": "public" - } - ], - "responsibilities": [ - "初始化可视化输出目录", - "生成评估报告的可视化图表框架", - "输出模拟评估数据以支持前端预览", - "提供详细的图表生成说明文档", - "协调不同评估维度(召回、有效、性能)的可视化流程" - ] - }, - { - "code_dossier": { - "code_purpose": "tool", - "description": "报告生成器,用于生成Cortex-Mem评估的JSON、Markdown和HTML格式报告", - "file_path": "examples/cortex-mem-evaluation/src/report/generator.rs", - "functions": [ - "new", - "generate_json_report", - "generate_markdown_report", - "generate_html_report", - "generate_comprehensive_report" - ], - "importance_score": 0.8, - "interfaces": [ - "ReportGenerator::new", - "ReportGenerator::generate_json_report", - "ReportGenerator::generate_markdown_report", - "ReportGenerator::generate_html_report", - "ReportGenerator::generate_comprehensive_report" - ], - "name": "generator.rs", - "source_summary": "//! 报告生成器\n//! \n//! 生成评估报告\n\nuse anyhow::Result;\nuse serde::Serialize;\nuse std::path::PathBuf;\n\n/// 报告生成器\npub struct ReportGenerator {\n /// 输出目录\n output_dir: PathBuf,\n}\n\nimpl ReportGenerator {\n /// 创建新的报告生成器\n pub fn new(output_dir: PathBuf) -> Self {\n Self { output_dir }\n }\n \n /// 生成JSON报告\n pub fn generate_json_report(&self, data: &T, filename: &str) -> Result<()> {\n let json = serde_json::to_string_pretty(data)?;\n let path = self.output_dir.join(filename);\n std::fs::write(path, json)?;\n Ok(())\n }\n \n /// 生成Markdown报告\n pub fn generate_markdown_report(&self, content: &str, filename: &str) -> Result<()> {\n let path = self.output_dir.join(filename);\n std::fs::write(path, content)?;\n Ok(())\n }\n \n /// 生成HTML报告\n pub fn generate_html_report(&self, content: &str, filename: &str) -> Result<()> {\n let html = format!(\n r#\"\n\n\n \n \n Cortex-Mem 评估报告\n \n\n\n

📊 Cortex-Mem 评估报告

\n

生成时间: {}

\n
\n {}\n\n\"#,\n chrono::Utc::now().format(\"%Y-%m-%d %H:%M:%S\"),\n content\n );\n \n let path = self.output_dir.join(filename);\n std::fs::write(path, html)?;\n Ok(())\n }\n \n /// 生成综合报告\n pub fn generate_comprehensive_report(\n &self,\n recall_metrics: Option<&serde_json::Value>,\n effectiveness_metrics: Option<&serde_json::Value>,\n performance_metrics: Option<&serde_json::Value>,\n ) -> Result<()> {\n let mut report = String::new();\n \n report.push_str(\"# Cortex-Mem 核心能力综合评估报告\\n\\n\");\n report.push_str(&format!(\"**报告生成时间**: {}\\n\\n\", \n chrono::Utc::now().format(\"%Y-%m-%d %H:%M:%S\")));\n \n // 执行摘要\n report.push_str(\"## 📋 执行摘要\\n\\n\");\n report.push_str(\"本报告总结了 Cortex-Mem 核心能力的评估结果,包括召回率、记忆有效性和性能三个方面。\\n\\n\");\n \n // 召回率评估结果\n if let Some(metrics) = recall_metrics {\n report.push_str(\"## 🔍 召回率评估结果\\n\\n\");\n report.push_str(\"### 关键指标\\n\");\n report.push_str(\"| 指标 | 值 | 说明 |\\n\");\n report.push_str(\"|------|-----|------|\\n\");\n \n if let Some(precision) = metrics.get(\"precision_at_k\") {\n if let Some(p1) = precision.get(\"1\") {\n report.push_str(&format!(\"| Precision@1 | {:.3} | 第一个结果的精确率 |\\n\", p1));\n }\n if let Some(p5) = precision.get(\"5\") {\n report.push_str(&format!(\"| Precision@5 | {:.3} | 前5个结果的精确率 |\\n\", p5));\n }\n }\n \n if let Some(recall) = metrics.get(\"recall_at_k\") {\n if let Some(r5) = recall.get(\"5\") {\n report.push_str(&format!(\"| Recall@5 | {:.3} | 前5个结果的召回率 |\\n\", r5));\n }\n }\n \n if let Some(map) = metrics.get(\"mean_average_precision\") {\n report.push_str(&format!(\"| MAP | {:.3} | 平均精确率均值 |\\n\", map));\n }\n \n if let Some(ndcg) = metrics.get(\"normalized_discounted_cumulative_gain\") {\n report.push_str(&format!(\"| NDCG | {:.3} | 归一化折损累计增益 |\\n\", ndcg));\n }\n report.push_str(\"\\n\");\n }\n \n // 有效性评估结果\n if let Some(metrics) = effectiveness_metrics {\n report.push_str(\"## ✅ 记忆有效性评估结果\\n\\n\");\n \n if let Some(overall) = metrics.get(\"overall_score\") {\n report.push_str(&format!(\"### 综合得分: {:.2}/1.00\\n\\n\", overall));\n }\n \n report.push_str(\"### 各维度得分\\n\");\n report.push_str(\"| 维度 | 得分 | 状态 |\\n\");\n report.push_str(\"|------|------|------|\\n\");\n \n if let Some(fact) = metrics.get(\"fact_extraction_accuracy\") {\n if let Some(f1) = fact.get(\"f1_score\") {\n let score = f1.as_f64().unwrap_or(0.0);\n let status = if score >= 0.9 { \"✅ 优秀\" } else if score >= 0.7 { \"⚠️ 良好\" } else { \"❌ 需改进\" };\n report.push_str(&format!(\"| 事实提取 | {:.3} | {} |\\n\", score, status));\n }\n }\n \n if let Some(class) = metrics.get(\"classification_accuracy\") {\n if let Some(accuracy) = class.get(\"accuracy\") {\n let score = accuracy.as_f64().unwrap_or(0.0);\n let status = if score >= 0.9 { \"✅ 优秀\" } else if score >= 0.7 { \"⚠️ 良好\" } else { \"❌ 需改进\" };\n report.push_str(&format!(\"| 记忆分类 | {:.3} | {} |\\n\", score, status));\n }\n }\n report.push_str(\"\\n\");\n }\n \n // 性能评估结果\n if let Some(_metrics) = performance_metrics {\n report.push_str(\"## ⚡ 性能评估结果\\n\\n\");\n report.push_str(\"性能评估需要实际的 MemoryManager 实例才能运行。\\n\\n\");\n report.push_str(\"### 支持的测试类型\\n\");\n report.push_str(\"1. **基准测试**: 测量基本操作性能\\n\");\n report.push_str(\"2. **负载测试**: 模拟不同并发用户\\n\");\n report.push_str(\"3. **压力测试**: 测试系统极限\\n\");\n report.push_str(\"4. **可扩展性测试**: 验证不同规模下的性能\\n\\n\");\n }\n \n // 结论和建议\n report.push_str(\"## 🎯 结论与建议\\n\\n\");\n \n if recall_metrics.is_some() || effectiveness_metrics.is_some() {\n report.push_str(\"### 优势\\n\");\n report.push_str(\"- 评估框架结构完整,覆盖核心能力维度\\n\");\n report.push_str(\"- 支持多种评估指标和测试场景\\n\");\n report.push_str(\"- 配置灵活,可根据需要调整评估参数\\n\\n\");\n \n report.push_str(\"### 改进建议\\n\");\n report.push_str(\"1. **集成实际系统**: 将 MemoryManager 实例注入评估框架\\n\");\n report.push_str(\"2. **扩展测试数据集**: 增加更多样化的测试用例\\n\");\n report.push_str(\"3. **优化评估算法**: 改进指标计算方法的准确性\\n\");\n report.push_str(\"4. **添加自动化**: 实现持续集成和自动化评估\\n\\n\");\n } else {\n report.push_str(\"### 框架状态\\n\");\n report.push_str(\"✅ **框架就绪**: 评估框架已实现,结构完整\\n\");\n report.push_str(\"⚠️ **需要集成**: 需要提供 MemoryManager 实例以运行实际评估\\n\");\n report.push_str(\"📊 **支持全面**: 覆盖召回率、有效性、性能三个维度的评估\\n\\n\");\n }\n \n report.push_str(\"### 下一步计划\\n\");\n report.push_str(\"1. 运行实际评估获取基准数据\\n\");\n report.push_str(\"2. 根据评估结果优化系统实现\\n\");\n report.push_str(\"3. 建立定期评估机制\\n\");\n report.push_str(\"4. 扩展评估场景和测试用例\\n\\n\");\n \n report.push_str(\"---\\n\");\n report.push_str(\"*报告由 Cortex-Mem 评估框架生成*\\n\");\n \n // 生成各种格式的报告\n self.generate_markdown_report(&report, \"comprehensive_report.md\")?;\n self.generate_html_report(&report, \"comprehensive_report.html\")?;\n \n // 生成JSON格式的原始数据\n let json_data = serde_json::json!({\n \"report_generated_at\": chrono::Utc::now().to_rfc3339(),\n \"recall_metrics\": recall_metrics,\n \"effectiveness_metrics\": effectiveness_metrics,\n \"performance_metrics\": performance_metrics,\n \"report_version\": \"1.0.0\"\n });\n \n self.generate_json_report(&json_data, \"comprehensive_report.json\")?;\n \n Ok(())\n }\n}\n" - }, - "complexity_metrics": { - "cyclomatic_complexity": 21.0, - "lines_of_code": 212, - "number_of_classes": 1, - "number_of_functions": 5 - }, - "dependencies": [ - { - "dependency_type": "crate", + "dependency_type": "import", "is_external": true, - "line_number": 1, - "name": "anyhow", + "line_number": null, + "name": "langgraph.store.memory.InMemoryStore", "path": null, "version": null }, { - "dependency_type": "crate", + "dependency_type": "import", "is_external": true, - "line_number": 2, - "name": "serde", + "line_number": null, + "name": "httpx.Client", "path": null, "version": null }, { - "dependency_type": "crate", + "dependency_type": "import", "is_external": false, - "line_number": 3, - "name": "std", + "line_number": null, + "name": ".config_utils.check_openai_config", "path": null, "version": null - } - ], - "detailed_description": "ReportGenerator是一个功能工具类组件,负责生成Cortex-Mem系统评估报告。它支持多种输出格式,包括JSON、Markdown和HTML。组件的核心功能是将评估数据转换为结构化的报告,其中综合报告会整合召回率、记忆有效性和性能三个维度的评估结果,并生成包含执行摘要、关键指标、维度得分、结论建议等内容的完整报告。HTML报告包含内嵌的CSS样式,确保输出的可视化效果。组件采用不可变设计,通过构造函数注入输出目录,所有报告生成方法都是幂等的。", - "interfaces": [ + }, { - "description": "创建新的报告生成器实例,配置输出目录", - "interface_type": "constructor", - "name": "ReportGenerator::new", - "parameters": [ - { - "description": "报告输出目录路径", - "is_optional": false, - "name": "output_dir", - "param_type": "PathBuf" - } - ], - "return_type": "ReportGenerator", - "visibility": "public" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": ".config_utils.get_config_value", + "path": null, + "version": null }, { - "description": "生成格式化的JSON报告文件", - "interface_type": "method", - "name": "ReportGenerator::generate_json_report", - "parameters": [ - { - "description": "需要序列化的数据", - "is_optional": false, - "name": "data", - "param_type": "T: Serialize" - }, - { - "description": "输出文件名", - "is_optional": false, - "name": "filename", - "param_type": "&str" - } - ], - "return_type": "Result<()>", - "visibility": "public" + "dependency_type": "import", + "is_external": false, + "line_number": null, + "name": ".config_utils.validate_config", + "path": null, + "version": null }, { - "description": "生成Markdown报告文件", - "interface_type": "method", - "name": "ReportGenerator::generate_markdown_report", - "parameters": [ - { - "description": "Markdown内容", - "is_optional": false, - "name": "content", - "param_type": "&str" - }, - { - "description": "输出文件名", - "is_optional": false, - "name": "filename", - "param_type": "&str" - } - ], - "return_type": "Result<()>", - "visibility": "public" + "dependency_type": "import", + "is_external": true, + "line_number": null, + "name": "langgraph.store.memory.InMemoryStore", + "path": null, + "version": null }, { - "description": "生成带样式的HTML报告文件", - "interface_type": "method", - "name": "ReportGenerator::generate_html_report", - "parameters": [ - { - "description": "HTML主体内容", - "is_optional": false, - "name": "content", - "param_type": "&str" - }, - { - "description": "输出文件名", - "is_optional": false, - "name": "filename", - "param_type": "&str" - } - ], - "return_type": "Result<()>", - "visibility": "public" + "dependency_type": "built_in", + "is_external": false, + "line_number": null, + "name": "json", + "path": null, + "version": null }, { - "description": "生成整合多维度指标的综合评估报告", - "interface_type": "method", - "name": "ReportGenerator::generate_comprehensive_report", + "dependency_type": "import", + "is_external": true, + "line_number": null, + "name": "httpx", + "path": null, + "version": null + } + ], + "detailed_description": "The LangMemSearch class implements a memory retrieval and question-answering system for evaluating conversational AI agents. It integrates with LangMem (via InMemoryStore) to retrieve conversation memories, uses semantic keyword matching to score relevance, and employs OpenAI's LLM to generate concise answers based on retrieved memories. The component processes structured question-answer datasets, supports multi-user memory contexts, and saves results incrementally to a JSON file. It includes configuration loading, retry logic for API calls, and timing metrics for performance analysis.", + "interfaces": [ + { + "description": null, + "interface_type": "class", + "name": "LangMemSearch", "parameters": [ { - "description": "召回率评估指标", + "description": null, "is_optional": true, - "name": "recall_metrics", - "param_type": "Option<&serde_json::Value>" + "name": "output_path", + "param_type": "str" }, { - "description": "记忆有效性评估指标", + "description": null, "is_optional": true, - "name": "effectiveness_metrics", - "param_type": "Option<&serde_json::Value>" + "name": "top_k", + "param_type": "int" }, { - "description": "性能评估指标", + "description": null, "is_optional": true, - "name": "performance_metrics", - "param_type": "Option<&serde_json::Value>" + "name": "config_path", + "param_type": "str" } ], - "return_type": "Result<()>", + "return_type": null, "visibility": "public" } ], "responsibilities": [ - "管理报告输出目录配置", - "生成JSON格式的结构化评估报告", - "生成Markdown格式的人可读报告", - "生成带有样式化的HTML可视化报告", - "整合多维度评估指标生成综合报告" + "Retrieve conversation memories from LangMem store using user-specific namespaces", + "Generate concise LLM-based answers by combining memories from two speakers with a structured prompt", + "Process batch question-answer datasets with incremental result persistence", + "Load and validate configuration from config.toml with fallback paths", + "Manage OpenAI API client with custom HTTP settings and model selection" ] }, { @@ -11263,38 +8416,33 @@ Code analysis results from preprocessing phase, including definitions of functio { "code_dossier": { "code_purpose": "command", - "description": "LLM client trait for text generation and embeddings with structured extraction capabilities", + "description": "OpenAI-based LLM client implementation for text generation, embeddings, and structured extraction. Provides fallback mechanisms and health checking.", "file_path": "cortex-mem-core/src/llm/client.rs", "functions": [ - "create_llm_client", "new", "build_keyword_prompt", "build_summary_prompt", - "parse_keywords" + "parse_keywords", + "create_llm_client" ], "importance_score": 0.8, "interfaces": [ - "LLMClient", - "OpenAILLMClient", - "OpenAILLMClient::new", - "OpenAILLMClient::build_keyword_prompt", - "OpenAILLMClient::build_summary_prompt", - "OpenAILLMClient::parse_keywords" + "LLMClient" ], "name": "client.rs", - "source_summary": "use std::time::Duration;\n\nuse async_trait::async_trait;\nuse rig::providers::openai::CompletionModel;\nuse rig::{\n agent::Agent,\n client::{CompletionClient, EmbeddingsClient},\n completion::Prompt,\n embeddings::EmbeddingsBuilder,\n providers::openai::{Client, EmbeddingModel as OpenAIEmbeddingModel},\n};\nuse tokio::time::sleep;\nuse tracing::{debug, error, info};\n\nuse crate::{\n EmbeddingConfig,\n config::LLMConfig,\n error::{MemoryError, Result},\n llm::extractor_types::*,\n};\n\n/// LLM client trait for text generation and embeddings\n#[async_trait]\npub trait LLMClient: Send + Sync + dyn_clone::DynClone {\n /// Generate text completion\n async fn complete(&self, prompt: &str) -> Result;\n\n /// Generate embeddings for text\n async fn embed(&self, text: &str) -> Result>;\n\n /// Generate embeddings for multiple texts\n async fn embed_batch(&self, texts: &[String]) -> Result>>;\n\n /// Extract key information from memory content\n async fn extract_keywords(&self, content: &str) -> Result>;\n\n /// Summarize memory content\n async fn summarize(&self, content: &str, max_length: Option) -> Result;\n\n /// Check if the LLM service is available\n async fn health_check(&self) -> Result;\n\n // New extractor-based methods\n\n /// Extract structured facts from text using rig extractor\n async fn extract_structured_facts(&self, prompt: &str) -> Result;\n\n /// Extract detailed facts with metadata using rig extractor\n async fn extract_detailed_facts(&self, prompt: &str) -> Result;\n\n /// Extract keywords using rig extractor\n async fn extract_keywords_structured(&self, prompt: &str) -> Result;\n\n /// Classify memory type using rig extractor\n async fn classify_memory(&self, prompt: &str) -> Result;\n\n /// Score memory importance using rig extractor\n async fn score_importance(&self, prompt: &str) -> Result;\n\n /// Check for duplicates using rig extractor\n async fn check_duplicates(&self, prompt: &str) -> Result;\n\n /// Generate summary using rig extractor\n async fn generate_summary(&self, prompt: &str) -> Result;\n\n /// Detect language using rig extractor\n async fn detect_language(&self, prompt: &str) -> Result;\n\n /// Extract entities using rig extractor\n async fn extract_entities(&self, prompt: &str) -> Result;\n\n /// Analyze conversation using rig extractor\n async fn analyze_conversation(&self, prompt: &str) -> Result;\n}\n\ndyn_clone::clone_trait_object!(LLMClient);\n\n/// OpenAI-based LLM client implementation using rig\npub struct OpenAILLMClient {\n completion_model: Agent,\n completion_model_name: String,\n embedding_model: OpenAIEmbeddingModel,\n client: Client,\n}\n\nimpl OpenAILLMClient {\n /// Create a new OpenAI LLM client\n pub fn new(llm_config: &LLMConfig, embedding_config: &EmbeddingConfig) -> Result {\n let client = Client::builder(&llm_config.api_key)\n .base_url(&llm_config.api_base_url)\n .build();\n\n let completion_model: Agent = client\n .completion_model(&llm_config.model_efficient)\n .completions_api()\n .into_agent_builder()\n .temperature(llm_config.temperature as f64)\n .max_tokens(llm_config.max_tokens as u64)\n .build();\n\n let embedding_client = Client::builder(&embedding_config.api_key)\n .base_url(&embedding_config.api_base_url)\n .build();\n let embedding_model = embedding_client.embedding_model(&embedding_config.model_name);\n\n Ok(Self {\n completion_model,\n completion_model_name: llm_config.model_efficient.clone(),\n embedding_model,\n client,\n })\n }\n\n /// Build a prompt for keyword extraction\n fn build_keyword_prompt(&self, content: &str) -> String {\n format!(\n \"Extract the most important keywords and key phrases from the following text. \\\n Return only the keywords separated by commas, without any additional explanation.\\n\\n\\\n Text: {}\\n\\n\\\n Keywords:\",\n content\n )\n }\n\n /// Build a prompt for summarization\n fn build_summary_prompt(&self, content: &str, max_length: Option) -> String {\n let length_instruction = match max_length {\n Some(len) => format!(\"in approximately {} words\", len),\n None => \"concisely\".to_string(),\n };\n\n format!(\n \"Summarize the following text {}. Focus on the main points and key information.\\n\\n\\\n Text: {}\\n\\n\\\n Summary:\",\n length_instruction, content\n )\n }\n\n /// Parse keywords from LLM response\n fn parse_keywords(&self, response: &str) -> Vec {\n response\n .split(',')\n .map(|s| s.trim().to_string())\n .filter(|s| !s.is_empty())\n .collect()\n }\n}\n\nimpl Clone for OpenAILLMClient {\n fn clone(&self) -> Self {\n Self {\n completion_model: self.completion_model.clone(),\n completion_model_name: self.completion_model_name.clone(),\n embedding_model: self.embedding_model.clone(),\n client: self.client.clone(),\n }\n }\n}\n\n#[async_trait]\nimpl LLMClient for OpenAILLMClient {\n async fn complete(&self, prompt: &str) -> Result {\n let response = self\n .completion_model\n .prompt(prompt)\n .multi_turn(10)\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))?;\n\n debug!(\"Generated completion for prompt length: {}\", prompt.len());\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n Ok(response)\n }\n\n async fn embed(&self, text: &str) -> Result> {\n let builder = EmbeddingsBuilder::new(self.embedding_model.clone())\n .document(text)\n .map_err(|e| MemoryError::LLM(e.to_string()))?;\n\n let embeddings = builder\n .build()\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))?;\n\n sleep(Duration::from_secs(2)).await;\n\n if let Some((_, embedding)) = embeddings.first() {\n debug!(\"Generated embedding for text length: {}\", text.len());\n Ok(embedding.first().vec.iter().map(|&x| x as f32).collect())\n } else {\n Err(MemoryError::LLM(\"No embedding generated\".to_string()))\n }\n }\n\n async fn embed_batch(&self, texts: &[String]) -> Result>> {\n let mut results = Vec::new();\n\n // Process in batches to avoid rate limits\n for text in texts {\n let embedding = self.embed(text).await?;\n results.push(embedding);\n }\n\n debug!(\"Generated embeddings for {} texts\", texts.len());\n Ok(results)\n }\n\n async fn extract_keywords(&self, content: &str) -> Result> {\n let prompt = self.build_keyword_prompt(content);\n\n // Use rig's structured extractor instead of string parsing\n match self.extract_keywords_structured(&prompt).await {\n Ok(keyword_extraction) => {\n debug!(\n \"Extracted {} keywords from content using rig extractor\",\n keyword_extraction.keywords.len()\n );\n Ok(keyword_extraction.keywords)\n }\n Err(e) => {\n // Fallback to traditional method if extractor fails\n debug!(\n \"Rig extractor failed, falling back to traditional method: {}\",\n e\n );\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n let response = self.complete(&prompt).await?;\n let keywords = self.parse_keywords(&response);\n debug!(\n \"Extracted {} keywords from content using fallback method\",\n keywords.len()\n );\n Ok(keywords)\n }\n }\n }\n\n async fn summarize(&self, content: &str, max_length: Option) -> Result {\n let prompt = self.build_summary_prompt(content, max_length);\n\n // Use rig's structured extractor instead of string parsing\n match self.generate_summary(&prompt).await {\n Ok(summary_result) => {\n debug!(\n \"Generated summary of length: {} using rig extractor\",\n summary_result.summary.len()\n );\n Ok(summary_result.summary.trim().to_string())\n }\n Err(e) => {\n // Fallback to traditional method if extractor fails\n debug!(\n \"Rig extractor failed, falling back to traditional method: {}\",\n e\n );\n let summary = self.complete(&prompt).await?;\n debug!(\n \"Generated summary of length: {} using fallback method\",\n summary.len()\n );\n Ok(summary.trim().to_string())\n }\n }\n }\n\n async fn health_check(&self) -> Result {\n // Try a simple embedding request to check if the service is available\n match self.embed(\"health check\").await {\n Ok(_) => {\n info!(\"LLM service health check passed\");\n Ok(true)\n }\n Err(e) => {\n error!(\"LLM service health check failed: {}\", e);\n Ok(false)\n }\n }\n }\n\n async fn extract_structured_facts(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn extract_detailed_facts(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn extract_keywords_structured(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(500)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn classify_memory(&self, prompt: &str) -> Result {\n // Instead of using the extractor which requires context, we'll use a simpler approach\n // with direct completion and parse the result\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n // Use direct completion for more reliable classification\n let completion = self.complete(prompt).await?;\n\n // Parse the completion to extract the memory type\n let response = completion.trim();\n\n // Extract the memory type from the response\n let memory_type = if response.to_lowercase().contains(\"conversational\") {\n \"Conversational\".to_string()\n } else if response.to_lowercase().contains(\"procedural\") {\n \"Procedural\".to_string()\n } else if response.to_lowercase().contains(\"factual\") {\n \"Factual\".to_string()\n } else if response.to_lowercase().contains(\"semantic\") {\n \"Semantic\".to_string()\n } else if response.to_lowercase().contains(\"episodic\") {\n \"Episodic\".to_string()\n } else if response.to_lowercase().contains(\"personal\") {\n \"Personal\".to_string()\n } else {\n // Try to extract the exact word and use MemoryType::parse\n response\n .lines()\n .find_map(|line| {\n let line = line.trim();\n [\n \"Conversational\",\n \"Procedural\",\n \"Factual\",\n \"Semantic\",\n \"Episodic\",\n \"Personal\",\n ]\n .iter()\n .find(|&typ| line.contains(typ))\n })\n .map(|typ| typ.to_string())\n .unwrap_or_else(|| \"Conversational\".to_string())\n };\n\n Ok(MemoryClassification {\n memory_type,\n confidence: 0.8, // Default confidence\n reasoning: format!(\"LLM classification response: {}\", response),\n })\n }\n\n async fn score_importance(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(500)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn check_duplicates(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(500)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn generate_summary(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(1000)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn detect_language(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(200)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn extract_entities(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(1000)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn analyze_conversation(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(1500)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n}\n\n/// Factory function to create LLM clients based on configuration\npub fn create_llm_client(\n llm_config: &LLMConfig,\n embedding_config: &EmbeddingConfig,\n) -> Result> {\n // For now, we only support OpenAI\n let client = OpenAILLMClient::new(llm_config, embedding_config)?;\n Ok(Box::new(client))\n}\n" + "source_summary": "use async_trait::async_trait;\nuse rig::providers::openai::CompletionModel;\nuse rig::{\n agent::Agent,\n client::{CompletionClient, EmbeddingsClient},\n completion::Prompt,\n embeddings::EmbeddingsBuilder,\n providers::openai::{Client, EmbeddingModel as OpenAIEmbeddingModel},\n};\nuse tokio::time::sleep;\nuse tracing::{debug, error, info};\n\nuse crate::{\n EmbeddingConfig,\n config::LLMConfig,\n error::{MemoryError, Result},\n llm::extractor_types::*,\n};\n\n/// LLM client trait for text generation and embeddings\n#[async_trait]\npub trait LLMClient: Send + Sync + dyn_clone::DynClone {\n /// Generate text completion\n async fn complete(&self, prompt: &str) -> Result;\n\n /// Generate embeddings for text\n async fn embed(&self, text: &str) -> Result>;\n\n /// Generate embeddings for multiple texts\n async fn embed_batch(&self, texts: &[String]) -> Result>>;\n\n /// Extract key information from memory content\n async fn extract_keywords(&self, content: &str) -> Result>;\n\n /// Summarize memory content\n async fn summarize(&self, content: &str, max_length: Option) -> Result;\n\n /// Check if the LLM service is available\n async fn health_check(&self) -> Result;\n\n // New extractor-based methods\n\n /// Extract structured facts from text using rig extractor\n async fn extract_structured_facts(&self, prompt: &str) -> Result;\n\n /// Extract detailed facts with metadata using rig extractor\n async fn extract_detailed_facts(&self, prompt: &str) -> Result;\n\n /// Extract keywords using rig extractor\n async fn extract_keywords_structured(&self, prompt: &str) -> Result;\n\n /// Classify memory type using rig extractor\n async fn classify_memory(&self, prompt: &str) -> Result;\n\n /// Score memory importance using rig extractor\n async fn score_importance(&self, prompt: &str) -> Result;\n\n /// Check for duplicates using rig extractor\n async fn check_duplicates(&self, prompt: &str) -> Result;\n\n /// Generate summary using rig extractor\n async fn generate_summary(&self, prompt: &str) -> Result;\n\n /// Detect language using rig extractor\n async fn detect_language(&self, prompt: &str) -> Result;\n\n /// Extract entities using rig extractor\n async fn extract_entities(&self, prompt: &str) -> Result;\n\n /// Analyze conversation using rig extractor\n async fn analyze_conversation(&self, prompt: &str) -> Result;\n}\n\ndyn_clone::clone_trait_object!(LLMClient);\n\n/// OpenAI-based LLM client implementation using rig\npub struct OpenAILLMClient {\n completion_model: Agent,\n completion_model_name: String,\n embedding_model: OpenAIEmbeddingModel,\n client: Client,\n}\n\nimpl OpenAILLMClient {\n /// Create a new OpenAI LLM client\n pub fn new(llm_config: &LLMConfig, embedding_config: &EmbeddingConfig) -> Result {\n let client = Client::builder(&llm_config.api_key)\n .base_url(&llm_config.api_base_url)\n .build();\n\n let completion_model: Agent = client\n .completion_model(&llm_config.model_efficient)\n .completions_api()\n .into_agent_builder()\n .temperature(llm_config.temperature as f64)\n .max_tokens(llm_config.max_tokens as u64)\n .build();\n\n let embedding_client = Client::builder(&embedding_config.api_key)\n .base_url(&embedding_config.api_base_url)\n .build();\n let embedding_model = embedding_client.embedding_model(&embedding_config.model_name);\n\n Ok(Self {\n completion_model,\n completion_model_name: llm_config.model_efficient.clone(),\n embedding_model,\n client,\n })\n }\n\n /// Build a prompt for keyword extraction\n fn build_keyword_prompt(&self, content: &str) -> String {\n format!(\n \"Extract the most important keywords and key phrases from the following text. \\\n Return only the keywords separated by commas, without any additional explanation.\\n\\n\\\n Text: {}\\n\\n\\\n Keywords:\",\n content\n )\n }\n\n /// Build a prompt for summarization\n fn build_summary_prompt(&self, content: &str, max_length: Option) -> String {\n let length_instruction = match max_length {\n Some(len) => format!(\"in approximately {} words\", len),\n None => \"concisely\".to_string(),\n };\n\n format!(\n \"Summarize the following text {}. Focus on the main points and key information.\\n\\n\\\n Text: {}\\n\\n\\\n Summary:\",\n length_instruction, content\n )\n }\n\n /// Parse keywords from LLM response\n fn parse_keywords(&self, response: &str) -> Vec {\n response\n .split(',')\n .map(|s| s.trim().to_string())\n .filter(|s| !s.is_empty())\n .collect()\n }\n}\n\nimpl Clone for OpenAILLMClient {\n fn clone(&self) -> Self {\n Self {\n completion_model: self.completion_model.clone(),\n completion_model_name: self.completion_model_name.clone(),\n embedding_model: self.embedding_model.clone(),\n client: self.client.clone(),\n }\n }\n}\n\n#[async_trait]\nimpl LLMClient for OpenAILLMClient {\n async fn complete(&self, prompt: &str) -> Result {\n let response = self\n .completion_model\n .prompt(prompt)\n .multi_turn(10)\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))?;\n\n debug!(\"Generated completion for prompt length: {}\", prompt.len());\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n Ok(response)\n }\n\n async fn embed(&self, text: &str) -> Result> {\n let builder = EmbeddingsBuilder::new(self.embedding_model.clone())\n .document(text)\n .map_err(|e| MemoryError::LLM(e.to_string()))?;\n\n let embeddings = builder\n .build()\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))?;\n\n if let Some((_, embedding)) = embeddings.first() {\n debug!(\"Generated embedding for text length: {}\", text.len());\n Ok(embedding.first().vec.iter().map(|&x| x as f32).collect())\n } else {\n Err(MemoryError::LLM(\"No embedding generated\".to_string()))\n }\n }\n\n async fn embed_batch(&self, texts: &[String]) -> Result>> {\n let mut results = Vec::new();\n\n // Process in batches to avoid rate limits\n for text in texts {\n let embedding = self.embed(text).await?;\n sleep(std::time::Duration::from_secs(1)).await;\n results.push(embedding);\n }\n\n debug!(\"Generated embeddings for {} texts\", texts.len());\n Ok(results)\n }\n\n async fn extract_keywords(&self, content: &str) -> Result> {\n let prompt = self.build_keyword_prompt(content);\n\n // Use rig's structured extractor instead of string parsing\n match self.extract_keywords_structured(&prompt).await {\n Ok(keyword_extraction) => {\n debug!(\n \"Extracted {} keywords from content using rig extractor\",\n keyword_extraction.keywords.len()\n );\n Ok(keyword_extraction.keywords)\n }\n Err(e) => {\n // Fallback to traditional method if extractor fails\n debug!(\n \"Rig extractor failed, falling back to traditional method: {}\",\n e\n );\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n let response = self.complete(&prompt).await?;\n let keywords = self.parse_keywords(&response);\n debug!(\n \"Extracted {} keywords from content using fallback method\",\n keywords.len()\n );\n Ok(keywords)\n }\n }\n }\n\n async fn summarize(&self, content: &str, max_length: Option) -> Result {\n let prompt = self.build_summary_prompt(content, max_length);\n\n // Use rig's structured extractor instead of string parsing\n match self.generate_summary(&prompt).await {\n Ok(summary_result) => {\n debug!(\n \"Generated summary of length: {} using rig extractor\",\n summary_result.summary.len()\n );\n Ok(summary_result.summary.trim().to_string())\n }\n Err(e) => {\n // Fallback to traditional method if extractor fails\n debug!(\n \"Rig extractor failed, falling back to traditional method: {}\",\n e\n );\n let summary = self.complete(&prompt).await?;\n debug!(\n \"Generated summary of length: {} using fallback method\",\n summary.len()\n );\n Ok(summary.trim().to_string())\n }\n }\n }\n\n async fn health_check(&self) -> Result {\n // Try a simple embedding request to check if the service is available\n match self.embed(\"health check\").await {\n Ok(_) => {\n info!(\"LLM service health check passed\");\n Ok(true)\n }\n Err(e) => {\n error!(\"LLM service health check failed: {}\", e);\n Ok(false)\n }\n }\n }\n\n async fn extract_structured_facts(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn extract_detailed_facts(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn extract_keywords_structured(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(500)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn classify_memory(&self, prompt: &str) -> Result {\n // Instead of using the extractor which requires context, we'll use a simpler approach\n // with direct completion and parse the result\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n // Use direct completion for more reliable classification\n let completion = self.complete(prompt).await?;\n\n // Parse the completion to extract the memory type\n let response = completion.trim();\n\n // Extract the memory type from the response\n let memory_type = if response.to_lowercase().contains(\"conversational\") {\n \"Conversational\".to_string()\n } else if response.to_lowercase().contains(\"procedural\") {\n \"Procedural\".to_string()\n } else if response.to_lowercase().contains(\"factual\") {\n \"Factual\".to_string()\n } else if response.to_lowercase().contains(\"semantic\") {\n \"Semantic\".to_string()\n } else if response.to_lowercase().contains(\"episodic\") {\n \"Episodic\".to_string()\n } else if response.to_lowercase().contains(\"personal\") {\n \"Personal\".to_string()\n } else {\n // Try to extract the exact word and use MemoryType::parse\n response\n .lines()\n .find_map(|line| {\n let line = line.trim();\n [\n \"Conversational\",\n \"Procedural\",\n \"Factual\",\n \"Semantic\",\n \"Episodic\",\n \"Personal\",\n ]\n .iter()\n .find(|&typ| line.contains(typ))\n })\n .map(|typ| typ.to_string())\n .unwrap_or_else(|| \"Conversational\".to_string())\n };\n\n Ok(MemoryClassification {\n memory_type,\n confidence: 0.8, // Default confidence\n reasoning: format!(\"LLM classification response: {}\", response),\n })\n }\n\n async fn score_importance(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(500)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn check_duplicates(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(500)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn generate_summary(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(1000)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn detect_language(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(200)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn extract_entities(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(1000)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n\n async fn analyze_conversation(&self, prompt: &str) -> Result {\n let extractor = self\n .client\n .extractor_completions_api::(&self.completion_model_name)\n .preamble(prompt)\n .max_tokens(1500)\n .build();\n\n #[cfg(debug_assertions)]\n tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n\n extractor\n .extract(\"\")\n .await\n .map_err(|e| MemoryError::LLM(e.to_string()))\n }\n}\n\n/// Factory function to create LLM clients based on configuration\npub fn create_llm_client(\n llm_config: &LLMConfig,\n embedding_config: &EmbeddingConfig,\n) -> Result> {\n // For now, we only support OpenAI\n let client = OpenAILLMClient::new(llm_config, embedding_config)?;\n Ok(Box::new(client))\n}\n" }, "complexity_metrics": { "cyclomatic_complexity": 29.0, - "lines_of_code": 500, + "lines_of_code": 497, "number_of_classes": 1, - "number_of_functions": 25 + "number_of_functions": 27 }, "dependencies": [ { "dependency_type": "library", "is_external": true, - "line_number": 3, + "line_number": null, "name": "async_trait", "path": null, "version": null @@ -11302,154 +8450,45 @@ Code analysis results from preprocessing phase, including definitions of functio { "dependency_type": "library", "is_external": true, - "line_number": 6, + "line_number": null, "name": "rig", "path": null, "version": null }, { - "dependency_type": "library", - "is_external": true, - "line_number": 10, - "name": "tokio", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": 11, - "name": "tracing", - "path": null, - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": 14, - "name": "crate", - "path": null, - "version": null - } - ], - "detailed_description": "This component implements a comprehensive LLM client for interacting with OpenAI services, providing both traditional text completion/embedding functionality and advanced structured data extraction through the 'rig' framework. The client supports multiple extraction types including keywords, summaries, facts, entities, and conversation analysis. It features fallback mechanisms for critical operations and includes rate limiting considerations through sleep intervals. The implementation follows a factory pattern for client creation and supports configuration-driven initialization.", - "interfaces": [ - { - "description": "Main trait defining LLM client functionality for text generation and embeddings", - "interface_type": "trait", - "name": "LLMClient", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "OpenAI-based implementation of LLMClient trait", - "interface_type": "struct", - "name": "OpenAILLMClient", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Create a new OpenAI LLM client with configuration", - "interface_type": "method", - "name": "OpenAILLMClient::new", - "parameters": [ - { - "description": "LLM configuration including API key and model settings", - "is_optional": false, - "name": "llm_config", - "param_type": "LLMConfig" - }, - { - "description": "Embedding configuration including API key and model settings", - "is_optional": false, - "name": "embedding_config", - "param_type": "EmbeddingConfig" - } - ], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "Build a prompt for keyword extraction", - "interface_type": "method", - "name": "OpenAILLMClient::build_keyword_prompt", - "parameters": [ - { - "description": "Text content to extract keywords from", - "is_optional": false, - "name": "content", - "param_type": "&str" - } - ], - "return_type": "String", - "visibility": "private" - }, - { - "description": "Build a prompt for summarization", - "interface_type": "method", - "name": "OpenAILLMClient::build_summary_prompt", - "parameters": [ - { - "description": "Text content to summarize", - "is_optional": false, - "name": "content", - "param_type": "&str" - }, - { - "description": "Maximum length for the summary", - "is_optional": true, - "name": "max_length", - "param_type": "Option" - } - ], - "return_type": "String", - "visibility": "private" - }, - { - "description": "Parse keywords from LLM response", - "interface_type": "method", - "name": "OpenAILLMClient::parse_keywords", - "parameters": [ - { - "description": "LLM response containing comma-separated keywords", - "is_optional": false, - "name": "response", - "param_type": "&str" - } - ], - "return_type": "Vec", - "visibility": "private" + "dependency_type": "library", + "is_external": true, + "line_number": null, + "name": "tokio", + "path": null, + "version": null }, { - "description": "Factory function to create LLM clients based on configuration", - "interface_type": "function", - "name": "create_llm_client", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "llm_config", - "param_type": "&LLMConfig" - }, - { - "description": null, - "is_optional": false, - "name": "embedding_config", - "param_type": "&EmbeddingConfig" - } - ], - "return_type": "Result>", + "dependency_type": "library", + "is_external": true, + "line_number": null, + "name": "tracing", + "path": null, + "version": null + } + ], + "detailed_description": "This component implements an OpenAI-based LLM client that provides both traditional text completion and modern structured extraction capabilities through the rig framework. It serves as a unified interface for various LLM operations including text generation, embeddings, keyword extraction, summarization, and specialized extraction tasks like fact extraction, entity recognition, and conversation analysis. The implementation features fallback mechanisms that use traditional completion when structured extraction fails, ensuring reliability. It also includes rate limiting (1 second delay between batch operations) and health checking through embedding requests. The client is designed to be cloned and used concurrently, making it suitable for asynchronous applications.", + "interfaces": [ + { + "description": "Core trait defining LLM operations for text generation, embeddings, and structured extraction", + "interface_type": "trait", + "name": "LLMClient", + "parameters": [], + "return_type": null, "visibility": "public" } ], "responsibilities": [ - "Provide unified interface for LLM text completion and embedding generation", - "Implement structured data extraction for various information types using rig framework", - "Handle fallback mechanisms when structured extraction fails", - "Manage OpenAI client configuration and connection lifecycle", - "Provide health checking and monitoring capabilities for LLM services" + "Provide unified interface for LLM operations including text completion and embeddings", + "Implement structured data extraction using rig framework with fallback to traditional methods", + "Manage OpenAI client configuration and connection with proper error handling", + "Handle batch operations with rate limiting to prevent API rate limit issues", + "Provide health checking capability to verify LLM service availability" ] }, { @@ -13561,244 +10600,528 @@ Code analysis results from preprocessing phase, including definitions of functio "version": null }, { - "dependency_type": "function", + "dependency_type": "function", + "is_external": false, + "line_number": 11, + "name": "create_memory_classifier", + "path": "cortex-mem-core/src/memory/classification", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "DuplicateDetector", + "path": "cortex-mem-core/src/memory/deduplication", + "version": null + }, + { + "dependency_type": "function", + "is_external": false, + "line_number": 11, + "name": "create_duplicate_detector", + "path": "cortex-mem-core/src/memory/deduplication", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "FactExtractor", + "path": "cortex-mem-core/src/memory/extractor", + "version": null + }, + { + "dependency_type": "function", + "is_external": false, + "line_number": 11, + "name": "create_fact_extractor", + "path": "cortex-mem-core/src/memory/extractor", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "ImportanceEvaluator", + "path": "cortex-mem-core/src/memory/importance", + "version": null + }, + { + "dependency_type": "function", + "is_external": false, + "line_number": 11, + "name": "create_importance_evaluator", + "path": "cortex-mem-core/src/memory/importance", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "MemoryUpdater", + "path": "cortex-mem-core/src/memory/updater", + "version": null + }, + { + "dependency_type": "function", + "is_external": false, + "line_number": 11, + "name": "create_memory_updater", + "path": "cortex-mem-core/src/memory/updater", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "Filters", + "path": "cortex-mem-core/src/types", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "Memory", + "path": "cortex-mem-core/src/types", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "MemoryEvent", + "path": "cortex-mem-core/src/types", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "MemoryMetadata", + "path": "cortex-mem-core/src/types", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "MemoryResult", + "path": "cortex-mem-core/src/types", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "MemoryType", + "path": "cortex-mem-core/src/types", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "ScoredMemory", + "path": "cortex-mem-core/src/types", + "version": null + }, + { + "dependency_type": "module", + "is_external": false, + "line_number": 11, + "name": "VectorStore", + "path": "cortex-mem-core/src/vector_store", + "version": null + } + ], + "detailed_description": "The MemoryManager is the central orchestrator for all memory operations in the system. It integrates various components such as vector storage, LLM services, and specialized processors (fact extractor, memory updater, importance evaluator, duplicate detector, memory classifier) to provide a comprehensive memory management solution.\n\nThe component follows a dependency injection pattern where external services (vector store, LLM client, config) are provided at construction time. It then creates specialized processors using these dependencies, allowing for flexible composition and testing.\n\nKey functionality includes:\n- Memory lifecycle management (create, read, update, delete)\n- Advanced memory processing with LLM-enhanced metadata generation\n- Deduplication and merging of similar memories\n- Procedural memory creation for agent workflows\n- Search with configurable similarity thresholds and importance weighting\n- Comprehensive statistics and health monitoring\n\nThe architecture enables both direct memory operations and higher-level conversation-based memory creation, making it suitable for AI agent systems where memory evolves through interactions.", + "interfaces": [ + { + "description": "Core memory manager that orchestrates memory operations", + "interface_type": "struct", + "name": "MemoryManager", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Memory statistics", + "interface_type": "struct", + "name": "MemoryStats", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Health status of memory system components", + "interface_type": "struct", + "name": "HealthStatus", + "parameters": [], + "return_type": null, + "visibility": "public" + } + ], + "responsibilities": [ + "Orchestrate memory operations by coordinating vector storage, LLM services, and specialized processors", + "Manage the complete memory lifecycle including creation, storage, retrieval, update, and deletion", + "Enhance memory with LLM-generated metadata such as keywords, summaries, importance scores, entities, and topics", + "Detect and handle duplicate memories through sophisticated similarity analysis and merging", + "Provide advanced search capabilities with importance-weighted ranking and configurable similarity thresholds" + ] + }, + { + "code_dossier": { + "code_purpose": "specificfeature", + "description": "Optimization issue detector for memory management system that identifies various types of memory optimization opportunities including duplicates, quality issues, outdated content, classification problems, and space inefficiency.", + "file_path": "cortex-mem-core/src/memory/optimization_detector.rs", + "functions": [ + "new", + "with_memory_manager", + "with_config", + "detect_issues", + "detect_duplicates", + "detect_quality_issues", + "detect_outdated_issues", + "detect_classification_issues", + "detect_space_inefficiency", + "calculate_semantic_similarity_from_embeddings", + "cosine_similarity", + "evaluate_memory_quality", + "check_classification_quality", + "detect_memory_type_from_content", + "limit_issues_per_type" + ], + "importance_score": 0.8, + "interfaces": [ + "OptimizationDetector::new", + "OptimizationDetector::with_memory_manager", + "OptimizationDetector::with_config", + "OptimizationDetector::detect_issues", + "OptimizationDetector::detect_duplicates", + "OptimizationDetector::detect_quality_issues", + "OptimizationDetector::detect_outdated_issues", + "OptimizationDetector::detect_classification_issues", + "OptimizationDetector::detect_space_inefficiency", + "OptimizationDetector::evaluate_memory_quality", + "OptimizationDetector::check_classification_quality" + ], + "name": "optimization_detector.rs", + "source_summary": "use chrono::Utc;\nuse std::sync::Arc;\nuse tracing::debug;\nuse uuid::Uuid;\n\nuse crate::{\n error::Result,\n memory::MemoryManager,\n types::{IssueKind, IssueSeverity, OptimizationFilters, OptimizationIssue},\n};\n\n/// 优化问题检测器\npub struct OptimizationDetector {\n // 检测器配置\n config: OptimizationDetectorConfig,\n memory_manager: Arc,\n}\n\n#[derive(Debug, Clone)]\npub struct OptimizationDetectorConfig {\n pub duplicate_threshold: f32,\n pub quality_threshold: f32,\n pub time_decay_days: u32,\n pub max_issues_per_type: usize,\n}\n\nimpl Default for OptimizationDetectorConfig {\n fn default() -> Self {\n Self {\n duplicate_threshold: 0.85,\n quality_threshold: 0.4,\n time_decay_days: 180,\n max_issues_per_type: 1000,\n }\n }\n}\n\nimpl OptimizationDetector {\n pub fn new() -> Self {\n // 需要MemoryManager才能使用,需要使用with_memory_manager\n panic!(\"OptimizationDetector requires MemoryManager. Use with_memory_manager() instead.\");\n }\n\n pub fn with_memory_manager(memory_manager: Arc) -> Self {\n Self {\n config: OptimizationDetectorConfig::default(),\n memory_manager,\n }\n }\n\n pub fn with_config(\n config: OptimizationDetectorConfig,\n memory_manager: Arc,\n ) -> Self {\n Self {\n config,\n memory_manager,\n }\n }\n\n /// 检测需要优化的内存问题\n pub async fn detect_issues(\n &self,\n filters: &OptimizationFilters,\n ) -> Result> {\n tracing::info!(\"开始检测内存优化问题\");\n\n // 转换为MemoryManager使用的Filters\n let mm_filters = crate::types::Filters {\n user_id: filters.user_id.clone(),\n agent_id: filters.agent_id.clone(),\n run_id: None,\n memory_type: filters.memory_type.as_ref().map(|mt| mt.clone()),\n actor_id: None,\n min_importance: filters.importance_range.as_ref().and_then(|r| r.min),\n max_importance: filters.importance_range.as_ref().and_then(|r| r.max),\n created_after: filters.date_range.as_ref().and_then(|r| r.start),\n created_before: filters.date_range.as_ref().and_then(|r| r.end),\n updated_after: None,\n updated_before: None,\n entities: None,\n topics: None,\n custom: filters.custom_filters.clone(),\n };\n\n let mut all_issues = Vec::new();\n\n // 1. 检测重复问题\n let duplicates = self.detect_duplicates(&mm_filters).await?;\n all_issues.extend(duplicates);\n\n // 2. 检测质量问题\n let quality_issues = self.detect_quality_issues(&mm_filters).await?;\n all_issues.extend(quality_issues);\n\n // 3. 检测过时问题\n let outdated_issues = self.detect_outdated_issues(&mm_filters).await?;\n all_issues.extend(outdated_issues);\n\n // 4. 检测分类问题\n let classification_issues = self.detect_classification_issues(&mm_filters).await?;\n all_issues.extend(classification_issues);\n\n // 5. 检测空间效率问题\n let space_issues = self.detect_space_inefficiency(&mm_filters).await?;\n all_issues.extend(space_issues);\n\n // 限制每个类型的问题数量\n all_issues = self.limit_issues_per_type(all_issues);\n\n tracing::info!(\"检测完成,发现 {} 个问题\", all_issues.len());\n Ok(all_issues)\n }\n\n /// 检测重复记忆\n async fn detect_duplicates(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测重复记忆\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n if memories.len() < 2 {\n tracing::debug!(\"记忆数量不足,跳过重复检测\");\n return Ok(issues);\n }\n\n // 直接使用内存管理器进行重复检测\n // TODO: 实现真正的重复检测逻辑\n\n // 检测重复记忆组\n let mut processed_memories = std::collections::HashSet::new();\n\n for (i, memory_i) in memories.iter().enumerate() {\n if processed_memories.contains(&memory_i.id) {\n continue;\n }\n\n // 检查记忆是否已归档\n let is_archived_i = memory_i\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived_i {\n debug!(\"跳过已归档的记忆: {}\", memory_i.id);\n continue;\n }\n\n let mut similar_memories = Vec::new();\n\n // 与其他记忆进行比较\n for (j, memory_j) in memories.iter().enumerate() {\n if i >= j || processed_memories.contains(&memory_j.id) {\n continue;\n }\n\n // 检查记忆是否已归档\n let is_archived_j = memory_j\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived_j {\n debug!(\"跳过已归档的记忆: {}\", memory_j.id);\n continue;\n }\n\n // 使用已存储的embedding计算语义相似度(避免重复调用embed API)\n let similarity = self.calculate_semantic_similarity_from_embeddings(\n &memory_i.embedding,\n &memory_j.embedding,\n &memory_i.content,\n &memory_j.content,\n );\n\n if similarity >= self.config.duplicate_threshold {\n similar_memories.push(memory_j.clone());\n processed_memories.insert(memory_j.id.clone());\n }\n }\n\n if similar_memories.len() > 0 {\n // 发现重复记忆组\n let mut affected_memories = vec![memory_i.clone()];\n affected_memories.extend(similar_memories.clone());\n\n let duplicate_count = affected_memories.len();\n let severity = if similar_memories.len() > 2 {\n IssueSeverity::High\n } else {\n IssueSeverity::Medium\n };\n\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::Duplicate,\n severity,\n description: format!(\"检测到 {} 个高度相似的重复记忆\", duplicate_count),\n affected_memories: affected_memories.iter().map(|m| m.id.clone()).collect(),\n recommendation: format!(\"建议合并这 {} 个重复记忆\", duplicate_count),\n };\n issues.push(issue);\n processed_memories.insert(memory_i.id.clone());\n }\n }\n\n tracing::info!(\"重复检测完成,发现 {} 个重复问题\", issues.len());\n Ok(issues)\n }\n\n /// 检测质量问题\n async fn detect_quality_issues(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测质量问题\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n for memory in memories {\n // 检查记忆是否已归档\n let is_archived = memory\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived {\n debug!(\"跳过已归档的记忆: {}\", memory.id);\n continue;\n }\n\n let quality_score = self.evaluate_memory_quality(&memory).await?;\n\n if quality_score < self.config.quality_threshold {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::LowQuality,\n severity: if quality_score < self.config.quality_threshold / 2.0 {\n IssueSeverity::High\n } else {\n IssueSeverity::Low\n },\n description: format!(\n \"记忆质量评分过低: {:.2} (阈值: {:.2})\",\n quality_score, self.config.quality_threshold\n ),\n affected_memories: vec![memory.id],\n recommendation: \"建议更新或删除低质量记忆\".to_string(),\n };\n issues.push(issue);\n }\n }\n\n tracing::info!(\"质量检测完成,发现 {} 个质量问题\", issues.len());\n Ok(issues)\n }\n\n /// 检测过时问题\n async fn detect_outdated_issues(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测过时问题\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n let _cutoff_date = Utc::now() - chrono::Duration::days(self.config.time_decay_days as i64);\n\n for memory in memories {\n // 检查记忆是否已归档\n let is_archived = memory\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived {\n debug!(\"跳过已归档的记忆: {}\", memory.id);\n continue;\n }\n\n let days_since_update = (Utc::now() - memory.updated_at).num_days();\n let is_outdated = days_since_update as u32 > self.config.time_decay_days;\n\n if is_outdated {\n let severity = if days_since_update as u32 > self.config.time_decay_days * 2 {\n IssueSeverity::High\n } else if days_since_update as u32\n > (self.config.time_decay_days as f32 * 1.5) as u32\n {\n IssueSeverity::Medium\n } else {\n IssueSeverity::Low\n };\n\n let recommendation = if severity == IssueSeverity::High {\n \"建议删除过时记忆\".to_string()\n } else {\n \"建议归档过时记忆\".to_string()\n };\n\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::Outdated,\n severity,\n description: format!(\n \"记忆已 {} 天未更新,超过阈值 {} 天\",\n days_since_update, self.config.time_decay_days\n ),\n affected_memories: vec![memory.id],\n recommendation,\n };\n issues.push(issue);\n }\n }\n\n tracing::info!(\"过时检测完成,发现 {} 个过时问题\", issues.len());\n Ok(issues)\n }\n\n /// 检测分类问题\n async fn detect_classification_issues(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测分类问题\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n for memory in memories {\n // 检查记忆是否已归档\n let is_archived = memory\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived {\n debug!(\"跳过已归档的记忆: {}\", memory.id);\n continue;\n }\n\n let classification_issues = self.check_classification_quality(&memory).await?;\n\n for issue_desc in classification_issues {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::PoorClassification,\n severity: IssueSeverity::Low,\n description: format!(\"分类问题: {}\", issue_desc),\n affected_memories: vec![memory.id.clone()],\n recommendation: \"建议重新分类记忆\".to_string(),\n };\n issues.push(issue);\n }\n }\n\n tracing::info!(\"分类检测完成,发现 {} 个分类问题\", issues.len());\n Ok(issues)\n }\n\n /// 检测空间效率问题\n async fn detect_space_inefficiency(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测空间效率问题\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n // 获取统计数据\n let stats = self.memory_manager.get_stats(filters).await?;\n\n // 1. 检查单个记忆的大小问题\n for memory in &memories {\n // 检查记忆是否已归档\n let is_archived = memory\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived {\n debug!(\"跳过已归档的记忆: {}\", memory.id);\n continue;\n }\n\n let memory_size = memory.content.len() + memory.embedding.len() * 4; // 粗略估算\n\n // 如果记忆超过一定大小且重要性很低\n if memory_size > 10000 && memory.metadata.importance_score < 0.3 {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::SpaceInefficient,\n severity: IssueSeverity::Low,\n description: format!(\n \"大记忆占用空间过多且重要性低,大小: {} 字节\",\n memory_size\n ),\n affected_memories: vec![memory.id.clone()],\n recommendation: \"建议对大记忆进行摘要或归档\".to_string(),\n };\n issues.push(issue);\n }\n }\n\n // 2. 检查总存储情况\n let total_memories = stats.total_count;\n if total_memories > 10000 {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::SpaceInefficient,\n severity: IssueSeverity::Medium,\n description: format!(\"记忆数量过多: {},可能影响查询性能\", total_memories),\n affected_memories: Vec::new(), // 影响所有记忆\n recommendation: \"建议进行深度优化和清理\".to_string(),\n };\n issues.push(issue);\n }\n\n // 3. 检查低重要性记忆(排除已归档的记忆)\n let low_importance_memories: Vec<_> = memories\n .iter()\n .filter(|m| {\n m.metadata.importance_score < 0.2 &&\n // 排除已归档的记忆\n !m.metadata.custom.get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false)\n })\n .collect();\n\n let unarchived_count = total_memories\n - memories\n .iter()\n .filter(|m| {\n m.metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false)\n })\n .count();\n\n if low_importance_memories.len() > unarchived_count / 4 {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::SpaceInefficient,\n severity: IssueSeverity::Medium,\n description: format!(\n \"低重要性记忆过多: {} / {} ({:.1}%)\",\n low_importance_memories.len(),\n unarchived_count,\n low_importance_memories.len() as f64 / unarchived_count as f64 * 100.0\n ),\n affected_memories: low_importance_memories\n .iter()\n .map(|m| m.id.clone())\n .collect(),\n recommendation: \"建议归档或删除低重要性记忆\".to_string(),\n };\n issues.push(issue);\n }\n\n tracing::info!(\"空间效率检测完成,发现 {} 个空间问题\", issues.len());\n Ok(issues)\n }\n\n /// 计算记忆的语义相似度(使用已存储的embedding)\n fn calculate_semantic_similarity_from_embeddings(\n &self, \n embedding1: &[f32], \n embedding2: &[f32],\n content1_preview: &str,\n content2_preview: &str,\n ) -> f32 {\n // 直接计算余弦相似度,无需重新生成embedding\n let similarity = self.cosine_similarity(embedding1, embedding2);\n\n tracing::debug!(\n \"语义相似度计算: {} vs {} = {:.3}\",\n content1_preview.chars().take(50).collect::(),\n content2_preview.chars().take(50).collect::(),\n similarity\n );\n\n similarity\n }\n\n /// 计算余弦相似度\n fn cosine_similarity(&self, vec1: &[f32], vec2: &[f32]) -> f32 {\n if vec1.len() != vec2.len() || vec1.is_empty() {\n return 0.0;\n }\n\n let mut dot_product = 0.0;\n let mut norm1 = 0.0;\n let mut norm2 = 0.0;\n\n for i in 0..vec1.len() {\n dot_product += vec1[i] * vec2[i];\n norm1 += vec1[i] * vec1[i];\n norm2 += vec2[i] * vec2[i];\n }\n\n if norm1 == 0.0 || norm2 == 0.0 {\n return 0.0;\n }\n\n dot_product / (norm1.sqrt() * norm2.sqrt())\n }\n\n /// 评估记忆质量\n async fn evaluate_memory_quality(&self, memory: &crate::types::Memory) -> Result {\n let mut quality_score = 0.0;\n let max_score = 1.0;\n\n // 1. 内容长度评分 (30%)\n let content_length_score = if memory.content.len() < 10 {\n 0.1\n } else if memory.content.len() < 50 {\n 0.5\n } else if memory.content.len() < 200 {\n 0.8\n } else {\n 1.0\n };\n quality_score += content_length_score * 0.3;\n\n // 2. 结构化程度评分 (20%)\n let has_sentences = memory.content.contains('.')\n || memory.content.contains('!')\n || memory.content.contains('?');\n let has_paragraphs = memory.content.contains('\\n');\n let structural_score = if has_sentences && has_paragraphs {\n 1.0\n } else if has_sentences || has_paragraphs {\n 0.7\n } else {\n 0.3\n };\n quality_score += structural_score * 0.2;\n\n // 3. 重要性评分 (20%)\n quality_score += memory.metadata.importance_score * 0.2;\n\n // 4. 元数据完整性 (15%)\n let metadata_score =\n if !memory.metadata.entities.is_empty() && !memory.metadata.topics.is_empty() {\n 1.0\n } else if !memory.metadata.entities.is_empty() || !memory.metadata.topics.is_empty() {\n 0.6\n } else {\n 0.2\n };\n quality_score += metadata_score * 0.15;\n\n // 5. 更新频率评分 (15%)\n let days_since_update = (chrono::Utc::now() - memory.updated_at).num_days();\n let update_score = if days_since_update < 7 {\n 1.0\n } else if days_since_update < 30 {\n 0.8\n } else if days_since_update < 90 {\n 0.5\n } else {\n 0.2\n };\n quality_score += update_score * 0.15;\n\n Ok(quality_score.min(max_score))\n }\n\n /// 检查分类质量\n async fn check_classification_quality(\n &self,\n memory: &crate::types::Memory,\n ) -> Result> {\n let mut issues = Vec::new();\n\n // 只有当内容非常短且为默认类型时才检查类型是否合适\n if memory.metadata.memory_type == crate::types::MemoryType::Conversational\n && memory.content.len() < 20\n {\n tracing::debug!(\"记忆 {} 太短且为默认类型,建议重新分类\", memory.id);\n }\n\n // 2. 检查实体提取 - 只有内容很长时才检查\n if memory.metadata.entities.is_empty() && memory.content.len() > 200 {\n issues.push(\"缺少实体信息\".to_string());\n }\n\n // 3. 检查主题提取 - 只有内容很长时才检查\n if memory.metadata.topics.is_empty() && memory.content.len() > 100 {\n issues.push(\"缺少主题信息\".to_string());\n }\n\n // 4. 检查记忆类型与内容是否匹配 - 更宽松的逻辑\n let detected_type = self.detect_memory_type_from_content(&memory.content).await;\n\n // 如果检测到的类型与当前类型不同,且内容足够长,才认为是问题\n if detected_type != memory.metadata.memory_type && memory.content.len() > 50 {\n issues.push(format!(\n \"记忆类型与内容可能不匹配: 当前 {:?}, 检测到 {:?}\",\n memory.metadata.memory_type, detected_type\n ));\n }\n\n Ok(issues)\n }\n\n /// 使用LLM从内容检测记忆类型\n async fn detect_memory_type_from_content(&self, content: &str) -> crate::types::MemoryType {\n let llm_client = self.memory_manager.llm_client();\n\n // 检查内容是否为空或过短\n if content.trim().is_empty() {\n tracing::warn!(\"记忆内容为空,默认分类为Conversational\");\n return crate::types::MemoryType::Conversational;\n }\n\n if content.trim().len() < 5 {\n tracing::warn!(\"记忆内容过短: '{}',默认分类为Conversational\", content);\n return crate::types::MemoryType::Conversational;\n }\n\n // 记录调试信息\n tracing::debug!(\n \"开始对记忆内容进行LLM分类: '{}...'\",\n content.chars().take(50).collect::()\n );\n\n // 创建分类提示\n let prompt = format!(\n r#\"Classify the following memory content into one of these categories:\n\n1. Conversational - Dialogue, conversations, or interactive exchanges\n2. Procedural - Instructions, how-to information, or step-by-step processes\n3. Factual - Objective facts, data, or verifiable information\n4. Semantic - Concepts, meanings, definitions, or general knowledge\n5. Episodic - Specific events, experiences, or temporal information\n6. Personal - Personal preferences, characteristics, or individual-specific information\n\nContent: \"{}\"\n\nRespond with only the category name (e.g., \"Conversational\", \"Procedural\", etc.):\"#,\n content\n );\n\n // 使用LLM分类器进行分类\n match llm_client.classify_memory(&prompt).await {\n Ok(classification) => {\n let memory_type = crate::types::MemoryType::parse(&classification.memory_type);\n\n tracing::info!(\n \"LLM分类成功: '{}' -> {:?} (置信度: {})\",\n content.chars().take(30).collect::(),\n memory_type,\n classification.confidence\n );\n\n memory_type\n }\n Err(e) => {\n tracing::error!(\n \"LLM分类失败: '{}' -> 错误: {}, 使用默认分类Conversational\",\n content.chars().take(30).collect::(),\n e\n );\n crate::types::MemoryType::Conversational // 失败时的回退\n }\n }\n }\n\n /// 限制每个类型的问题数量\n fn limit_issues_per_type(&self, issues: Vec) -> Vec {\n let mut issues_by_type: std::collections::HashMap> =\n std::collections::HashMap::new();\n\n for issue in &issues {\n issues_by_type\n .entry(issue.kind.clone())\n .or_insert_with(Vec::new)\n .push(issue.clone());\n }\n\n let mut limited_issues = Vec::new();\n\n for (kind, mut kind_issues) in issues_by_type {\n if kind_issues.len() > self.config.max_issues_per_type {\n kind_issues.truncate(self.config.max_issues_per_type);\n tracing::warn!(\n \"{:?} 类型的问题数量超过限制,截取到 {} 个\",\n kind,\n self.config.max_issues_per_type\n );\n }\n limited_issues.extend(kind_issues);\n }\n\n limited_issues\n }\n}\n\nimpl Default for OptimizationDetector {\n fn default() -> Self {\n panic!(\"OptimizationDetector requires MemoryManager. Use with_memory_manager() instead.\");\n }\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 54.0, + "lines_of_code": 741, + "number_of_classes": 2, + "number_of_functions": 17 + }, + "dependencies": [ + { + "dependency_type": "external", + "is_external": true, + "line_number": 1, + "name": "chrono", + "path": null, + "version": null + }, + { + "dependency_type": "standard_library", "is_external": false, - "line_number": 11, - "name": "create_memory_classifier", - "path": "cortex-mem-core/src/memory/classification", + "line_number": 2, + "name": "std::sync::Arc", + "path": null, "version": null }, { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "DuplicateDetector", - "path": "cortex-mem-core/src/memory/deduplication", + "dependency_type": "external", + "is_external": true, + "line_number": 3, + "name": "tracing", + "path": null, "version": null }, { - "dependency_type": "function", - "is_external": false, - "line_number": 11, - "name": "create_duplicate_detector", - "path": "cortex-mem-core/src/memory/deduplication", + "dependency_type": "external", + "is_external": true, + "line_number": 4, + "name": "uuid", + "path": null, "version": null }, { - "dependency_type": "module", + "dependency_type": "internal", "is_external": false, - "line_number": 11, - "name": "FactExtractor", - "path": "cortex-mem-core/src/memory/extractor", + "line_number": 7, + "name": "crate::error::Result", + "path": "cortex-mem-core/src/error.rs", "version": null }, { - "dependency_type": "function", + "dependency_type": "internal", "is_external": false, - "line_number": 11, - "name": "create_fact_extractor", - "path": "cortex-mem-core/src/memory/extractor", + "line_number": 8, + "name": "crate::memory::MemoryManager", + "path": "cortex-mem-core/src/memory/mod.rs", "version": null }, { - "dependency_type": "module", + "dependency_type": "internal", "is_external": false, - "line_number": 11, - "name": "ImportanceEvaluator", - "path": "cortex-mem-core/src/memory/importance", + "line_number": 9, + "name": "crate::types::IssueKind", + "path": "cortex-mem-core/src/types/mod.rs", "version": null }, { - "dependency_type": "function", + "dependency_type": "internal", "is_external": false, - "line_number": 11, - "name": "create_importance_evaluator", - "path": "cortex-mem-core/src/memory/importance", + "line_number": 9, + "name": "crate::types::IssueSeverity", + "path": "cortex-mem-core/src/types/mod.rs", "version": null }, { - "dependency_type": "module", + "dependency_type": "internal", "is_external": false, - "line_number": 11, - "name": "MemoryUpdater", - "path": "cortex-mem-core/src/memory/updater", + "line_number": 9, + "name": "crate::types::OptimizationFilters", + "path": "cortex-mem-core/src/types/mod.rs", "version": null }, { - "dependency_type": "function", + "dependency_type": "internal", "is_external": false, - "line_number": 11, - "name": "create_memory_updater", - "path": "cortex-mem-core/src/memory/updater", + "line_number": 9, + "name": "crate::types::OptimizationIssue", + "path": "cortex-mem-core/src/types/mod.rs", "version": null - }, + } + ], + "detailed_description": "The OptimizationDetector is a comprehensive memory optimization analysis component that systematically identifies various types of memory issues in a memory management system. It implements a multi-faceted approach to memory optimization by detecting five main categories of issues: duplicate memories based on semantic similarity using pre-computed embeddings, low-quality memories based on content length, structure, importance score, metadata completeness, and update frequency, outdated memories based on last update time exceeding configurable thresholds, poor classification issues where memory type doesn't match content characteristics or metadata is incomplete, and space inefficiency issues including oversized memories with low importance, excessive total memory count, and high proportions of low-importance memories. The detector uses a configurable threshold-based approach with parameters for duplicate detection, quality assessment, time decay, and issue limits. It integrates with the MemoryManager to retrieve memories and statistics, and leverages an LLM client for content-based memory type classification. The component follows a builder pattern for construction with required MemoryManager dependency and optional configuration. All detection methods filter out archived memories and can be customized through OptimizationFilters. The results are consolidated and limited by issue type to prevent overwhelming outputs.", + "interfaces": [ { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "Filters", - "path": "cortex-mem-core/src/types", - "version": null + "description": "Default constructor that panics to enforce proper initialization with MemoryManager", + "interface_type": "constructor", + "name": "OptimizationDetector::new", + "parameters": [], + "return_type": "Self", + "visibility": "public" }, { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "Memory", - "path": "cortex-mem-core/src/types", - "version": null + "description": "Constructor that initializes detector with required memory manager dependency", + "interface_type": "constructor", + "name": "OptimizationDetector::with_memory_manager", + "parameters": [ + { + "description": "Shared reference to memory manager for data access", + "is_optional": false, + "name": "memory_manager", + "param_type": "Arc" + } + ], + "return_type": "Self", + "visibility": "public" }, { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "MemoryEvent", - "path": "cortex-mem-core/src/types", - "version": null + "description": "Constructor that initializes detector with custom configuration and memory manager", + "interface_type": "constructor", + "name": "OptimizationDetector::with_config", + "parameters": [ + { + "description": "Custom configuration for detection thresholds", + "is_optional": false, + "name": "config", + "param_type": "OptimizationDetectorConfig" + }, + { + "description": "Shared reference to memory manager for data access", + "is_optional": false, + "name": "memory_manager", + "param_type": "Arc" + } + ], + "return_type": "Self", + "visibility": "public" }, { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "MemoryMetadata", - "path": "cortex-mem-core/src/types", - "version": null + "description": "Main entry point that orchestrates all detection algorithms and returns consolidated optimization issues", + "interface_type": "method", + "name": "OptimizationDetector::detect_issues", + "parameters": [ + { + "description": "Filters to constrain the scope of detection", + "is_optional": false, + "name": "filters", + "param_type": "&OptimizationFilters" + } + ], + "return_type": "Result>", + "visibility": "public" }, { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "MemoryResult", - "path": "cortex-mem-core/src/types", - "version": null + "description": "Detects duplicate memories by comparing semantic similarity of embeddings", + "interface_type": "method", + "name": "OptimizationDetector::detect_duplicates", + "parameters": [ + { + "description": "Converted filters for memory retrieval", + "is_optional": false, + "name": "filters", + "param_type": "&crate::types::Filters" + } + ], + "return_type": "Result>", + "visibility": "private" }, { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "MemoryType", - "path": "cortex-mem-core/src/types", - "version": null + "description": "Identifies low-quality memories based on multi-dimensional quality scoring", + "interface_type": "method", + "name": "OptimizationDetector::detect_quality_issues", + "parameters": [ + { + "description": "Converted filters for memory retrieval", + "is_optional": false, + "name": "filters", + "param_type": "&crate::types::Filters" + } + ], + "return_type": "Result>", + "visibility": "private" }, { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "ScoredMemory", - "path": "cortex-mem-core/src/types", - "version": null + "description": "Detects memories that haven't been updated within configured time thresholds", + "interface_type": "method", + "name": "OptimizationDetector::detect_outdated_issues", + "parameters": [ + { + "description": "Converted filters for memory retrieval", + "is_optional": false, + "name": "filters", + "param_type": "&crate::types::Filters" + } + ], + "return_type": "Result>", + "visibility": "private" }, { - "dependency_type": "module", - "is_external": false, - "line_number": 11, - "name": "VectorStore", - "path": "cortex-mem-core/src/vector_store", - "version": null - } - ], - "detailed_description": "The MemoryManager is the central orchestrator for all memory operations in the system. It integrates various components such as vector storage, LLM services, and specialized processors (fact extractor, memory updater, importance evaluator, duplicate detector, memory classifier) to provide a comprehensive memory management solution.\n\nThe component follows a dependency injection pattern where external services (vector store, LLM client, config) are provided at construction time. It then creates specialized processors using these dependencies, allowing for flexible composition and testing.\n\nKey functionality includes:\n- Memory lifecycle management (create, read, update, delete)\n- Advanced memory processing with LLM-enhanced metadata generation\n- Deduplication and merging of similar memories\n- Procedural memory creation for agent workflows\n- Search with configurable similarity thresholds and importance weighting\n- Comprehensive statistics and health monitoring\n\nThe architecture enables both direct memory operations and higher-level conversation-based memory creation, making it suitable for AI agent systems where memory evolves through interactions.", - "interfaces": [ + "description": "Identifies issues with memory classification including type-content mismatches and missing metadata", + "interface_type": "method", + "name": "OptimizationDetector::detect_classification_issues", + "parameters": [ + { + "description": "Converted filters for memory retrieval", + "is_optional": false, + "name": "filters", + "param_type": "&crate::types::Filters" + } + ], + "return_type": "Result>", + "visibility": "private" + }, { - "description": "Core memory manager that orchestrates memory operations", - "interface_type": "struct", - "name": "MemoryManager", - "parameters": [], - "return_type": null, - "visibility": "public" + "description": "Detects various space efficiency problems including oversized memories and excessive memory counts", + "interface_type": "method", + "name": "OptimizationDetector::detect_space_inefficiency", + "parameters": [ + { + "description": "Converted filters for memory retrieval", + "is_optional": false, + "name": "filters", + "param_type": "&crate::types::Filters" + } + ], + "return_type": "Result>", + "visibility": "private" }, { - "description": "Memory statistics", - "interface_type": "struct", - "name": "MemoryStats", - "parameters": [], - "return_type": null, - "visibility": "public" + "description": "Calculates a composite quality score for a memory based on multiple factors", + "interface_type": "method", + "name": "OptimizationDetector::evaluate_memory_quality", + "parameters": [ + { + "description": "Memory to evaluate for quality", + "is_optional": false, + "name": "memory", + "param_type": "&crate::types::Memory" + } + ], + "return_type": "Result", + "visibility": "private" }, { - "description": "Health status of memory system components", - "interface_type": "struct", - "name": "HealthStatus", - "parameters": [], - "return_type": null, - "visibility": "public" + "description": "Evaluates the quality of memory classification and metadata completeness", + "interface_type": "method", + "name": "OptimizationDetector::check_classification_quality", + "parameters": [ + { + "description": "Memory to evaluate for classification quality", + "is_optional": false, + "name": "memory", + "param_type": "&crate::types::Memory" + } + ], + "return_type": "Result>", + "visibility": "private" } ], "responsibilities": [ - "Orchestrate memory operations by coordinating vector storage, LLM services, and specialized processors", - "Manage the complete memory lifecycle including creation, storage, retrieval, update, and deletion", - "Enhance memory with LLM-generated metadata such as keywords, summaries, importance scores, entities, and topics", - "Detect and handle duplicate memories through sophisticated similarity analysis and merging", - "Provide advanced search capabilities with importance-weighted ranking and configurable similarity thresholds" + "Detect duplicate memories by calculating semantic similarity from pre-computed embeddings using cosine similarity", + "Identify low-quality memories through multi-dimensional quality scoring based on content length, structure, importance, metadata completeness, and recency", + "Detect outdated memories by comparing last update time against configurable time decay thresholds", + "Identify memory classification issues including inappropriate memory types, missing entities/topics, and type-content mismatches", + "Detect space inefficiency problems such as oversized low-importance memories, excessive memory counts, and high proportions of low-importance unarchived memories" ] }, { "code_dossier": { "code_purpose": "specificfeature", - "description": "Optimization issue detector for memory management system that identifies various types of memory optimization opportunities including duplicates, quality issues, outdated content, classification problems, and space inefficiency.", - "file_path": "cortex-mem-core/src/memory/optimization_detector.rs", + "description": "核心协调组件,负责主动内存优化操作的全流程管理,包括检测、分析、执行和报告。", + "file_path": "cortex-mem-core/src/memory/optimizer.rs", "functions": [ - "new", - "with_memory_manager", - "with_config", - "detect_issues", - "detect_duplicates", - "detect_quality_issues", - "detect_outdated_issues", - "detect_classification_issues", - "detect_space_inefficiency", - "calculate_semantic_similarity_from_embeddings", - "cosine_similarity", - "evaluate_memory_quality", - "check_classification_quality", - "detect_memory_type_from_content", - "limit_issues_per_type" + "optimize", + "create_optimization_plan", + "get_optimization_status", + "cancel_optimization", + "create_dry_run_result", + "update_optimization_status", + "create" ], "importance_score": 0.8, "interfaces": [ - "OptimizationDetector::new", - "OptimizationDetector::with_memory_manager", - "OptimizationDetector::with_config", - "OptimizationDetector::detect_issues", - "OptimizationDetector::detect_duplicates", - "OptimizationDetector::detect_quality_issues", - "OptimizationDetector::detect_outdated_issues", - "OptimizationDetector::detect_classification_issues", - "OptimizationDetector::detect_space_inefficiency", - "OptimizationDetector::evaluate_memory_quality", - "OptimizationDetector::check_classification_quality" + "MemoryOptimizer" ], - "name": "optimization_detector.rs", - "source_summary": "use chrono::Utc;\nuse std::sync::Arc;\nuse tracing::debug;\nuse uuid::Uuid;\n\nuse crate::{\n error::Result,\n memory::MemoryManager,\n types::{IssueKind, IssueSeverity, OptimizationFilters, OptimizationIssue},\n};\n\n/// 优化问题检测器\npub struct OptimizationDetector {\n // 检测器配置\n config: OptimizationDetectorConfig,\n memory_manager: Arc,\n}\n\n#[derive(Debug, Clone)]\npub struct OptimizationDetectorConfig {\n pub duplicate_threshold: f32,\n pub quality_threshold: f32,\n pub time_decay_days: u32,\n pub max_issues_per_type: usize,\n}\n\nimpl Default for OptimizationDetectorConfig {\n fn default() -> Self {\n Self {\n duplicate_threshold: 0.85,\n quality_threshold: 0.4,\n time_decay_days: 180,\n max_issues_per_type: 1000,\n }\n }\n}\n\nimpl OptimizationDetector {\n pub fn new() -> Self {\n // 需要MemoryManager才能使用,需要使用with_memory_manager\n panic!(\"OptimizationDetector requires MemoryManager. Use with_memory_manager() instead.\");\n }\n\n pub fn with_memory_manager(memory_manager: Arc) -> Self {\n Self {\n config: OptimizationDetectorConfig::default(),\n memory_manager,\n }\n }\n\n pub fn with_config(\n config: OptimizationDetectorConfig,\n memory_manager: Arc,\n ) -> Self {\n Self {\n config,\n memory_manager,\n }\n }\n\n /// 检测需要优化的内存问题\n pub async fn detect_issues(\n &self,\n filters: &OptimizationFilters,\n ) -> Result> {\n tracing::info!(\"开始检测内存优化问题\");\n\n // 转换为MemoryManager使用的Filters\n let mm_filters = crate::types::Filters {\n user_id: filters.user_id.clone(),\n agent_id: filters.agent_id.clone(),\n run_id: None,\n memory_type: filters.memory_type.as_ref().map(|mt| mt.clone()),\n actor_id: None,\n min_importance: filters.importance_range.as_ref().and_then(|r| r.min),\n max_importance: filters.importance_range.as_ref().and_then(|r| r.max),\n created_after: filters.date_range.as_ref().and_then(|r| r.start),\n created_before: filters.date_range.as_ref().and_then(|r| r.end),\n updated_after: None,\n updated_before: None,\n entities: None,\n topics: None,\n custom: filters.custom_filters.clone(),\n };\n\n let mut all_issues = Vec::new();\n\n // 1. 检测重复问题\n let duplicates = self.detect_duplicates(&mm_filters).await?;\n all_issues.extend(duplicates);\n\n // 2. 检测质量问题\n let quality_issues = self.detect_quality_issues(&mm_filters).await?;\n all_issues.extend(quality_issues);\n\n // 3. 检测过时问题\n let outdated_issues = self.detect_outdated_issues(&mm_filters).await?;\n all_issues.extend(outdated_issues);\n\n // 4. 检测分类问题\n let classification_issues = self.detect_classification_issues(&mm_filters).await?;\n all_issues.extend(classification_issues);\n\n // 5. 检测空间效率问题\n let space_issues = self.detect_space_inefficiency(&mm_filters).await?;\n all_issues.extend(space_issues);\n\n // 限制每个类型的问题数量\n all_issues = self.limit_issues_per_type(all_issues);\n\n tracing::info!(\"检测完成,发现 {} 个问题\", all_issues.len());\n Ok(all_issues)\n }\n\n /// 检测重复记忆\n async fn detect_duplicates(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测重复记忆\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n if memories.len() < 2 {\n tracing::debug!(\"记忆数量不足,跳过重复检测\");\n return Ok(issues);\n }\n\n // 直接使用内存管理器进行重复检测\n // TODO: 实现真正的重复检测逻辑\n\n // 检测重复记忆组\n let mut processed_memories = std::collections::HashSet::new();\n\n for (i, memory_i) in memories.iter().enumerate() {\n if processed_memories.contains(&memory_i.id) {\n continue;\n }\n\n // 检查记忆是否已归档\n let is_archived_i = memory_i\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived_i {\n debug!(\"跳过已归档的记忆: {}\", memory_i.id);\n continue;\n }\n\n let mut similar_memories = Vec::new();\n\n // 与其他记忆进行比较\n for (j, memory_j) in memories.iter().enumerate() {\n if i >= j || processed_memories.contains(&memory_j.id) {\n continue;\n }\n\n // 检查记忆是否已归档\n let is_archived_j = memory_j\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived_j {\n debug!(\"跳过已归档的记忆: {}\", memory_j.id);\n continue;\n }\n\n // 使用已存储的embedding计算语义相似度(避免重复调用embed API)\n let similarity = self.calculate_semantic_similarity_from_embeddings(\n &memory_i.embedding,\n &memory_j.embedding,\n &memory_i.content,\n &memory_j.content,\n );\n\n if similarity >= self.config.duplicate_threshold {\n similar_memories.push(memory_j.clone());\n processed_memories.insert(memory_j.id.clone());\n }\n }\n\n if similar_memories.len() > 0 {\n // 发现重复记忆组\n let mut affected_memories = vec![memory_i.clone()];\n affected_memories.extend(similar_memories.clone());\n\n let duplicate_count = affected_memories.len();\n let severity = if similar_memories.len() > 2 {\n IssueSeverity::High\n } else {\n IssueSeverity::Medium\n };\n\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::Duplicate,\n severity,\n description: format!(\"检测到 {} 个高度相似的重复记忆\", duplicate_count),\n affected_memories: affected_memories.iter().map(|m| m.id.clone()).collect(),\n recommendation: format!(\"建议合并这 {} 个重复记忆\", duplicate_count),\n };\n issues.push(issue);\n processed_memories.insert(memory_i.id.clone());\n }\n }\n\n tracing::info!(\"重复检测完成,发现 {} 个重复问题\", issues.len());\n Ok(issues)\n }\n\n /// 检测质量问题\n async fn detect_quality_issues(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测质量问题\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n for memory in memories {\n // 检查记忆是否已归档\n let is_archived = memory\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived {\n debug!(\"跳过已归档的记忆: {}\", memory.id);\n continue;\n }\n\n let quality_score = self.evaluate_memory_quality(&memory).await?;\n\n if quality_score < self.config.quality_threshold {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::LowQuality,\n severity: if quality_score < self.config.quality_threshold / 2.0 {\n IssueSeverity::High\n } else {\n IssueSeverity::Low\n },\n description: format!(\n \"记忆质量评分过低: {:.2} (阈值: {:.2})\",\n quality_score, self.config.quality_threshold\n ),\n affected_memories: vec![memory.id],\n recommendation: \"建议更新或删除低质量记忆\".to_string(),\n };\n issues.push(issue);\n }\n }\n\n tracing::info!(\"质量检测完成,发现 {} 个质量问题\", issues.len());\n Ok(issues)\n }\n\n /// 检测过时问题\n async fn detect_outdated_issues(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测过时问题\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n let _cutoff_date = Utc::now() - chrono::Duration::days(self.config.time_decay_days as i64);\n\n for memory in memories {\n // 检查记忆是否已归档\n let is_archived = memory\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived {\n debug!(\"跳过已归档的记忆: {}\", memory.id);\n continue;\n }\n\n let days_since_update = (Utc::now() - memory.updated_at).num_days();\n let is_outdated = days_since_update as u32 > self.config.time_decay_days;\n\n if is_outdated {\n let severity = if days_since_update as u32 > self.config.time_decay_days * 2 {\n IssueSeverity::High\n } else if days_since_update as u32\n > (self.config.time_decay_days as f32 * 1.5) as u32\n {\n IssueSeverity::Medium\n } else {\n IssueSeverity::Low\n };\n\n let recommendation = if severity == IssueSeverity::High {\n \"建议删除过时记忆\".to_string()\n } else {\n \"建议归档过时记忆\".to_string()\n };\n\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::Outdated,\n severity,\n description: format!(\n \"记忆已 {} 天未更新,超过阈值 {} 天\",\n days_since_update, self.config.time_decay_days\n ),\n affected_memories: vec![memory.id],\n recommendation,\n };\n issues.push(issue);\n }\n }\n\n tracing::info!(\"过时检测完成,发现 {} 个过时问题\", issues.len());\n Ok(issues)\n }\n\n /// 检测分类问题\n async fn detect_classification_issues(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测分类问题\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n for memory in memories {\n // 检查记忆是否已归档\n let is_archived = memory\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived {\n debug!(\"跳过已归档的记忆: {}\", memory.id);\n continue;\n }\n\n let classification_issues = self.check_classification_quality(&memory).await?;\n\n for issue_desc in classification_issues {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::PoorClassification,\n severity: IssueSeverity::Low,\n description: format!(\"分类问题: {}\", issue_desc),\n affected_memories: vec![memory.id.clone()],\n recommendation: \"建议重新分类记忆\".to_string(),\n };\n issues.push(issue);\n }\n }\n\n tracing::info!(\"分类检测完成,发现 {} 个分类问题\", issues.len());\n Ok(issues)\n }\n\n /// 检测空间效率问题\n async fn detect_space_inefficiency(\n &self,\n filters: &crate::types::Filters,\n ) -> Result> {\n tracing::info!(\"检测空间效率问题\");\n\n let mut issues = Vec::new();\n\n // 获取所有记忆\n let memories = self.memory_manager.list(filters, None).await?;\n\n // 获取统计数据\n let stats = self.memory_manager.get_stats(filters).await?;\n\n // 1. 检查单个记忆的大小问题\n for memory in &memories {\n // 检查记忆是否已归档\n let is_archived = memory\n .metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false);\n\n // 如果已归档,跳过检查\n if is_archived {\n debug!(\"跳过已归档的记忆: {}\", memory.id);\n continue;\n }\n\n let memory_size = memory.content.len() + memory.embedding.len() * 4; // 粗略估算\n\n // 如果记忆超过一定大小且重要性很低\n if memory_size > 10000 && memory.metadata.importance_score < 0.3 {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::SpaceInefficient,\n severity: IssueSeverity::Low,\n description: format!(\n \"大记忆占用空间过多且重要性低,大小: {} 字节\",\n memory_size\n ),\n affected_memories: vec![memory.id.clone()],\n recommendation: \"建议对大记忆进行摘要或归档\".to_string(),\n };\n issues.push(issue);\n }\n }\n\n // 2. 检查总存储情况\n let total_memories = stats.total_count;\n if total_memories > 10000 {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::SpaceInefficient,\n severity: IssueSeverity::Medium,\n description: format!(\"记忆数量过多: {},可能影响查询性能\", total_memories),\n affected_memories: Vec::new(), // 影响所有记忆\n recommendation: \"建议进行深度优化和清理\".to_string(),\n };\n issues.push(issue);\n }\n\n // 3. 检查低重要性记忆(排除已归档的记忆)\n let low_importance_memories: Vec<_> = memories\n .iter()\n .filter(|m| {\n m.metadata.importance_score < 0.2 &&\n // 排除已归档的记忆\n !m.metadata.custom.get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false)\n })\n .collect();\n\n let unarchived_count = total_memories\n - memories\n .iter()\n .filter(|m| {\n m.metadata\n .custom\n .get(\"archived\")\n .and_then(|v| v.as_bool())\n .unwrap_or(false)\n })\n .count();\n\n if low_importance_memories.len() > unarchived_count / 4 {\n let issue = OptimizationIssue {\n id: Uuid::new_v4().to_string(),\n kind: IssueKind::SpaceInefficient,\n severity: IssueSeverity::Medium,\n description: format!(\n \"低重要性记忆过多: {} / {} ({:.1}%)\",\n low_importance_memories.len(),\n unarchived_count,\n low_importance_memories.len() as f64 / unarchived_count as f64 * 100.0\n ),\n affected_memories: low_importance_memories\n .iter()\n .map(|m| m.id.clone())\n .collect(),\n recommendation: \"建议归档或删除低重要性记忆\".to_string(),\n };\n issues.push(issue);\n }\n\n tracing::info!(\"空间效率检测完成,发现 {} 个空间问题\", issues.len());\n Ok(issues)\n }\n\n /// 计算记忆的语义相似度(使用已存储的embedding)\n fn calculate_semantic_similarity_from_embeddings(\n &self, \n embedding1: &[f32], \n embedding2: &[f32],\n content1_preview: &str,\n content2_preview: &str,\n ) -> f32 {\n // 直接计算余弦相似度,无需重新生成embedding\n let similarity = self.cosine_similarity(embedding1, embedding2);\n\n tracing::debug!(\n \"语义相似度计算: {} vs {} = {:.3}\",\n content1_preview.chars().take(50).collect::(),\n content2_preview.chars().take(50).collect::(),\n similarity\n );\n\n similarity\n }\n\n /// 计算余弦相似度\n fn cosine_similarity(&self, vec1: &[f32], vec2: &[f32]) -> f32 {\n if vec1.len() != vec2.len() || vec1.is_empty() {\n return 0.0;\n }\n\n let mut dot_product = 0.0;\n let mut norm1 = 0.0;\n let mut norm2 = 0.0;\n\n for i in 0..vec1.len() {\n dot_product += vec1[i] * vec2[i];\n norm1 += vec1[i] * vec1[i];\n norm2 += vec2[i] * vec2[i];\n }\n\n if norm1 == 0.0 || norm2 == 0.0 {\n return 0.0;\n }\n\n dot_product / (norm1.sqrt() * norm2.sqrt())\n }\n\n /// 评估记忆质量\n async fn evaluate_memory_quality(&self, memory: &crate::types::Memory) -> Result {\n let mut quality_score = 0.0;\n let max_score = 1.0;\n\n // 1. 内容长度评分 (30%)\n let content_length_score = if memory.content.len() < 10 {\n 0.1\n } else if memory.content.len() < 50 {\n 0.5\n } else if memory.content.len() < 200 {\n 0.8\n } else {\n 1.0\n };\n quality_score += content_length_score * 0.3;\n\n // 2. 结构化程度评分 (20%)\n let has_sentences = memory.content.contains('.')\n || memory.content.contains('!')\n || memory.content.contains('?');\n let has_paragraphs = memory.content.contains('\\n');\n let structural_score = if has_sentences && has_paragraphs {\n 1.0\n } else if has_sentences || has_paragraphs {\n 0.7\n } else {\n 0.3\n };\n quality_score += structural_score * 0.2;\n\n // 3. 重要性评分 (20%)\n quality_score += memory.metadata.importance_score * 0.2;\n\n // 4. 元数据完整性 (15%)\n let metadata_score =\n if !memory.metadata.entities.is_empty() && !memory.metadata.topics.is_empty() {\n 1.0\n } else if !memory.metadata.entities.is_empty() || !memory.metadata.topics.is_empty() {\n 0.6\n } else {\n 0.2\n };\n quality_score += metadata_score * 0.15;\n\n // 5. 更新频率评分 (15%)\n let days_since_update = (chrono::Utc::now() - memory.updated_at).num_days();\n let update_score = if days_since_update < 7 {\n 1.0\n } else if days_since_update < 30 {\n 0.8\n } else if days_since_update < 90 {\n 0.5\n } else {\n 0.2\n };\n quality_score += update_score * 0.15;\n\n Ok(quality_score.min(max_score))\n }\n\n /// 检查分类质量\n async fn check_classification_quality(\n &self,\n memory: &crate::types::Memory,\n ) -> Result> {\n let mut issues = Vec::new();\n\n // 只有当内容非常短且为默认类型时才检查类型是否合适\n if memory.metadata.memory_type == crate::types::MemoryType::Conversational\n && memory.content.len() < 20\n {\n tracing::debug!(\"记忆 {} 太短且为默认类型,建议重新分类\", memory.id);\n }\n\n // 2. 检查实体提取 - 只有内容很长时才检查\n if memory.metadata.entities.is_empty() && memory.content.len() > 200 {\n issues.push(\"缺少实体信息\".to_string());\n }\n\n // 3. 检查主题提取 - 只有内容很长时才检查\n if memory.metadata.topics.is_empty() && memory.content.len() > 100 {\n issues.push(\"缺少主题信息\".to_string());\n }\n\n // 4. 检查记忆类型与内容是否匹配 - 更宽松的逻辑\n let detected_type = self.detect_memory_type_from_content(&memory.content).await;\n\n // 如果检测到的类型与当前类型不同,且内容足够长,才认为是问题\n if detected_type != memory.metadata.memory_type && memory.content.len() > 50 {\n issues.push(format!(\n \"记忆类型与内容可能不匹配: 当前 {:?}, 检测到 {:?}\",\n memory.metadata.memory_type, detected_type\n ));\n }\n\n Ok(issues)\n }\n\n /// 使用LLM从内容检测记忆类型\n async fn detect_memory_type_from_content(&self, content: &str) -> crate::types::MemoryType {\n let llm_client = self.memory_manager.llm_client();\n\n // 检查内容是否为空或过短\n if content.trim().is_empty() {\n tracing::warn!(\"记忆内容为空,默认分类为Conversational\");\n return crate::types::MemoryType::Conversational;\n }\n\n if content.trim().len() < 5 {\n tracing::warn!(\"记忆内容过短: '{}',默认分类为Conversational\", content);\n return crate::types::MemoryType::Conversational;\n }\n\n // 记录调试信息\n tracing::debug!(\n \"开始对记忆内容进行LLM分类: '{}...'\",\n content.chars().take(50).collect::()\n );\n\n // 创建分类提示\n let prompt = format!(\n r#\"Classify the following memory content into one of these categories:\n\n1. Conversational - Dialogue, conversations, or interactive exchanges\n2. Procedural - Instructions, how-to information, or step-by-step processes\n3. Factual - Objective facts, data, or verifiable information\n4. Semantic - Concepts, meanings, definitions, or general knowledge\n5. Episodic - Specific events, experiences, or temporal information\n6. Personal - Personal preferences, characteristics, or individual-specific information\n\nContent: \"{}\"\n\nRespond with only the category name (e.g., \"Conversational\", \"Procedural\", etc.):\"#,\n content\n );\n\n // 使用LLM分类器进行分类\n match llm_client.classify_memory(&prompt).await {\n Ok(classification) => {\n let memory_type = crate::types::MemoryType::parse(&classification.memory_type);\n\n tracing::info!(\n \"LLM分类成功: '{}' -> {:?} (置信度: {})\",\n content.chars().take(30).collect::(),\n memory_type,\n classification.confidence\n );\n\n memory_type\n }\n Err(e) => {\n tracing::error!(\n \"LLM分类失败: '{}' -> 错误: {}, 使用默认分类Conversational\",\n content.chars().take(30).collect::(),\n e\n );\n crate::types::MemoryType::Conversational // 失败时的回退\n }\n }\n }\n\n /// 限制每个类型的问题数量\n fn limit_issues_per_type(&self, issues: Vec) -> Vec {\n let mut issues_by_type: std::collections::HashMap> =\n std::collections::HashMap::new();\n\n for issue in &issues {\n issues_by_type\n .entry(issue.kind.clone())\n .or_insert_with(Vec::new)\n .push(issue.clone());\n }\n\n let mut limited_issues = Vec::new();\n\n for (kind, mut kind_issues) in issues_by_type {\n if kind_issues.len() > self.config.max_issues_per_type {\n kind_issues.truncate(self.config.max_issues_per_type);\n tracing::warn!(\n \"{:?} 类型的问题数量超过限制,截取到 {} 个\",\n kind,\n self.config.max_issues_per_type\n );\n }\n limited_issues.extend(kind_issues);\n }\n\n limited_issues\n }\n}\n\nimpl Default for OptimizationDetector {\n fn default() -> Self {\n panic!(\"OptimizationDetector requires MemoryManager. Use with_memory_manager() instead.\");\n }\n}\n" + "name": "optimizer.rs", + "source_summary": "use async_trait::async_trait;\nuse chrono::Utc;\nuse std::sync::Arc;\nuse uuid::Uuid;\n\nuse crate::{\n error::Result,\n memory::MemoryManager,\n types::{\n OptimizationConfig, OptimizationRequest, OptimizationResult, OptimizationStrategy,\n OptimizationStatus, OptimizationStatusType,\n },\n};\n\nuse super::{\n optimization_analyzer::OptimizationAnalyzer,\n optimization_detector::OptimizationDetector,\n execution_engine::ExecutionEngine,\n result_reporter::ResultReporter,\n};\n\n/// 主动内存优化器 - 核心协调组件\n#[async_trait]\npub trait MemoryOptimizer: Send + Sync {\n /// 执行优化操作\n async fn optimize(&self, request: &OptimizationRequest) -> Result;\n \n /// 创建优化计划(预览模式)\n async fn create_optimization_plan(&self, strategy: OptimizationStrategy) -> Result;\n \n /// 获取优化状态\n async fn get_optimization_status(&self) -> Result>;\n \n /// 取消正在进行的优化\n async fn cancel_optimization(&self, optimization_id: &str) -> Result<()>;\n}\n\n/// MemoryOptimizer 实现\npub struct DefaultMemoryOptimizer {\n #[allow(dead_code)]\n memory_manager: Arc,\n #[allow(dead_code)]\n config: OptimizationConfig,\n detector: Arc,\n analyzer: Arc,\n executor: Arc,\n reporter: Arc,\n running_optimizations: tokio::sync::RwLock>,\n}\n\nimpl DefaultMemoryOptimizer {\n pub fn new(\n memory_manager: Arc,\n config: OptimizationConfig,\n ) -> Self {\n let memory_manager_detector = memory_manager.clone();\n let memory_manager_analyzer = memory_manager.clone();\n let memory_manager_executor = memory_manager.clone();\n \n Self {\n memory_manager,\n config,\n detector: Arc::new(OptimizationDetector::with_memory_manager(memory_manager_detector)),\n analyzer: Arc::new(OptimizationAnalyzer::with_memory_manager(memory_manager_analyzer)),\n executor: Arc::new(ExecutionEngine::with_memory_manager(memory_manager_executor)),\n reporter: Arc::new(ResultReporter::new()),\n running_optimizations: tokio::sync::RwLock::new(std::collections::HashMap::new()),\n }\n }\n}\n\n#[async_trait]\nimpl MemoryOptimizer for DefaultMemoryOptimizer {\n async fn optimize(&self, request: &OptimizationRequest) -> Result {\n let optimization_id = request.optimization_id.clone()\n .unwrap_or_else(|| Uuid::new_v4().to_string());\n \n // 初始化优化状态\n let mut status = OptimizationStatus {\n optimization_id: optimization_id.clone(),\n status: OptimizationStatusType::Running,\n progress: 0,\n current_phase: \"初始化\".to_string(),\n started_at: Some(Utc::now()),\n estimated_completion: None,\n };\n \n // 记录正在运行的优化\n {\n let mut running = self.running_optimizations.write().await;\n running.insert(optimization_id.clone(), status.clone());\n }\n \n let start_time = Utc::now();\n \n tracing::info!(optimization_id = optimization_id, \"开始执行内存优化\");\n \n // 1. 检测问题 (20%)\n {\n status.progress = 20;\n status.current_phase = \"检测问题\".to_string();\n self.update_optimization_status(&optimization_id, &status).await;\n tracing::info!(\"开始检测内存优化问题\");\n }\n \n let issues = self.detector.detect_issues(&request.filters).await?;\n \n // 2. 分析制定计划 (40%)\n {\n status.progress = 40;\n status.current_phase = \"制定优化计划\".to_string();\n self.update_optimization_status(&optimization_id, &status).await;\n tracing::info!(\"制定优化计划\");\n }\n \n let plan = self.analyzer.create_optimization_plan(&issues, &request.strategy, &request.filters).await?;\n \n // 3. 执行优化 (80%)\n {\n status.progress = 80;\n status.current_phase = \"执行优化\".to_string();\n self.update_optimization_status(&optimization_id, &status).await;\n tracing::info!(\"执行优化计划\");\n }\n \n let result = if request.dry_run {\n // 干运行模式 - 不实际执行优化\n self.create_dry_run_result(&optimization_id, request, start_time, plan)\n } else {\n self.executor.execute_plan(&optimization_id, plan).await?\n };\n \n // 4. 报告结果 (100%)\n {\n status.progress = 100;\n status.current_phase = \"完成\".to_string();\n status.status = OptimizationStatusType::Completed;\n self.update_optimization_status(&optimization_id, &status).await;\n \n self.reporter.report_optimization_result(&result).await?;\n }\n \n // 从运行中优化列表中移除\n {\n let mut running = self.running_optimizations.write().await;\n running.remove(&optimization_id);\n }\n \n tracing::info!(optimization_id = optimization_id, \"优化完成: {} 项操作\", result.actions_performed.len());\n Ok(result)\n }\n \n async fn create_optimization_plan(&self, strategy: OptimizationStrategy) -> Result {\n let issues = self.detector.detect_issues(&Default::default()).await?;\n self.analyzer.create_optimization_plan(&issues, &strategy, &Default::default()).await\n }\n \n async fn get_optimization_status(&self) -> Result> {\n let running = self.running_optimizations.read().await;\n let statuses = running.values().cloned().collect::>();\n \n // 这里可以从历史记录中读取已完成的优化状态\n // 暂时只返回正在运行的优化状态\n \n Ok(statuses)\n }\n \n async fn cancel_optimization(&self, optimization_id: &str) -> Result<()> {\n let mut running = self.running_optimizations.write().await;\n \n if let Some(status) = running.get_mut(optimization_id) {\n status.status = OptimizationStatusType::Cancelled;\n }\n \n // 这里应该发送取消信号给执行引擎\n // 暂时只是更新状态\n \n tracing::info!(\"优化任务已取消: {}\", optimization_id);\n Ok(())\n }\n}\n\nimpl DefaultMemoryOptimizer {\n /// 创建干运行结果\n fn create_dry_run_result(\n &self,\n optimization_id: &str,\n request: &OptimizationRequest,\n start_time: chrono::DateTime,\n plan: super::optimization_plan::OptimizationPlan,\n ) -> OptimizationResult {\n let end_time = Utc::now();\n \n OptimizationResult {\n optimization_id: optimization_id.to_string(),\n strategy: request.strategy.clone(),\n start_time,\n end_time,\n issues_found: plan.issues,\n actions_performed: plan.actions,\n metrics: None,\n success: true,\n error_message: None,\n }\n }\n \n /// 更新优化状态\n async fn update_optimization_status(\n &self,\n optimization_id: &str,\n status: &OptimizationStatus,\n ) {\n let mut running = self.running_optimizations.write().await;\n running.insert(optimization_id.to_string(), status.clone());\n }\n}\n\nimpl DefaultMemoryOptimizer {\n /// 创建新的MemoryOptimizer实例\n pub async fn create(\n memory_manager: Arc,\n config: OptimizationConfig,\n ) -> Result> {\n Ok(Box::new(Self::new(memory_manager, config)))\n }\n}" }, "complexity_metrics": { - "cyclomatic_complexity": 54.0, - "lines_of_code": 741, - "number_of_classes": 2, - "number_of_functions": 17 + "cyclomatic_complexity": 4.0, + "lines_of_code": 226, + "number_of_classes": 1, + "number_of_functions": 8 }, "dependencies": [ { "dependency_type": "external", "is_external": true, "line_number": 1, - "name": "chrono", + "name": "async_trait", "path": null, "version": null }, { - "dependency_type": "standard_library", - "is_external": false, + "dependency_type": "external", + "is_external": true, "line_number": 2, - "name": "std::sync::Arc", + "name": "chrono", "path": null, "version": null }, { - "dependency_type": "external", - "is_external": true, + "dependency_type": "standard", + "is_external": false, "line_number": 3, - "name": "tracing", + "name": "std", "path": null, "version": null }, @@ -13830,7 +11153,7 @@ Code analysis results from preprocessing phase, including definitions of functio "dependency_type": "internal", "is_external": false, "line_number": 9, - "name": "crate::types::IssueKind", + "name": "crate::types::OptimizationConfig", "path": "cortex-mem-core/src/types/mod.rs", "version": null }, @@ -13838,7 +11161,7 @@ Code analysis results from preprocessing phase, including definitions of functio "dependency_type": "internal", "is_external": false, "line_number": 9, - "name": "crate::types::IssueSeverity", + "name": "crate::types::OptimizationRequest", "path": "cortex-mem-core/src/types/mod.rs", "version": null }, @@ -13846,7 +11169,7 @@ Code analysis results from preprocessing phase, including definitions of functio "dependency_type": "internal", "is_external": false, "line_number": 9, - "name": "crate::types::OptimizationFilters", + "name": "crate::types::OptimizationResult", "path": "cortex-mem-core/src/types/mod.rs", "version": null }, @@ -13854,3354 +11177,3505 @@ Code analysis results from preprocessing phase, including definitions of functio "dependency_type": "internal", "is_external": false, "line_number": 9, - "name": "crate::types::OptimizationIssue", + "name": "crate::types::OptimizationStrategy", "path": "cortex-mem-core/src/types/mod.rs", "version": null - } - ], - "detailed_description": "The OptimizationDetector is a comprehensive memory optimization analysis component that systematically identifies various types of memory issues in a memory management system. It implements a multi-faceted approach to memory optimization by detecting five main categories of issues: duplicate memories based on semantic similarity using pre-computed embeddings, low-quality memories based on content length, structure, importance score, metadata completeness, and update frequency, outdated memories based on last update time exceeding configurable thresholds, poor classification issues where memory type doesn't match content characteristics or metadata is incomplete, and space inefficiency issues including oversized memories with low importance, excessive total memory count, and high proportions of low-importance memories. The detector uses a configurable threshold-based approach with parameters for duplicate detection, quality assessment, time decay, and issue limits. It integrates with the MemoryManager to retrieve memories and statistics, and leverages an LLM client for content-based memory type classification. The component follows a builder pattern for construction with required MemoryManager dependency and optional configuration. All detection methods filter out archived memories and can be customized through OptimizationFilters. The results are consolidated and limited by issue type to prevent overwhelming outputs.", - "interfaces": [ + }, { - "description": "Default constructor that panics to enforce proper initialization with MemoryManager", - "interface_type": "constructor", - "name": "OptimizationDetector::new", - "parameters": [], - "return_type": "Self", - "visibility": "public" + "dependency_type": "internal", + "is_external": false, + "line_number": 9, + "name": "crate::types::OptimizationStatus", + "path": "cortex-mem-core/src/types/mod.rs", + "version": null }, { - "description": "Constructor that initializes detector with required memory manager dependency", - "interface_type": "constructor", - "name": "OptimizationDetector::with_memory_manager", - "parameters": [ - { - "description": "Shared reference to memory manager for data access", - "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" - } - ], - "return_type": "Self", - "visibility": "public" + "dependency_type": "internal", + "is_external": false, + "line_number": 9, + "name": "crate::types::OptimizationStatusType", + "path": "cortex-mem-core/src/types/mod.rs", + "version": null }, { - "description": "Constructor that initializes detector with custom configuration and memory manager", - "interface_type": "constructor", - "name": "OptimizationDetector::with_config", - "parameters": [ - { - "description": "Custom configuration for detection thresholds", - "is_optional": false, - "name": "config", - "param_type": "OptimizationDetectorConfig" - }, - { - "description": "Shared reference to memory manager for data access", - "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" - } - ], - "return_type": "Self", - "visibility": "public" + "dependency_type": "internal", + "is_external": false, + "line_number": 12, + "name": "super::optimization_analyzer::OptimizationAnalyzer", + "path": "cortex-mem-core/src/memory/optimization_analyzer.rs", + "version": null }, { - "description": "Main entry point that orchestrates all detection algorithms and returns consolidated optimization issues", - "interface_type": "method", - "name": "OptimizationDetector::detect_issues", - "parameters": [ - { - "description": "Filters to constrain the scope of detection", - "is_optional": false, - "name": "filters", - "param_type": "&OptimizationFilters" - } - ], - "return_type": "Result>", + "dependency_type": "internal", + "is_external": false, + "line_number": 13, + "name": "super::optimization_detector::OptimizationDetector", + "path": "cortex-mem-core/src/memory/optimization_detector.rs", + "version": null + }, + { + "dependency_type": "internal", + "is_external": false, + "line_number": 14, + "name": "super::execution_engine::ExecutionEngine", + "path": "cortex-mem-core/src/memory/execution_engine.rs", + "version": null + }, + { + "dependency_type": "internal", + "is_external": false, + "line_number": 15, + "name": "super::result_reporter::ResultReporter", + "path": "cortex-mem-core/src/memory/result_reporter.rs", + "version": null + }, + { + "dependency_type": "external", + "is_external": true, + "line_number": 174, + "name": "tracing", + "path": null, + "version": null + } + ], + "detailed_description": "该组件是内存优化系统的核心协调器,实现了MemoryOptimizer异步trait。它通过组合Detector、Analyzer、ExecutionEngine和ResultReporter等子组件,提供了一个完整的内存优化流程。主要功能包括:执行端到端的内存优化操作(optimize),创建优化计划(create_optimization_plan),获取当前优化状态(get_optimization_status),以及取消正在进行的优化任务(cancel_optimization)。组件采用干运行(dry run)模式支持安全预演,并通过RwLock管理并发的优化任务状态。整个优化过程被划分为检测、分析、执行和报告四个阶段,每个阶段都会更新进度状态并记录日志。", + "interfaces": [ + { + "description": "内存优化器的主要接口,定义了优化操作的核心方法。", + "interface_type": "trait", + "name": "MemoryOptimizer", + "parameters": [], + "return_type": null, "visibility": "public" }, { - "description": "Detects duplicate memories by comparing semantic similarity of embeddings", + "description": "执行完整的内存优化流程,包括检测、分析、执行和报告。", "interface_type": "method", - "name": "OptimizationDetector::detect_duplicates", + "name": "optimize", "parameters": [ { - "description": "Converted filters for memory retrieval", + "description": "优化请求参数", "is_optional": false, - "name": "filters", - "param_type": "&crate::types::Filters" + "name": "request", + "param_type": "OptimizationRequest" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "Result", + "visibility": "public" }, { - "description": "Identifies low-quality memories based on multi-dimensional quality scoring", + "description": "根据指定策略创建优化计划(预览模式)。", "interface_type": "method", - "name": "OptimizationDetector::detect_quality_issues", + "name": "create_optimization_plan", "parameters": [ { - "description": "Converted filters for memory retrieval", + "description": "优化策略", "is_optional": false, - "name": "filters", - "param_type": "&crate::types::Filters" + "name": "strategy", + "param_type": "OptimizationStrategy" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "Result", + "visibility": "public" }, { - "description": "Detects memories that haven't been updated within configured time thresholds", + "description": "获取当前所有优化任务的执行状态。", "interface_type": "method", - "name": "OptimizationDetector::detect_outdated_issues", + "name": "get_optimization_status", + "parameters": [], + "return_type": "Result>", + "visibility": "public" + }, + { + "description": "取消指定ID的正在进行的优化任务。", + "interface_type": "method", + "name": "cancel_optimization", "parameters": [ { - "description": "Converted filters for memory retrieval", + "description": "要取消的优化任务ID", "is_optional": false, - "name": "filters", - "param_type": "&crate::types::Filters" + "name": "optimization_id", + "param_type": "str" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "Result<()>", + "visibility": "public" }, { - "description": "Identifies issues with memory classification including type-content mismatches and missing metadata", + "description": "创建干运行模式下的模拟优化结果。", "interface_type": "method", - "name": "OptimizationDetector::detect_classification_issues", + "name": "create_dry_run_result", "parameters": [ { - "description": "Converted filters for memory retrieval", + "description": "优化任务ID", "is_optional": false, - "name": "filters", - "param_type": "&crate::types::Filters" + "name": "optimization_id", + "param_type": "str" + }, + { + "description": "优化请求", + "is_optional": false, + "name": "request", + "param_type": "OptimizationRequest" + }, + { + "description": "开始时间", + "is_optional": false, + "name": "start_time", + "param_type": "DateTime" + }, + { + "description": "优化计划", + "is_optional": false, + "name": "plan", + "param_type": "OptimizationPlan" } ], - "return_type": "Result>", + "return_type": "OptimizationResult", "visibility": "private" }, { - "description": "Detects various space efficiency problems including oversized memories and excessive memory counts", + "description": "更新指定优化任务的执行状态。", "interface_type": "method", - "name": "OptimizationDetector::detect_space_inefficiency", + "name": "update_optimization_status", "parameters": [ { - "description": "Converted filters for memory retrieval", + "description": "优化任务ID", "is_optional": false, - "name": "filters", - "param_type": "&crate::types::Filters" + "name": "optimization_id", + "param_type": "str" + }, + { + "description": "新的状态信息", + "is_optional": false, + "name": "status", + "param_type": "OptimizationStatus" } ], - "return_type": "Result>", + "return_type": null, "visibility": "private" }, { - "description": "Calculates a composite quality score for a memory based on multiple factors", + "description": "创建新的MemoryOptimizer实例。", "interface_type": "method", - "name": "OptimizationDetector::evaluate_memory_quality", + "name": "create", "parameters": [ { - "description": "Memory to evaluate for quality", + "description": "内存管理器实例", "is_optional": false, - "name": "memory", - "param_type": "&crate::types::Memory" + "name": "memory_manager", + "param_type": "Arc" + }, + { + "description": "优化配置", + "is_optional": false, + "name": "config", + "param_type": "OptimizationConfig" } ], - "return_type": "Result", - "visibility": "private" + "return_type": "Result>", + "visibility": "public" }, { - "description": "Evaluates the quality of memory classification and metadata completeness", - "interface_type": "method", - "name": "OptimizationDetector::check_classification_quality", + "description": "构造一个新的DefaultMemoryOptimizer实例。", + "interface_type": "constructor", + "name": "new", "parameters": [ { - "description": "Memory to evaluate for classification quality", + "description": "内存管理器实例", "is_optional": false, - "name": "memory", - "param_type": "&crate::types::Memory" + "name": "memory_manager", + "param_type": "Arc" + }, + { + "description": "优化配置", + "is_optional": false, + "name": "config", + "param_type": "OptimizationConfig" } ], - "return_type": "Result>", - "visibility": "private" + "return_type": "DefaultMemoryOptimizer", + "visibility": "public" } ], "responsibilities": [ - "Detect duplicate memories by calculating semantic similarity from pre-computed embeddings using cosine similarity", - "Identify low-quality memories through multi-dimensional quality scoring based on content length, structure, importance, metadata completeness, and recency", - "Detect outdated memories by comparing last update time against configurable time decay thresholds", - "Identify memory classification issues including inappropriate memory types, missing entities/topics, and type-content mismatches", - "Detect space inefficiency problems such as oversized low-importance memories, excessive memory counts, and high proportions of low-importance unarchived memories" + "协调内存优化的全流程,包括检测、分析、执行和报告", + "管理并跟踪多个并发优化任务的执行状态", + "提供干运行模式支持,允许预演优化计划而不实际执行", + "作为统一入口处理内存优化相关的所有操作请求", + "维护优化任务的生命周期,包括启动、监控、取消和清理" ] }, { "code_dossier": { "code_purpose": "specificfeature", - "description": "核心协调组件,负责主动内存优化操作的全流程管理,包括检测、分析、执行和报告。", - "file_path": "cortex-mem-core/src/memory/optimizer.rs", + "description": "优化分析器 - 负责分析内存中的问题并制定相应的优化策略", + "file_path": "cortex-mem-core/src/memory/optimization_analyzer.rs", "functions": [ - "optimize", + "new", + "with_memory_manager", + "with_config", "create_optimization_plan", - "get_optimization_status", - "cancel_optimization", - "create_dry_run_result", - "update_optimization_status", - "create" + "generate_optimization_actions", + "filter_issues_by_strategy", + "analyze_issue_and_generate_actions", + "filter_actions_conservatively", + "analyze_optimization_impact", + "calculate_risk_level" ], "importance_score": 0.8, "interfaces": [ - "MemoryOptimizer" + "OptimizationAnalyzer", + "OptimizationAnalyzerConfig", + "OptimizationImpact", + "RiskLevel" ], - "name": "optimizer.rs", - "source_summary": "use async_trait::async_trait;\nuse chrono::Utc;\nuse std::sync::Arc;\nuse uuid::Uuid;\n\nuse crate::{\n error::Result,\n memory::MemoryManager,\n types::{\n OptimizationConfig, OptimizationRequest, OptimizationResult, OptimizationStrategy,\n OptimizationStatus, OptimizationStatusType,\n },\n};\n\nuse super::{\n optimization_analyzer::OptimizationAnalyzer,\n optimization_detector::OptimizationDetector,\n execution_engine::ExecutionEngine,\n result_reporter::ResultReporter,\n};\n\n/// 主动内存优化器 - 核心协调组件\n#[async_trait]\npub trait MemoryOptimizer: Send + Sync {\n /// 执行优化操作\n async fn optimize(&self, request: &OptimizationRequest) -> Result;\n \n /// 创建优化计划(预览模式)\n async fn create_optimization_plan(&self, strategy: OptimizationStrategy) -> Result;\n \n /// 获取优化状态\n async fn get_optimization_status(&self) -> Result>;\n \n /// 取消正在进行的优化\n async fn cancel_optimization(&self, optimization_id: &str) -> Result<()>;\n}\n\n/// MemoryOptimizer 实现\npub struct DefaultMemoryOptimizer {\n #[allow(dead_code)]\n memory_manager: Arc,\n #[allow(dead_code)]\n config: OptimizationConfig,\n detector: Arc,\n analyzer: Arc,\n executor: Arc,\n reporter: Arc,\n running_optimizations: tokio::sync::RwLock>,\n}\n\nimpl DefaultMemoryOptimizer {\n pub fn new(\n memory_manager: Arc,\n config: OptimizationConfig,\n ) -> Self {\n let memory_manager_detector = memory_manager.clone();\n let memory_manager_analyzer = memory_manager.clone();\n let memory_manager_executor = memory_manager.clone();\n \n Self {\n memory_manager,\n config,\n detector: Arc::new(OptimizationDetector::with_memory_manager(memory_manager_detector)),\n analyzer: Arc::new(OptimizationAnalyzer::with_memory_manager(memory_manager_analyzer)),\n executor: Arc::new(ExecutionEngine::with_memory_manager(memory_manager_executor)),\n reporter: Arc::new(ResultReporter::new()),\n running_optimizations: tokio::sync::RwLock::new(std::collections::HashMap::new()),\n }\n }\n}\n\n#[async_trait]\nimpl MemoryOptimizer for DefaultMemoryOptimizer {\n async fn optimize(&self, request: &OptimizationRequest) -> Result {\n let optimization_id = request.optimization_id.clone()\n .unwrap_or_else(|| Uuid::new_v4().to_string());\n \n // 初始化优化状态\n let mut status = OptimizationStatus {\n optimization_id: optimization_id.clone(),\n status: OptimizationStatusType::Running,\n progress: 0,\n current_phase: \"初始化\".to_string(),\n started_at: Some(Utc::now()),\n estimated_completion: None,\n };\n \n // 记录正在运行的优化\n {\n let mut running = self.running_optimizations.write().await;\n running.insert(optimization_id.clone(), status.clone());\n }\n \n let start_time = Utc::now();\n \n tracing::info!(optimization_id = optimization_id, \"开始执行内存优化\");\n \n // 1. 检测问题 (20%)\n {\n status.progress = 20;\n status.current_phase = \"检测问题\".to_string();\n self.update_optimization_status(&optimization_id, &status).await;\n tracing::info!(\"开始检测内存优化问题\");\n }\n \n let issues = self.detector.detect_issues(&request.filters).await?;\n \n // 2. 分析制定计划 (40%)\n {\n status.progress = 40;\n status.current_phase = \"制定优化计划\".to_string();\n self.update_optimization_status(&optimization_id, &status).await;\n tracing::info!(\"制定优化计划\");\n }\n \n let plan = self.analyzer.create_optimization_plan(&issues, &request.strategy, &request.filters).await?;\n \n // 3. 执行优化 (80%)\n {\n status.progress = 80;\n status.current_phase = \"执行优化\".to_string();\n self.update_optimization_status(&optimization_id, &status).await;\n tracing::info!(\"执行优化计划\");\n }\n \n let result = if request.dry_run {\n // 干运行模式 - 不实际执行优化\n self.create_dry_run_result(&optimization_id, request, start_time, plan)\n } else {\n self.executor.execute_plan(&optimization_id, plan).await?\n };\n \n // 4. 报告结果 (100%)\n {\n status.progress = 100;\n status.current_phase = \"完成\".to_string();\n status.status = OptimizationStatusType::Completed;\n self.update_optimization_status(&optimization_id, &status).await;\n \n self.reporter.report_optimization_result(&result).await?;\n }\n \n // 从运行中优化列表中移除\n {\n let mut running = self.running_optimizations.write().await;\n running.remove(&optimization_id);\n }\n \n tracing::info!(optimization_id = optimization_id, \"优化完成: {} 项操作\", result.actions_performed.len());\n Ok(result)\n }\n \n async fn create_optimization_plan(&self, strategy: OptimizationStrategy) -> Result {\n let issues = self.detector.detect_issues(&Default::default()).await?;\n self.analyzer.create_optimization_plan(&issues, &strategy, &Default::default()).await\n }\n \n async fn get_optimization_status(&self) -> Result> {\n let running = self.running_optimizations.read().await;\n let statuses = running.values().cloned().collect::>();\n \n // 这里可以从历史记录中读取已完成的优化状态\n // 暂时只返回正在运行的优化状态\n \n Ok(statuses)\n }\n \n async fn cancel_optimization(&self, optimization_id: &str) -> Result<()> {\n let mut running = self.running_optimizations.write().await;\n \n if let Some(status) = running.get_mut(optimization_id) {\n status.status = OptimizationStatusType::Cancelled;\n }\n \n // 这里应该发送取消信号给执行引擎\n // 暂时只是更新状态\n \n tracing::info!(\"优化任务已取消: {}\", optimization_id);\n Ok(())\n }\n}\n\nimpl DefaultMemoryOptimizer {\n /// 创建干运行结果\n fn create_dry_run_result(\n &self,\n optimization_id: &str,\n request: &OptimizationRequest,\n start_time: chrono::DateTime,\n plan: super::optimization_plan::OptimizationPlan,\n ) -> OptimizationResult {\n let end_time = Utc::now();\n \n OptimizationResult {\n optimization_id: optimization_id.to_string(),\n strategy: request.strategy.clone(),\n start_time,\n end_time,\n issues_found: plan.issues,\n actions_performed: plan.actions,\n metrics: None,\n success: true,\n error_message: None,\n }\n }\n \n /// 更新优化状态\n async fn update_optimization_status(\n &self,\n optimization_id: &str,\n status: &OptimizationStatus,\n ) {\n let mut running = self.running_optimizations.write().await;\n running.insert(optimization_id.to_string(), status.clone());\n }\n}\n\nimpl DefaultMemoryOptimizer {\n /// 创建新的MemoryOptimizer实例\n pub async fn create(\n memory_manager: Arc,\n config: OptimizationConfig,\n ) -> Result> {\n Ok(Box::new(Self::new(memory_manager, config)))\n }\n}" + "name": "optimization_analyzer.rs", + "source_summary": "use std::collections::HashMap;\nuse std::sync::Arc;\nuse uuid::Uuid;\n\nuse crate::{\n error::Result,\n types::{\n OptimizationAction, OptimizationFilters, OptimizationIssue, OptimizationStrategy,\n IssueKind, IssueSeverity,\n },\n memory::MemoryManager,\n};\n\nuse super::optimization_plan::{OptimizationPlan, ActionStatistics};\n\n/// 优化分析器 - 负责分析问题并制定优化策略\npub struct OptimizationAnalyzer {\n // 分析器配置\n config: OptimizationAnalyzerConfig,\n #[allow(dead_code)]\n memory_manager: Arc,\n}\n\n#[derive(Debug, Clone)]\npub struct OptimizationAnalyzerConfig {\n pub max_actions_per_plan: usize,\n pub conservative_mode: bool,\n}\n\nimpl Default for OptimizationAnalyzerConfig {\n fn default() -> Self {\n Self {\n max_actions_per_plan: 5000,\n conservative_mode: false,\n }\n }\n}\n\nimpl OptimizationAnalyzer {\n pub fn new() -> Self {\n panic!(\"OptimizationAnalyzer requires MemoryManager. Use with_memory_manager() instead.\");\n }\n \n pub fn with_memory_manager(memory_manager: Arc) -> Self {\n Self {\n config: OptimizationAnalyzerConfig::default(),\n memory_manager,\n }\n }\n \n pub fn with_config(config: OptimizationAnalyzerConfig, memory_manager: Arc) -> Self {\n Self {\n config,\n memory_manager,\n }\n }\n \n /// 根据问题制定优化计划\n pub async fn create_optimization_plan(\n &self,\n issues: &[OptimizationIssue],\n strategy: &OptimizationStrategy,\n filters: &OptimizationFilters,\n ) -> Result {\n let optimization_id = Uuid::new_v4().to_string();\n \n tracing::info!(optimization_id = optimization_id, \"制定优化计划, 策略: {:?}, 问题数量: {}\", strategy, issues.len());\n \n let actions = self.generate_optimization_actions(issues, strategy).await?;\n \n let plan = OptimizationPlan::new(\n optimization_id,\n strategy.clone(),\n issues.to_vec(),\n actions,\n filters.clone(),\n );\n \n tracing::info!(optimization_id = plan.optimization_id, \"计划制定完成: {} 个操作\", plan.actions.len());\n Ok(plan)\n }\n \n /// 生成优化操作\n async fn generate_optimization_actions(\n &self,\n issues: &[OptimizationIssue],\n strategy: &OptimizationStrategy,\n ) -> Result> {\n let mut actions = Vec::new();\n \n // 根据策略过滤相关问题\n let relevant_issues = self.filter_issues_by_strategy(issues, strategy);\n \n tracing::info!(\"策略 {:?} 相关的 {} 个问题\", strategy, relevant_issues.len());\n \n for issue in relevant_issues {\n let issue_actions = self.analyze_issue_and_generate_actions(&issue).await?;\n actions.extend(issue_actions);\n \n // 限制操作数量以防止计划过大\n if actions.len() >= self.config.max_actions_per_plan {\n tracing::warn!(\"达到最大操作数量限制: {}\", self.config.max_actions_per_plan);\n break;\n }\n }\n \n // 如果是保守模式,进一步过滤操作\n if self.config.conservative_mode {\n actions = self.filter_actions_conservatively(actions);\n }\n \n Ok(actions)\n }\n \n /// 根据策略过滤相关问题\n fn filter_issues_by_strategy<'a>(\n &'a self,\n issues: &'a [OptimizationIssue],\n strategy: &'a OptimizationStrategy,\n ) -> Vec<&'a OptimizationIssue> {\n match strategy {\n OptimizationStrategy::Full => issues.iter().collect(),\n OptimizationStrategy::Incremental => {\n // 只处理高严重程度的问题\n issues.iter()\n .filter(|issue| {\n matches!(issue.severity, IssueSeverity::High | IssueSeverity::Critical)\n })\n .collect()\n }\n OptimizationStrategy::Batch => {\n // 处理所有Medium及以上的问题\n issues.iter()\n .filter(|issue| {\n !matches!(issue.severity, IssueSeverity::Low)\n })\n .collect()\n }\n OptimizationStrategy::Deduplication => {\n issues.iter()\n .filter(|issue| matches!(issue.kind, IssueKind::Duplicate))\n .collect()\n }\n OptimizationStrategy::Relevance => {\n issues.iter()\n .filter(|issue| matches!(issue.kind, IssueKind::Outdated))\n .collect()\n }\n OptimizationStrategy::Quality => {\n issues.iter()\n .filter(|issue| matches!(issue.kind, IssueKind::LowQuality))\n .collect()\n }\n OptimizationStrategy::Space => {\n issues.iter()\n .filter(|issue| matches!(issue.kind, IssueKind::SpaceInefficient))\n .collect()\n }\n }\n }\n \n /// 分析单个问题并生成相应的操作\n async fn analyze_issue_and_generate_actions(\n &self,\n issue: &OptimizationIssue,\n ) -> Result> {\n let mut actions = Vec::new();\n \n match issue.kind {\n IssueKind::Duplicate => {\n if issue.affected_memories.len() > 1 {\n actions.push(OptimizationAction::Merge {\n memories: issue.affected_memories.clone(),\n });\n }\n }\n IssueKind::LowQuality => {\n // 为每个低质量记忆生成操作\n for memory_id in &issue.affected_memories {\n // 对于质量极低的记忆,建议删除\n // 对于中等质量的问题,建议更新重要性分数\n actions.push(OptimizationAction::Delete {\n memory_id: memory_id.clone(),\n });\n }\n }\n IssueKind::Outdated => {\n // 过时记忆可能需要删除或归档\n for memory_id in &issue.affected_memories {\n if issue.severity == IssueSeverity::Critical {\n actions.push(OptimizationAction::Delete {\n memory_id: memory_id.clone(),\n });\n } else {\n actions.push(OptimizationAction::Archive {\n memory_id: memory_id.clone(),\n });\n }\n }\n }\n IssueKind::PoorClassification => {\n // 重新分类记忆\n for memory_id in &issue.affected_memories {\n actions.push(OptimizationAction::Reclassify {\n memory_id: memory_id.clone(),\n });\n }\n }\n IssueKind::SpaceInefficient => {\n // 空间效率问题一般通过归档处理\n for memory_id in &issue.affected_memories {\n actions.push(OptimizationAction::Archive {\n memory_id: memory_id.clone(),\n });\n }\n }\n }\n \n Ok(actions)\n }\n \n /// 保守模式过滤操作\n fn filter_actions_conservatively(&self, actions: Vec) -> Vec {\n let mut filtered = Vec::new();\n \n for action in actions {\n match action {\n // 在保守模式下,避免删除操作\n OptimizationAction::Delete { .. } => {\n tracing::info!(\"保守模式: 跳过删除操作\");\n continue;\n }\n // 将删除操作转换为归档操作\n OptimizationAction::Archive { .. } => {\n // 保留归档操作\n filtered.push(action);\n }\n _ => {\n // 保留其他操作\n filtered.push(action);\n }\n }\n }\n \n filtered\n }\n \n /// 分析优化效果预测\n pub fn analyze_optimization_impact(\n &self,\n plan: &OptimizationPlan,\n ) -> Result {\n let stats = plan.action_statistics();\n let issue_stats = plan.issue_statistics();\n \n let mut predictions = HashMap::new();\n \n // 预测去重效果\n if stats.merge_count > 0 {\n predictions.insert(\"deduplication\".to_string(), format!(\"预计合并 {} 个重复记忆\", stats.merge_count));\n }\n \n // 预测空间节省\n if stats.delete_count > 0 {\n predictions.insert(\"space_saving\".to_string(), format!(\"预计删除 {} 个记忆\", stats.delete_count));\n }\n \n // 预测质量改善\n if stats.update_count > 0 {\n predictions.insert(\"quality_improvement\".to_string(), format!(\"预计更新 {} 个记忆\", stats.update_count));\n }\n \n // 预测性能提升\n let critical_issues = issue_stats.critical_or_high();\n if critical_issues > 0 {\n predictions.insert(\"performance_boost\".to_string(), format!(\"预计解决 {} 个严重问题\", critical_issues));\n }\n \n Ok(OptimizationImpact {\n estimated_duration_minutes: plan.estimated_duration_minutes,\n risk_level: self.calculate_risk_level(&stats),\n predictions,\n statistics: stats,\n })\n }\n \n /// 计算风险等级\n fn calculate_risk_level(&self, stats: &ActionStatistics) -> RiskLevel {\n let total_actions = stats.total();\n \n if total_actions == 0 {\n return RiskLevel::VeryLow;\n }\n \n let deletion_ratio = stats.delete_count as f64 / total_actions as f64;\n let merge_ratio = stats.merge_count as f64 / total_actions as f64;\n \n if deletion_ratio > 0.3 || merge_ratio > 0.5 {\n RiskLevel::High\n } else if deletion_ratio > 0.1 || merge_ratio > 0.3 {\n RiskLevel::Medium\n } else if deletion_ratio > 0.05 || merge_ratio > 0.1 {\n RiskLevel::Low\n } else {\n RiskLevel::VeryLow\n }\n }\n}\n\n/// 优化影响分析\n#[derive(Debug, Clone)]\npub struct OptimizationImpact {\n pub estimated_duration_minutes: u64,\n pub risk_level: RiskLevel,\n pub predictions: HashMap,\n pub statistics: ActionStatistics,\n}\n\n/// 风险等级\n#[derive(Debug, Clone, PartialEq)]\npub enum RiskLevel {\n VeryLow,\n Low,\n Medium,\n High,\n}\n\nimpl std::fmt::Display for RiskLevel {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n match self {\n RiskLevel::VeryLow => write!(f, \"极低\"),\n RiskLevel::Low => write!(f, \"低\"),\n RiskLevel::Medium => write!(f, \"中等\"),\n RiskLevel::High => write!(f, \"高\"),\n }\n }\n}\n\nimpl Default for OptimizationAnalyzer {\n fn default() -> Self {\n panic!(\"OptimizationAnalyzer requires MemoryManager. Use with_memory_manager() instead.\");\n }\n}" }, "complexity_metrics": { - "cyclomatic_complexity": 4.0, - "lines_of_code": 226, - "number_of_classes": 1, - "number_of_functions": 8 + "cyclomatic_complexity": 26.0, + "lines_of_code": 343, + "number_of_classes": 4, + "number_of_functions": 16 }, "dependencies": [ { - "dependency_type": "external", - "is_external": true, + "dependency_type": "std", + "is_external": false, "line_number": 1, - "name": "async_trait", - "path": null, - "version": null - }, - { - "dependency_type": "external", - "is_external": true, - "line_number": 2, - "name": "chrono", + "name": "std::collections::HashMap", "path": null, "version": null }, { - "dependency_type": "standard", + "dependency_type": "std", "is_external": false, - "line_number": 3, - "name": "std", + "line_number": 2, + "name": "std::sync::Arc", "path": null, "version": null }, { - "dependency_type": "external", + "dependency_type": "crate", "is_external": true, - "line_number": 4, - "name": "uuid", + "line_number": 3, + "name": "uuid::Uuid", "path": null, "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 7, + "line_number": 6, "name": "crate::error::Result", - "path": "cortex-mem-core/src/error.rs", - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": 8, - "name": "crate::memory::MemoryManager", - "path": "cortex-mem-core/src/memory/mod.rs", + "path": "../error", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 9, - "name": "crate::types::OptimizationConfig", - "path": "cortex-mem-core/src/types/mod.rs", + "line_number": 7, + "name": "crate::types::OptimizationAction", + "path": "../types", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 9, - "name": "crate::types::OptimizationRequest", - "path": "cortex-mem-core/src/types/mod.rs", + "line_number": 7, + "name": "crate::types::OptimizationFilters", + "path": "../types", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 9, - "name": "crate::types::OptimizationResult", - "path": "cortex-mem-core/src/types/mod.rs", + "line_number": 7, + "name": "crate::types::OptimizationIssue", + "path": "../types", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 9, + "line_number": 7, "name": "crate::types::OptimizationStrategy", - "path": "cortex-mem-core/src/types/mod.rs", - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": 9, - "name": "crate::types::OptimizationStatus", - "path": "cortex-mem-core/src/types/mod.rs", + "path": "../types", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 9, - "name": "crate::types::OptimizationStatusType", - "path": "cortex-mem-core/src/types/mod.rs", + "line_number": 7, + "name": "crate::types::IssueKind", + "path": "../types", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 12, - "name": "super::optimization_analyzer::OptimizationAnalyzer", - "path": "cortex-mem-core/src/memory/optimization_analyzer.rs", + "line_number": 7, + "name": "crate::types::IssueSeverity", + "path": "../types", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 13, - "name": "super::optimization_detector::OptimizationDetector", - "path": "cortex-mem-core/src/memory/optimization_detector.rs", + "line_number": 8, + "name": "crate::memory::MemoryManager", + "path": "./memory", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 14, - "name": "super::execution_engine::ExecutionEngine", - "path": "cortex-mem-core/src/memory/execution_engine.rs", + "line_number": 11, + "name": "super::optimization_plan::OptimizationPlan", + "path": "../optimization_plan", "version": null }, { - "dependency_type": "internal", + "dependency_type": "local", "is_external": false, - "line_number": 15, - "name": "super::result_reporter::ResultReporter", - "path": "cortex-mem-core/src/memory/result_reporter.rs", + "line_number": 11, + "name": "super::optimization_plan::ActionStatistics", + "path": "../optimization_plan", "version": null }, { - "dependency_type": "external", + "dependency_type": "crate", "is_external": true, - "line_number": 174, + "line_number": null, "name": "tracing", "path": null, "version": null } ], - "detailed_description": "该组件是内存优化系统的核心协调器,实现了MemoryOptimizer异步trait。它通过组合Detector、Analyzer、ExecutionEngine和ResultReporter等子组件,提供了一个完整的内存优化流程。主要功能包括:执行端到端的内存优化操作(optimize),创建优化计划(create_optimization_plan),获取当前优化状态(get_optimization_status),以及取消正在进行的优化任务(cancel_optimization)。组件采用干运行(dry run)模式支持安全预演,并通过RwLock管理并发的优化任务状态。整个优化过程被划分为检测、分析、执行和报告四个阶段,每个阶段都会更新进度状态并记录日志。", + "detailed_description": "该组件是内存优化系统的核心分析模块,负责根据检测到的内存问题和指定的优化策略生成具体的优化计划。它首先基于策略类型(如完全优化、增量优化、批处理等)过滤相关问题,然后针对每个问题类型(重复、低质量、过时等)生成相应的操作指令(合并、删除、归档等)。组件还提供保守模式以降低风险,并能预测优化操作的影响,包括空间节省、性能提升和风险等级评估。整个流程通过异步方法实现,确保在处理大量内存数据时的响应性。", "interfaces": [ { - "description": "内存优化器的主要接口,定义了优化操作的核心方法。", - "interface_type": "trait", - "name": "MemoryOptimizer", + "description": "核心优化分析器,负责制定优化计划和生成操作", + "interface_type": "struct", + "name": "OptimizationAnalyzer", "parameters": [], "return_type": null, "visibility": "public" }, { - "description": "执行完整的内存优化流程,包括检测、分析、执行和报告。", - "interface_type": "method", - "name": "optimize", - "parameters": [ - { - "description": "优化请求参数", - "is_optional": false, - "name": "request", - "param_type": "OptimizationRequest" - } - ], - "return_type": "Result", + "description": "分析器配置,控制最大操作数和是否启用保守模式", + "interface_type": "struct", + "name": "OptimizationAnalyzerConfig", + "parameters": [], + "return_type": null, "visibility": "public" }, { - "description": "根据指定策略创建优化计划(预览模式)。", + "description": "根据问题列表、策略和过滤器创建优化计划", "interface_type": "method", "name": "create_optimization_plan", "parameters": [ { - "description": "优化策略", + "description": "待处理的问题列表", + "is_optional": false, + "name": "issues", + "param_type": "&[OptimizationIssue]" + }, + { + "description": "采用的优化策略", "is_optional": false, "name": "strategy", - "param_type": "OptimizationStrategy" + "param_type": "&OptimizationStrategy" + }, + { + "description": "额外的过滤条件", + "is_optional": false, + "name": "filters", + "param_type": "&OptimizationFilters" } ], "return_type": "Result", "visibility": "public" }, { - "description": "获取当前所有优化任务的执行状态。", + "description": "分析优化计划的影响和风险等级", "interface_type": "method", - "name": "get_optimization_status", - "parameters": [], - "return_type": "Result>", + "name": "analyze_optimization_impact", + "parameters": [ + { + "description": "待评估的优化计划", + "is_optional": false, + "name": "plan", + "param_type": "&OptimizationPlan" + } + ], + "return_type": "Result", "visibility": "public" }, { - "description": "取消指定ID的正在进行的优化任务。", + "description": "使用指定的内存管理器创建分析器实例", "interface_type": "method", - "name": "cancel_optimization", + "name": "with_memory_manager", "parameters": [ { - "description": "要取消的优化任务ID", + "description": "内存管理器引用", "is_optional": false, - "name": "optimization_id", - "param_type": "str" + "name": "memory_manager", + "param_type": "Arc" } ], - "return_type": "Result<()>", + "return_type": "Self", "visibility": "public" }, { - "description": "创建干运行模式下的模拟优化结果。", + "description": "使用指定配置和内存管理器创建分析器实例", "interface_type": "method", - "name": "create_dry_run_result", + "name": "with_config", "parameters": [ { - "description": "优化任务ID", + "description": "分析器配置", "is_optional": false, - "name": "optimization_id", - "param_type": "str" + "name": "config", + "param_type": "OptimizationAnalyzerConfig" }, { - "description": "优化请求", + "description": "内存管理器引用", "is_optional": false, - "name": "request", - "param_type": "OptimizationRequest" - }, + "name": "memory_manager", + "param_type": "Arc" + } + ], + "return_type": "Self", + "visibility": "public" + }, + { + "description": "优化影响分析结果,包含预测、风险等级和统计信息", + "interface_type": "struct", + "name": "OptimizationImpact", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "优化操作的风险等级枚举", + "interface_type": "enum", + "name": "RiskLevel", + "parameters": [], + "return_type": null, + "visibility": "public" + } + ], + "responsibilities": [ + "根据优化策略过滤和选择需要处理的内存问题", + "分析各类内存问题并生成具体的优化操作指令", + "在保守模式下调整或过滤高风险操作以降低系统影响", + "评估和预测优化计划的执行效果与潜在风险", + "协调内存管理器与优化计划之间的数据交互" + ] + }, + { + "code_dossier": { + "code_purpose": "util", + "description": "A utility module providing shared helper functions for text processing, language detection, JSON extraction, message parsing, and Cypher query sanitization in a memory processing system.", + "file_path": "cortex-mem-core/src/memory/utils.rs", + "functions": [ + "remove_code_blocks", + "extract_json", + "detect_language", + "parse_messages", + "sanitize_for_cypher", + "filter_messages_by_role", + "filter_messages_by_roles" + ], + "importance_score": 0.8, + "interfaces": [ + "LanguageInfo", + "remove_code_blocks", + "extract_json", + "detect_language", + "parse_messages", + "sanitize_for_cypher", + "filter_messages_by_role", + "filter_messages_by_roles" + ], + "name": "utils.rs", + "source_summary": "use std::collections::HashMap;\nuse serde::{Deserialize, Serialize};\nuse tracing::debug;\n\n/// Language information structure\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LanguageInfo {\n pub language_code: String,\n pub language_name: String,\n pub confidence: f32,\n}\n\n/// Extract and remove code blocks from text (similar to mem0's remove_code_blocks)\npub fn remove_code_blocks(content: &str) -> String {\n use regex::Regex;\n let pattern = Regex::new(r\"^```[a-zA-Z0-9]*\\n([\\s\\S]*?)\\n```$\").unwrap();\n \n if let Some(match_result) = pattern.find(content.trim()) {\n let inner_content = &content[match_result.start() + 3..match_result.end() - 3];\n let cleaned = inner_content.trim();\n \n // Remove thinking blocks like ... or【thinking】...【/thinking】\n let cleaned = regex::Regex::new(r\"(.*?|【thinking】.*?【/thinking】)\")\n .unwrap_or_else(|_| {\n // If the primary pattern fails, create a simple one\n regex::Regex::new(r\"【thinking】.*?【/thinking】\").unwrap()\n })\n .replace_all(cleaned, \"\")\n .replace(\"\\n\\n\\n\", \"\\n\\n\")\n .trim()\n .to_string();\n \n cleaned\n } else {\n // If no code blocks found, remove thinking blocks from the whole text\n let cleaned = regex::Regex::new(r\"(.*?|【thinking】.*?【/thinking】)\")\n .unwrap_or_else(|_| {\n regex::Regex::new(r\"【thinking】.*?【/thinking】\").unwrap()\n })\n .replace_all(content, \"\")\n .replace(\"\\n\\n\\n\", \"\\n\\n\")\n .trim()\n .to_string();\n \n cleaned\n }\n}\n\n/// Extract JSON content from text, removing enclosing triple backticks and optional 'json' tag\npub fn extract_json(text: &str) -> String {\n let text = text.trim();\n \n // First try to find code blocks\n if let Some(pattern) = regex::Regex::new(r\"```(?:json)?\\s*(.*?)\\s*```\").unwrap().find(text) {\n let json_str = &text[pattern.start() + 3 + 3..pattern.end() - 3]; // Skip ``` and optional 'json\\n'\n json_str.trim().to_string()\n } else {\n // Assume it's raw JSON\n text.to_string()\n }\n}\n\n/// Detect language of the input text\npub fn detect_language(text: &str) -> LanguageInfo {\n // Simple language detection based on common patterns\n // For production use, consider using a proper NLP library like whatlang or cld3\n \n let clean_text = text.trim().to_lowercase();\n \n // Chinese detection\n if clean_text.chars().any(|c| (c as u32) > 0x4E00 && (c as u32) < 0x9FFF) {\n return LanguageInfo {\n language_code: \"zh\".to_string(),\n language_name: \"Chinese\".to_string(),\n confidence: 0.9,\n };\n }\n \n // Japanese detection (Hiragana, Katakana, Kanji)\n if clean_text.chars().any(|c| \n (c as u32 >= 0x3040 && c as u32 <= 0x30FF) || // Hiragana, Katakana\n ((c as u32) >= 0x4E00 && (c as u32) < 0x9FFF) // Kanji\n ) {\n return LanguageInfo {\n language_code: \"ja\".to_string(),\n language_name: \"Japanese\".to_string(),\n confidence: 0.8,\n };\n }\n \n // Korean detection\n if clean_text.chars().any(|c| c as u32 >= 0xAC00 && c as u32 <= 0xD7AF) {\n return LanguageInfo {\n language_code: \"ko\".to_string(),\n language_name: \"Korean\".to_string(),\n confidence: 0.8,\n };\n }\n \n // Russian/Cyrillic detection\n if clean_text.chars().any(|c| c as u32 >= 0x0400 && c as u32 <= 0x04FF) {\n return LanguageInfo {\n language_code: \"ru\".to_string(),\n language_name: \"Russian\".to_string(),\n confidence: 0.9,\n };\n }\n \n // Arabic detection\n if clean_text.chars().any(|c| c as u32 >= 0x0600 && c as u32 <= 0x06FF) {\n return LanguageInfo {\n language_code: \"ar\".to_string(),\n language_name: \"Arabic\".to_string(),\n confidence: 0.9,\n };\n }\n \n // Default to English\n LanguageInfo {\n language_code: \"en\".to_string(),\n language_name: \"English\".to_string(),\n confidence: 0.7,\n }\n}\n\n/// Parse messages from conversation (similar to mem0's parse_messages)\npub fn parse_messages(messages: &[crate::types::Message]) -> String {\n let mut response = String::new();\n \n for msg in messages {\n match msg.role.as_str() {\n \"system\" => response.push_str(&format!(\"system: {}\\n\", msg.content)),\n \"user\" => response.push_str(&format!(\"user: {}\\n\", msg.content)),\n \"assistant\" => response.push_str(&format!(\"assistant: {}\\n\", msg.content)),\n _ => debug!(\"Unknown message role: {}\", msg.role),\n }\n }\n \n response\n}\n\n/// Sanitize text for Cypher queries (similar to mem0's sanitize_relationship_for_cypher)\npub fn sanitize_for_cypher(text: &str) -> String {\n let char_map = HashMap::from([\n (\"...\", \"_ellipsis_\"),\n (\"…\", \"_ellipsis_\"),\n (\"。\", \"_period_\"),\n (\",\", \"_comma_\"),\n (\";\", \"_semicolon_\"),\n (\":\", \"_colon_\"),\n (\"!\", \"_exclamation_\"),\n (\"?\", \"_question_\"),\n (\"(\", \"_lparen_\"),\n (\")\", \"_rparen_\"),\n (\"【\", \"_lbracket_\"),\n (\"】\", \"_rbracket_\"),\n (\"《\", \"_langle_\"),\n (\"》\", \"_rangle_\"),\n (\"'\", \"_apostrophe_\"),\n (\"\\\"\", \"_quote_\"),\n (\"\\\\\", \"_backslash_\"),\n (\"/\", \"_slash_\"),\n (\"|\", \"_pipe_\"),\n (\"&\", \"_ampersand_\"),\n (\"=\", \"_equals_\"),\n (\"+\", \"_plus_\"),\n (\"*\", \"_asterisk_\"),\n (\"^\", \"_caret_\"),\n (\"%\", \"_percent_\"),\n (\"$\", \"_dollar_\"),\n (\"#\", \"_hash_\"),\n (\"@\", \"_at_\"),\n (\"!\", \"_bang_\"),\n (\"?\", \"_question_\"),\n (\"(\", \"_lparen_\"),\n (\")\", \"_rparen_\"),\n (\"[\", \"_lbracket_\"),\n (\"]\", \"_rbracket_\"),\n (\"{\", \"_lbrace_\"),\n (\"}\", \"_rbrace_\"),\n (\"<\", \"_langle_\"),\n (\">\", \"_rangle_\"),\n ]);\n \n let mut sanitized = text.to_string();\n \n for (old, new) in &char_map {\n sanitized = sanitized.replace(old, new);\n }\n \n // Clean up multiple underscores\n while sanitized.contains(\"__\") {\n sanitized = sanitized.replace(\"__\", \"_\");\n }\n \n sanitized.trim_start_matches('_').trim_end_matches('_').to_string()\n}\n\n/// Filter message history by roles (for user-only or assistant-only extraction)\npub fn filter_messages_by_role(messages: &[crate::types::Message], role: &str) -> Vec {\n messages\n .iter()\n .filter(|msg| msg.role == role)\n .cloned()\n .collect()\n}\n\n/// Filter messages by multiple roles\npub fn filter_messages_by_roles(messages: &[crate::types::Message], roles: &[&str]) -> Vec {\n messages\n .iter()\n .filter(|msg| roles.contains(&msg.role.as_str()))\n .cloned()\n .collect()\n}\n\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 14.0, + "lines_of_code": 216, + "number_of_classes": 1, + "number_of_functions": 8 + }, + "dependencies": [ + { + "dependency_type": "std", + "is_external": false, + "line_number": null, + "name": "std::collections::HashMap", + "path": "std::collections::HashMap", + "version": null + }, + { + "dependency_type": "crate", + "is_external": true, + "line_number": null, + "name": "serde", + "path": "serde", + "version": null + }, + { + "dependency_type": "crate", + "is_external": true, + "line_number": null, + "name": "tracing", + "path": "tracing", + "version": null + }, + { + "dependency_type": "crate", + "is_external": true, + "line_number": null, + "name": "regex", + "path": "regex", + "version": null + } + ], + "detailed_description": "This utility module provides a collection of pure helper functions for preprocessing and transforming text and message data within a memory processing system. Key functionalities include: (1) removing Markdown-style code blocks and thinking markers from text; (2) extracting JSON content from code fences; (3) detecting natural language based on Unicode ranges; (4) formatting conversation messages into a string representation; (5) sanitizing text for safe use in Cypher database queries by replacing special characters; and (6) filtering message histories by role(s). The functions are designed to be stateless and reusable across different components of the system, particularly in preprocessing pipelines for memory storage and retrieval.", + "interfaces": [ + { + "description": "Represents detected language information with code, name, and confidence score", + "interface_type": "struct", + "name": "LanguageInfo", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Removes code blocks and thinking patterns from text", + "interface_type": "function", + "name": "remove_code_blocks", + "parameters": [ { - "description": "开始时间", + "description": "Input text that may contain code blocks", "is_optional": false, - "name": "start_time", - "param_type": "DateTime" - }, + "name": "content", + "param_type": "&str" + } + ], + "return_type": "String", + "visibility": "public" + }, + { + "description": "Extracts JSON content from text, removing surrounding code fences", + "interface_type": "function", + "name": "extract_json", + "parameters": [ + { + "description": "Input text that may contain JSON in code blocks", + "is_optional": false, + "name": "text", + "param_type": "&str" + } + ], + "return_type": "String", + "visibility": "public" + }, + { + "description": "Detects the language of text based on Unicode character ranges", + "interface_type": "function", + "name": "detect_language", + "parameters": [ { - "description": "优化计划", + "description": "Input text to analyze", "is_optional": false, - "name": "plan", - "param_type": "OptimizationPlan" + "name": "text", + "param_type": "&str" } ], - "return_type": "OptimizationResult", - "visibility": "private" + "return_type": "LanguageInfo", + "visibility": "public" }, { - "description": "更新指定优化任务的执行状态。", - "interface_type": "method", - "name": "update_optimization_status", + "description": "Formats conversation messages into a string representation", + "interface_type": "function", + "name": "parse_messages", "parameters": [ { - "description": "优化任务ID", + "description": "Slice of message objects", "is_optional": false, - "name": "optimization_id", - "param_type": "str" - }, + "name": "messages", + "param_type": "&[crate::types::Message]" + } + ], + "return_type": "String", + "visibility": "public" + }, + { + "description": "Sanitizes text for safe use in Cypher database queries", + "interface_type": "function", + "name": "sanitize_for_cypher", + "parameters": [ { - "description": "新的状态信息", + "description": "Input text to sanitize", "is_optional": false, - "name": "status", - "param_type": "OptimizationStatus" + "name": "text", + "param_type": "&str" } ], - "return_type": null, - "visibility": "private" + "return_type": "String", + "visibility": "public" }, { - "description": "创建新的MemoryOptimizer实例。", - "interface_type": "method", - "name": "create", + "description": "Filters messages by a single role", + "interface_type": "function", + "name": "filter_messages_by_role", "parameters": [ { - "description": "内存管理器实例", + "description": "Slice of message objects", "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" + "name": "messages", + "param_type": "&[crate::types::Message]" }, { - "description": "优化配置", + "description": "Role to filter by", "is_optional": false, - "name": "config", - "param_type": "OptimizationConfig" + "name": "role", + "param_type": "&str" } ], - "return_type": "Result>", + "return_type": "Vec", "visibility": "public" }, { - "description": "构造一个新的DefaultMemoryOptimizer实例。", - "interface_type": "constructor", - "name": "new", + "description": "Filters messages by multiple roles", + "interface_type": "function", + "name": "filter_messages_by_roles", "parameters": [ { - "description": "内存管理器实例", + "description": "Slice of message objects", "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" + "name": "messages", + "param_type": "&[crate::types::Message]" }, { - "description": "优化配置", + "description": "Slice of roles to filter by", "is_optional": false, - "name": "config", - "param_type": "OptimizationConfig" + "name": "roles", + "param_type": "&[&str]" } ], - "return_type": "DefaultMemoryOptimizer", + "return_type": "Vec", "visibility": "public" } ], "responsibilities": [ - "协调内存优化的全流程,包括检测、分析、执行和报告", - "管理并跟踪多个并发优化任务的执行状态", - "提供干运行模式支持,允许预演优化计划而不实际执行", - "作为统一入口处理内存优化相关的所有操作请求", - "维护优化任务的生命周期,包括启动、监控、取消和清理" + "Provide text preprocessing utilities for code block and thinking pattern removal", + "Extract and clean JSON content from formatted text", + "Detect natural language from text based on Unicode character ranges", + "Format and filter conversation message histories", + "Sanitize text for safe use in Cypher database queries" ] }, { "code_dossier": { - "code_purpose": "specificfeature", - "description": "优化分析器 - 负责分析内存中的问题并制定相应的优化策略", - "file_path": "cortex-mem-core/src/memory/optimization_analyzer.rs", + "code_purpose": "types", + "description": "Defines a comprehensive error enumeration and associated result type for memory-related operations in the system.", + "file_path": "cortex-mem-core/src/error.rs", "functions": [ - "new", - "with_memory_manager", - "with_config", - "create_optimization_plan", - "generate_optimization_actions", - "filter_issues_by_strategy", - "analyze_issue_and_generate_actions", - "filter_actions_conservatively", - "analyze_optimization_impact", - "calculate_risk_level" + "config", + "validation", + "embedding", + "parse" ], "importance_score": 0.8, "interfaces": [ - "OptimizationAnalyzer", - "OptimizationAnalyzerConfig", - "OptimizationImpact", - "RiskLevel" + "MemoryError", + "Result" ], - "name": "optimization_analyzer.rs", - "source_summary": "use std::collections::HashMap;\nuse std::sync::Arc;\nuse uuid::Uuid;\n\nuse crate::{\n error::Result,\n types::{\n OptimizationAction, OptimizationFilters, OptimizationIssue, OptimizationStrategy,\n IssueKind, IssueSeverity,\n },\n memory::MemoryManager,\n};\n\nuse super::optimization_plan::{OptimizationPlan, ActionStatistics};\n\n/// 优化分析器 - 负责分析问题并制定优化策略\npub struct OptimizationAnalyzer {\n // 分析器配置\n config: OptimizationAnalyzerConfig,\n #[allow(dead_code)]\n memory_manager: Arc,\n}\n\n#[derive(Debug, Clone)]\npub struct OptimizationAnalyzerConfig {\n pub max_actions_per_plan: usize,\n pub conservative_mode: bool,\n}\n\nimpl Default for OptimizationAnalyzerConfig {\n fn default() -> Self {\n Self {\n max_actions_per_plan: 5000,\n conservative_mode: false,\n }\n }\n}\n\nimpl OptimizationAnalyzer {\n pub fn new() -> Self {\n panic!(\"OptimizationAnalyzer requires MemoryManager. Use with_memory_manager() instead.\");\n }\n \n pub fn with_memory_manager(memory_manager: Arc) -> Self {\n Self {\n config: OptimizationAnalyzerConfig::default(),\n memory_manager,\n }\n }\n \n pub fn with_config(config: OptimizationAnalyzerConfig, memory_manager: Arc) -> Self {\n Self {\n config,\n memory_manager,\n }\n }\n \n /// 根据问题制定优化计划\n pub async fn create_optimization_plan(\n &self,\n issues: &[OptimizationIssue],\n strategy: &OptimizationStrategy,\n filters: &OptimizationFilters,\n ) -> Result {\n let optimization_id = Uuid::new_v4().to_string();\n \n tracing::info!(optimization_id = optimization_id, \"制定优化计划, 策略: {:?}, 问题数量: {}\", strategy, issues.len());\n \n let actions = self.generate_optimization_actions(issues, strategy).await?;\n \n let plan = OptimizationPlan::new(\n optimization_id,\n strategy.clone(),\n issues.to_vec(),\n actions,\n filters.clone(),\n );\n \n tracing::info!(optimization_id = plan.optimization_id, \"计划制定完成: {} 个操作\", plan.actions.len());\n Ok(plan)\n }\n \n /// 生成优化操作\n async fn generate_optimization_actions(\n &self,\n issues: &[OptimizationIssue],\n strategy: &OptimizationStrategy,\n ) -> Result> {\n let mut actions = Vec::new();\n \n // 根据策略过滤相关问题\n let relevant_issues = self.filter_issues_by_strategy(issues, strategy);\n \n tracing::info!(\"策略 {:?} 相关的 {} 个问题\", strategy, relevant_issues.len());\n \n for issue in relevant_issues {\n let issue_actions = self.analyze_issue_and_generate_actions(&issue).await?;\n actions.extend(issue_actions);\n \n // 限制操作数量以防止计划过大\n if actions.len() >= self.config.max_actions_per_plan {\n tracing::warn!(\"达到最大操作数量限制: {}\", self.config.max_actions_per_plan);\n break;\n }\n }\n \n // 如果是保守模式,进一步过滤操作\n if self.config.conservative_mode {\n actions = self.filter_actions_conservatively(actions);\n }\n \n Ok(actions)\n }\n \n /// 根据策略过滤相关问题\n fn filter_issues_by_strategy<'a>(\n &'a self,\n issues: &'a [OptimizationIssue],\n strategy: &'a OptimizationStrategy,\n ) -> Vec<&'a OptimizationIssue> {\n match strategy {\n OptimizationStrategy::Full => issues.iter().collect(),\n OptimizationStrategy::Incremental => {\n // 只处理高严重程度的问题\n issues.iter()\n .filter(|issue| {\n matches!(issue.severity, IssueSeverity::High | IssueSeverity::Critical)\n })\n .collect()\n }\n OptimizationStrategy::Batch => {\n // 处理所有Medium及以上的问题\n issues.iter()\n .filter(|issue| {\n !matches!(issue.severity, IssueSeverity::Low)\n })\n .collect()\n }\n OptimizationStrategy::Deduplication => {\n issues.iter()\n .filter(|issue| matches!(issue.kind, IssueKind::Duplicate))\n .collect()\n }\n OptimizationStrategy::Relevance => {\n issues.iter()\n .filter(|issue| matches!(issue.kind, IssueKind::Outdated))\n .collect()\n }\n OptimizationStrategy::Quality => {\n issues.iter()\n .filter(|issue| matches!(issue.kind, IssueKind::LowQuality))\n .collect()\n }\n OptimizationStrategy::Space => {\n issues.iter()\n .filter(|issue| matches!(issue.kind, IssueKind::SpaceInefficient))\n .collect()\n }\n }\n }\n \n /// 分析单个问题并生成相应的操作\n async fn analyze_issue_and_generate_actions(\n &self,\n issue: &OptimizationIssue,\n ) -> Result> {\n let mut actions = Vec::new();\n \n match issue.kind {\n IssueKind::Duplicate => {\n if issue.affected_memories.len() > 1 {\n actions.push(OptimizationAction::Merge {\n memories: issue.affected_memories.clone(),\n });\n }\n }\n IssueKind::LowQuality => {\n // 为每个低质量记忆生成操作\n for memory_id in &issue.affected_memories {\n // 对于质量极低的记忆,建议删除\n // 对于中等质量的问题,建议更新重要性分数\n actions.push(OptimizationAction::Delete {\n memory_id: memory_id.clone(),\n });\n }\n }\n IssueKind::Outdated => {\n // 过时记忆可能需要删除或归档\n for memory_id in &issue.affected_memories {\n if issue.severity == IssueSeverity::Critical {\n actions.push(OptimizationAction::Delete {\n memory_id: memory_id.clone(),\n });\n } else {\n actions.push(OptimizationAction::Archive {\n memory_id: memory_id.clone(),\n });\n }\n }\n }\n IssueKind::PoorClassification => {\n // 重新分类记忆\n for memory_id in &issue.affected_memories {\n actions.push(OptimizationAction::Reclassify {\n memory_id: memory_id.clone(),\n });\n }\n }\n IssueKind::SpaceInefficient => {\n // 空间效率问题一般通过归档处理\n for memory_id in &issue.affected_memories {\n actions.push(OptimizationAction::Archive {\n memory_id: memory_id.clone(),\n });\n }\n }\n }\n \n Ok(actions)\n }\n \n /// 保守模式过滤操作\n fn filter_actions_conservatively(&self, actions: Vec) -> Vec {\n let mut filtered = Vec::new();\n \n for action in actions {\n match action {\n // 在保守模式下,避免删除操作\n OptimizationAction::Delete { .. } => {\n tracing::info!(\"保守模式: 跳过删除操作\");\n continue;\n }\n // 将删除操作转换为归档操作\n OptimizationAction::Archive { .. } => {\n // 保留归档操作\n filtered.push(action);\n }\n _ => {\n // 保留其他操作\n filtered.push(action);\n }\n }\n }\n \n filtered\n }\n \n /// 分析优化效果预测\n pub fn analyze_optimization_impact(\n &self,\n plan: &OptimizationPlan,\n ) -> Result {\n let stats = plan.action_statistics();\n let issue_stats = plan.issue_statistics();\n \n let mut predictions = HashMap::new();\n \n // 预测去重效果\n if stats.merge_count > 0 {\n predictions.insert(\"deduplication\".to_string(), format!(\"预计合并 {} 个重复记忆\", stats.merge_count));\n }\n \n // 预测空间节省\n if stats.delete_count > 0 {\n predictions.insert(\"space_saving\".to_string(), format!(\"预计删除 {} 个记忆\", stats.delete_count));\n }\n \n // 预测质量改善\n if stats.update_count > 0 {\n predictions.insert(\"quality_improvement\".to_string(), format!(\"预计更新 {} 个记忆\", stats.update_count));\n }\n \n // 预测性能提升\n let critical_issues = issue_stats.critical_or_high();\n if critical_issues > 0 {\n predictions.insert(\"performance_boost\".to_string(), format!(\"预计解决 {} 个严重问题\", critical_issues));\n }\n \n Ok(OptimizationImpact {\n estimated_duration_minutes: plan.estimated_duration_minutes,\n risk_level: self.calculate_risk_level(&stats),\n predictions,\n statistics: stats,\n })\n }\n \n /// 计算风险等级\n fn calculate_risk_level(&self, stats: &ActionStatistics) -> RiskLevel {\n let total_actions = stats.total();\n \n if total_actions == 0 {\n return RiskLevel::VeryLow;\n }\n \n let deletion_ratio = stats.delete_count as f64 / total_actions as f64;\n let merge_ratio = stats.merge_count as f64 / total_actions as f64;\n \n if deletion_ratio > 0.3 || merge_ratio > 0.5 {\n RiskLevel::High\n } else if deletion_ratio > 0.1 || merge_ratio > 0.3 {\n RiskLevel::Medium\n } else if deletion_ratio > 0.05 || merge_ratio > 0.1 {\n RiskLevel::Low\n } else {\n RiskLevel::VeryLow\n }\n }\n}\n\n/// 优化影响分析\n#[derive(Debug, Clone)]\npub struct OptimizationImpact {\n pub estimated_duration_minutes: u64,\n pub risk_level: RiskLevel,\n pub predictions: HashMap,\n pub statistics: ActionStatistics,\n}\n\n/// 风险等级\n#[derive(Debug, Clone, PartialEq)]\npub enum RiskLevel {\n VeryLow,\n Low,\n Medium,\n High,\n}\n\nimpl std::fmt::Display for RiskLevel {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n match self {\n RiskLevel::VeryLow => write!(f, \"极低\"),\n RiskLevel::Low => write!(f, \"低\"),\n RiskLevel::Medium => write!(f, \"中等\"),\n RiskLevel::High => write!(f, \"高\"),\n }\n }\n}\n\nimpl Default for OptimizationAnalyzer {\n fn default() -> Self {\n panic!(\"OptimizationAnalyzer requires MemoryManager. Use with_memory_manager() instead.\");\n }\n}" + "name": "error.rs", + "source_summary": "use thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum MemoryError {\n #[error(\"Vector store error: {0}\")]\n VectorStore(#[from] qdrant_client::QdrantError),\n \n #[error(\"LLM error: {0}\")]\n LLM(String),\n \n #[error(\"Serialization error: {0}\")]\n Serialization(#[from] serde_json::Error),\n \n #[error(\"HTTP client error: {0}\")]\n Http(#[from] reqwest::Error),\n \n #[error(\"Memory not found: {id}\")]\n NotFound { id: String },\n \n #[error(\"Invalid memory action: {action}\")]\n InvalidAction { action: String },\n \n #[error(\"Configuration error: {0}\")]\n Config(String),\n \n #[error(\"Validation error: {0}\")]\n Validation(String),\n \n #[error(\"Embedding error: {0}\")]\n Embedding(String),\n \n #[error(\"Parse error: {0}\")]\n Parse(String),\n}\n\npub type Result = std::result::Result;\n\nimpl MemoryError {\n pub fn config>(msg: S) -> Self {\n Self::Config(msg.into())\n }\n \n pub fn validation>(msg: S) -> Self {\n Self::Validation(msg.into())\n }\n \n pub fn embedding>(msg: S) -> Self {\n Self::Embedding(msg.into())\n }\n \n pub fn parse>(msg: S) -> Self {\n Self::Parse(msg.into())\n }\n}" }, "complexity_metrics": { - "cyclomatic_complexity": 26.0, - "lines_of_code": 343, - "number_of_classes": 4, - "number_of_functions": 16 + "cyclomatic_complexity": 1.0, + "lines_of_code": 54, + "number_of_classes": 0, + "number_of_functions": 4 }, "dependencies": [ { - "dependency_type": "std", - "is_external": false, + "dependency_type": "library", + "is_external": true, "line_number": 1, - "name": "std::collections::HashMap", + "name": "thiserror", "path": null, "version": null }, { - "dependency_type": "std", - "is_external": false, - "line_number": 2, - "name": "std::sync::Arc", + "dependency_type": "library", + "is_external": true, + "line_number": 5, + "name": "qdrant_client", "path": null, "version": null }, { - "dependency_type": "crate", + "dependency_type": "library", "is_external": true, - "line_number": 3, - "name": "uuid::Uuid", + "line_number": 12, + "name": "serde_json", "path": null, "version": null }, { - "dependency_type": "local", - "is_external": false, - "line_number": 6, - "name": "crate::error::Result", - "path": "../error", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 7, - "name": "crate::types::OptimizationAction", - "path": "../types", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 7, - "name": "crate::types::OptimizationFilters", - "path": "../types", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 7, - "name": "crate::types::OptimizationIssue", - "path": "../types", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 7, - "name": "crate::types::OptimizationStrategy", - "path": "../types", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 7, - "name": "crate::types::IssueKind", - "path": "../types", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 7, - "name": "crate::types::IssueSeverity", - "path": "../types", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 8, - "name": "crate::memory::MemoryManager", - "path": "./memory", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 11, - "name": "super::optimization_plan::OptimizationPlan", - "path": "../optimization_plan", - "version": null - }, - { - "dependency_type": "local", - "is_external": false, - "line_number": 11, - "name": "super::optimization_plan::ActionStatistics", - "path": "../optimization_plan", - "version": null - }, - { - "dependency_type": "crate", + "dependency_type": "library", "is_external": true, - "line_number": null, - "name": "tracing", + "line_number": 15, + "name": "reqwest", "path": null, "version": null } ], - "detailed_description": "该组件是内存优化系统的核心分析模块,负责根据检测到的内存问题和指定的优化策略生成具体的优化计划。它首先基于策略类型(如完全优化、增量优化、批处理等)过滤相关问题,然后针对每个问题类型(重复、低质量、过时等)生成相应的操作指令(合并、删除、归档等)。组件还提供保守模式以降低风险,并能预测优化操作的影响,包括空间节省、性能提升和风险等级评估。整个流程通过异步方法实现,确保在处理大量内存数据时的响应性。", + "detailed_description": "This component defines a centralized error handling mechanism for memory-related operations in the Cortex-MEM system. The `MemoryError` enum encapsulates various error conditions including vector store failures, LLM communication issues, serialization problems, HTTP client errors, missing memory entries, invalid actions, configuration issues, validation failures, embedding processing errors, and parsing problems. Each variant includes appropriate error source propagation using `#[from]` where applicable. The component also defines a type alias `Result` that uses `MemoryError` as the error type, promoting consistent error handling across the codebase. Additional helper constructor methods are provided for common error types to simplify error creation with custom messages.", "interfaces": [ { - "description": "核心优化分析器,负责制定优化计划和生成操作", - "interface_type": "struct", - "name": "OptimizationAnalyzer", + "description": "An enumeration of all possible error types that can occur in memory operations, using thiserror for automatic Display implementation and error source chaining.", + "interface_type": "enum", + "name": "MemoryError", "parameters": [], "return_type": null, "visibility": "public" }, { - "description": "分析器配置,控制最大操作数和是否启用保守模式", - "interface_type": "struct", - "name": "OptimizationAnalyzerConfig", + "description": "A type alias for Result that uses MemoryError as the error type, simplifying function signatures across the codebase.", + "interface_type": "type_alias", + "name": "Result", "parameters": [], - "return_type": null, + "return_type": "std::result::Result", "visibility": "public" }, { - "description": "根据问题列表、策略和过滤器创建优化计划", - "interface_type": "method", - "name": "create_optimization_plan", + "description": "Creates a Configuration error variant with the provided message.", + "interface_type": "function", + "name": "config", "parameters": [ { - "description": "待处理的问题列表", - "is_optional": false, - "name": "issues", - "param_type": "&[OptimizationIssue]" - }, - { - "description": "采用的优化策略", - "is_optional": false, - "name": "strategy", - "param_type": "&OptimizationStrategy" - }, - { - "description": "额外的过滤条件", + "description": "Error message that can be converted into a String", "is_optional": false, - "name": "filters", - "param_type": "&OptimizationFilters" + "name": "msg", + "param_type": "S" } ], - "return_type": "Result", + "return_type": "MemoryError", "visibility": "public" }, { - "description": "分析优化计划的影响和风险等级", - "interface_type": "method", - "name": "analyze_optimization_impact", + "description": "Creates a Validation error variant with the provided message.", + "interface_type": "function", + "name": "validation", "parameters": [ { - "description": "待评估的优化计划", + "description": "Error message that can be converted into a String", "is_optional": false, - "name": "plan", - "param_type": "&OptimizationPlan" + "name": "msg", + "param_type": "S" } ], - "return_type": "Result", + "return_type": "MemoryError", "visibility": "public" }, { - "description": "使用指定的内存管理器创建分析器实例", - "interface_type": "method", - "name": "with_memory_manager", + "description": "Creates an Embedding error variant with the provided message.", + "interface_type": "function", + "name": "embedding", "parameters": [ { - "description": "内存管理器引用", + "description": "Error message that can be converted into a String", "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" + "name": "msg", + "param_type": "S" } ], - "return_type": "Self", + "return_type": "MemoryError", "visibility": "public" }, { - "description": "使用指定配置和内存管理器创建分析器实例", - "interface_type": "method", - "name": "with_config", + "description": "Creates a Parse error variant with the provided message.", + "interface_type": "function", + "name": "parse", "parameters": [ { - "description": "分析器配置", - "is_optional": false, - "name": "config", - "param_type": "OptimizationAnalyzerConfig" - }, - { - "description": "内存管理器引用", + "description": "Error message that can be converted into a String", "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" + "name": "msg", + "param_type": "S" } ], - "return_type": "Self", + "return_type": "MemoryError", "visibility": "public" + } + ], + "responsibilities": [ + "Defines a unified error type for all memory-related operations in the system", + "Provides automatic error conversion from external dependencies such as Qdrant, serde_json, and reqwest", + "Offers convenient constructor methods for common error types with custom messages", + "Establishes a standard result type (Result) for consistent return value handling", + "Enables comprehensive error reporting with contextual information for debugging" + ] + }, + { + "code_dossier": { + "code_purpose": "tool", + "description": "Initializes and configures a logging system using tracing_subscriber with file output based on provided configuration, supporting timestamped log files and configurable log levels.", + "file_path": "cortex-mem-core/src/logging.rs", + "functions": [ + "init_logging" + ], + "importance_score": 0.8, + "interfaces": [ + "init_logging" + ], + "name": "logging.rs", + "source_summary": "use anyhow::Result;\nuse chrono::{DateTime, Local};\nuse std::fs;\nuse std::path::Path;\nuse tracing::info;\nuse tracing_subscriber::{\n EnvFilter, Layer, fmt, fmt::time::ChronoLocal, layer::SubscriberExt, util::SubscriberInitExt,\n};\n\n/// 初始化日志系统\npub fn init_logging(config: &cortex_mem_config::LoggingConfig) -> Result<()> {\n if !config.enabled {\n // 如果日志未启用,不设置任何tracing层\n tracing_subscriber::registry().try_init().ok(); // 避免重复初始化错误\n return Ok(());\n }\n\n // 创建日志目录(如果不存在)\n fs::create_dir_all(&config.log_directory)?;\n\n // 生成带时间戳的日志文件名\n let local_time: DateTime = Local::now();\n let log_file_name = format!(\"cortex-memo-{}.log\", local_time.format(\"%Y-%m-%d-%H-%M-%S\"));\n let log_file_path = Path::new(&config.log_directory).join(log_file_name);\n\n // 创建文件写入器\n let file_writer = std::fs::File::create(&log_file_path)?;\n\n // 根据配置的日志级别设置过滤器\n let level_filter = match config.level.to_lowercase().as_str() {\n \"error\" => \"error\",\n \"warn\" => \"warn\",\n \"info\" => \"info\",\n \"debug\" => \"debug\",\n \"trace\" => \"trace\",\n _ => \"info\", // 默认为info级别\n };\n\n // 只配置文件输出,不配置控制台输出\n let file_filter =\n EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level_filter));\n let file_layer = fmt::layer()\n .with_target(false)\n .with_ansi(false)\n .with_writer(std::sync::Mutex::new(file_writer))\n .with_timer(ChronoLocal::new(\"%Y-%m-%d %H:%M:%S%.3f\".into()))\n .with_filter(file_filter);\n\n // 初始化tracing订阅者,只添加文件层,不添加控制台层\n tracing_subscriber::registry().with(file_layer).try_init()?;\n\n info!(\"Logging initialized. Log file: {}\", log_file_path.display());\n Ok(())\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 3.0, + "lines_of_code": 54, + "number_of_classes": 0, + "number_of_functions": 1 + }, + "dependencies": [ + { + "dependency_type": "use", + "is_external": true, + "line_number": 1, + "name": "anyhow", + "path": null, + "version": null }, { - "description": "优化影响分析结果,包含预测、风险等级和统计信息", - "interface_type": "struct", - "name": "OptimizationImpact", - "parameters": [], - "return_type": null, - "visibility": "public" + "dependency_type": "use", + "is_external": true, + "line_number": 2, + "name": "chrono", + "path": null, + "version": null }, { - "description": "优化操作的风险等级枚举", - "interface_type": "enum", - "name": "RiskLevel", - "parameters": [], - "return_type": null, - "visibility": "public" + "dependency_type": "use", + "is_external": false, + "line_number": 3, + "name": "std::fs", + "path": null, + "version": null + }, + { + "dependency_type": "use", + "is_external": false, + "line_number": 4, + "name": "std::path::Path", + "path": null, + "version": null + }, + { + "dependency_type": "use", + "is_external": true, + "line_number": 5, + "name": "tracing", + "path": null, + "version": null + }, + { + "dependency_type": "use", + "is_external": true, + "line_number": 6, + "name": "tracing_subscriber", + "path": null, + "version": null + } + ], + "detailed_description": "The component is responsible for initializing a structured logging system using the `tracing` and `tracing_subscriber` crates. It reads logging configuration including whether logging is enabled, the log level, and the output directory. If logging is enabled, it creates the log directory if it does not exist, generates a timestamped log file name, creates the log file, and sets up a tracing subscriber that writes formatted logs to the file. The log format excludes targets and ANSI colors, uses local time formatting, and applies a filter based on the configured log level. When disabled, it performs a minimal initialization to prevent other components from failing due to missing subscribers. The function logs a confirmation message upon successful initialization.", + "interfaces": [ + { + "description": "Initializes the logging system. Returns Ok if setup succeeds or if logging is disabled; returns Err on filesystem or initialization errors.", + "interface_type": "function", + "name": "init_logging", + "parameters": [ + { + "description": "Reference to logging configuration containing enabled flag, log level, and log directory path", + "is_optional": false, + "name": "config", + "param_type": "&cortex_mem_config::LoggingConfig" + } + ], + "return_type": "Result<()>", + "visibility": "pub" } ], "responsibilities": [ - "根据优化策略过滤和选择需要处理的内存问题", - "分析各类内存问题并生成具体的优化操作指令", - "在保守模式下调整或过滤高风险操作以降低系统影响", - "评估和预测优化计划的执行效果与潜在风险", - "协调内存管理器与优化计划之间的数据交互" + "Initialize the tracing logging system based on runtime configuration", + "Create log directory and timestamped log file for persistent logging", + "Configure tracing subscriber with file output, time formatting, and level filtering", + "Support disabling logging while safely avoiding double-initialization errors", + "Provide observability into system behavior through structured file-based logs" ] }, { "code_dossier": { - "code_purpose": "util", - "description": "A utility module providing shared helper functions for text processing, language detection, JSON extraction, message parsing, and Cypher query sanitization in a memory processing system.", - "file_path": "cortex-mem-core/src/memory/utils.rs", + "code_purpose": "dao", + "description": "Qdrant vector store implementation for memory data persistence and retrieval.", + "file_path": "cortex-mem-core/src/vector_store/qdrant.rs", "functions": [ - "remove_code_blocks", - "extract_json", - "detect_language", - "parse_messages", - "sanitize_for_cypher", - "filter_messages_by_role", - "filter_messages_by_roles" + "new", + "new_with_llm_client", + "ensure_collection", + "verify_collection_dimension", + "memory_to_point", + "filters_to_qdrant_filter", + "point_to_memory", + "embedding_dim", + "set_embedding_dim", + "insert", + "search", + "search_with_threshold", + "update", + "delete", + "get", + "list", + "health_check" ], "importance_score": 0.8, "interfaces": [ - "LanguageInfo", - "remove_code_blocks", - "extract_json", - "detect_language", - "parse_messages", - "sanitize_for_cypher", - "filter_messages_by_role", - "filter_messages_by_roles" + "VectorStore" ], - "name": "utils.rs", - "source_summary": "use std::collections::HashMap;\nuse serde::{Deserialize, Serialize};\nuse tracing::debug;\n\n/// Language information structure\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct LanguageInfo {\n pub language_code: String,\n pub language_name: String,\n pub confidence: f32,\n}\n\n/// Extract and remove code blocks from text (similar to mem0's remove_code_blocks)\npub fn remove_code_blocks(content: &str) -> String {\n use regex::Regex;\n let pattern = Regex::new(r\"^```[a-zA-Z0-9]*\\n([\\s\\S]*?)\\n```$\").unwrap();\n \n if let Some(match_result) = pattern.find(content.trim()) {\n let inner_content = &content[match_result.start() + 3..match_result.end() - 3];\n let cleaned = inner_content.trim();\n \n // Remove thinking blocks like ... or【thinking】...【/thinking】\n let cleaned = regex::Regex::new(r\"(.*?|【thinking】.*?【/thinking】)\")\n .unwrap_or_else(|_| {\n // If the primary pattern fails, create a simple one\n regex::Regex::new(r\"【thinking】.*?【/thinking】\").unwrap()\n })\n .replace_all(cleaned, \"\")\n .replace(\"\\n\\n\\n\", \"\\n\\n\")\n .trim()\n .to_string();\n \n cleaned\n } else {\n // If no code blocks found, remove thinking blocks from the whole text\n let cleaned = regex::Regex::new(r\"(.*?|【thinking】.*?【/thinking】)\")\n .unwrap_or_else(|_| {\n regex::Regex::new(r\"【thinking】.*?【/thinking】\").unwrap()\n })\n .replace_all(content, \"\")\n .replace(\"\\n\\n\\n\", \"\\n\\n\")\n .trim()\n .to_string();\n \n cleaned\n }\n}\n\n/// Extract JSON content from text, removing enclosing triple backticks and optional 'json' tag\npub fn extract_json(text: &str) -> String {\n let text = text.trim();\n \n // First try to find code blocks\n if let Some(pattern) = regex::Regex::new(r\"```(?:json)?\\s*(.*?)\\s*```\").unwrap().find(text) {\n let json_str = &text[pattern.start() + 3 + 3..pattern.end() - 3]; // Skip ``` and optional 'json\\n'\n json_str.trim().to_string()\n } else {\n // Assume it's raw JSON\n text.to_string()\n }\n}\n\n/// Detect language of the input text\npub fn detect_language(text: &str) -> LanguageInfo {\n // Simple language detection based on common patterns\n // For production use, consider using a proper NLP library like whatlang or cld3\n \n let clean_text = text.trim().to_lowercase();\n \n // Chinese detection\n if clean_text.chars().any(|c| (c as u32) > 0x4E00 && (c as u32) < 0x9FFF) {\n return LanguageInfo {\n language_code: \"zh\".to_string(),\n language_name: \"Chinese\".to_string(),\n confidence: 0.9,\n };\n }\n \n // Japanese detection (Hiragana, Katakana, Kanji)\n if clean_text.chars().any(|c| \n (c as u32 >= 0x3040 && c as u32 <= 0x30FF) || // Hiragana, Katakana\n ((c as u32) >= 0x4E00 && (c as u32) < 0x9FFF) // Kanji\n ) {\n return LanguageInfo {\n language_code: \"ja\".to_string(),\n language_name: \"Japanese\".to_string(),\n confidence: 0.8,\n };\n }\n \n // Korean detection\n if clean_text.chars().any(|c| c as u32 >= 0xAC00 && c as u32 <= 0xD7AF) {\n return LanguageInfo {\n language_code: \"ko\".to_string(),\n language_name: \"Korean\".to_string(),\n confidence: 0.8,\n };\n }\n \n // Russian/Cyrillic detection\n if clean_text.chars().any(|c| c as u32 >= 0x0400 && c as u32 <= 0x04FF) {\n return LanguageInfo {\n language_code: \"ru\".to_string(),\n language_name: \"Russian\".to_string(),\n confidence: 0.9,\n };\n }\n \n // Arabic detection\n if clean_text.chars().any(|c| c as u32 >= 0x0600 && c as u32 <= 0x06FF) {\n return LanguageInfo {\n language_code: \"ar\".to_string(),\n language_name: \"Arabic\".to_string(),\n confidence: 0.9,\n };\n }\n \n // Default to English\n LanguageInfo {\n language_code: \"en\".to_string(),\n language_name: \"English\".to_string(),\n confidence: 0.7,\n }\n}\n\n/// Parse messages from conversation (similar to mem0's parse_messages)\npub fn parse_messages(messages: &[crate::types::Message]) -> String {\n let mut response = String::new();\n \n for msg in messages {\n match msg.role.as_str() {\n \"system\" => response.push_str(&format!(\"system: {}\\n\", msg.content)),\n \"user\" => response.push_str(&format!(\"user: {}\\n\", msg.content)),\n \"assistant\" => response.push_str(&format!(\"assistant: {}\\n\", msg.content)),\n _ => debug!(\"Unknown message role: {}\", msg.role),\n }\n }\n \n response\n}\n\n/// Sanitize text for Cypher queries (similar to mem0's sanitize_relationship_for_cypher)\npub fn sanitize_for_cypher(text: &str) -> String {\n let char_map = HashMap::from([\n (\"...\", \"_ellipsis_\"),\n (\"…\", \"_ellipsis_\"),\n (\"。\", \"_period_\"),\n (\",\", \"_comma_\"),\n (\";\", \"_semicolon_\"),\n (\":\", \"_colon_\"),\n (\"!\", \"_exclamation_\"),\n (\"?\", \"_question_\"),\n (\"(\", \"_lparen_\"),\n (\")\", \"_rparen_\"),\n (\"【\", \"_lbracket_\"),\n (\"】\", \"_rbracket_\"),\n (\"《\", \"_langle_\"),\n (\"》\", \"_rangle_\"),\n (\"'\", \"_apostrophe_\"),\n (\"\\\"\", \"_quote_\"),\n (\"\\\\\", \"_backslash_\"),\n (\"/\", \"_slash_\"),\n (\"|\", \"_pipe_\"),\n (\"&\", \"_ampersand_\"),\n (\"=\", \"_equals_\"),\n (\"+\", \"_plus_\"),\n (\"*\", \"_asterisk_\"),\n (\"^\", \"_caret_\"),\n (\"%\", \"_percent_\"),\n (\"$\", \"_dollar_\"),\n (\"#\", \"_hash_\"),\n (\"@\", \"_at_\"),\n (\"!\", \"_bang_\"),\n (\"?\", \"_question_\"),\n (\"(\", \"_lparen_\"),\n (\")\", \"_rparen_\"),\n (\"[\", \"_lbracket_\"),\n (\"]\", \"_rbracket_\"),\n (\"{\", \"_lbrace_\"),\n (\"}\", \"_rbrace_\"),\n (\"<\", \"_langle_\"),\n (\">\", \"_rangle_\"),\n ]);\n \n let mut sanitized = text.to_string();\n \n for (old, new) in &char_map {\n sanitized = sanitized.replace(old, new);\n }\n \n // Clean up multiple underscores\n while sanitized.contains(\"__\") {\n sanitized = sanitized.replace(\"__\", \"_\");\n }\n \n sanitized.trim_start_matches('_').trim_end_matches('_').to_string()\n}\n\n/// Filter message history by roles (for user-only or assistant-only extraction)\npub fn filter_messages_by_role(messages: &[crate::types::Message], role: &str) -> Vec {\n messages\n .iter()\n .filter(|msg| msg.role == role)\n .cloned()\n .collect()\n}\n\n/// Filter messages by multiple roles\npub fn filter_messages_by_roles(messages: &[crate::types::Message], roles: &[&str]) -> Vec {\n messages\n .iter()\n .filter(|msg| roles.contains(&msg.role.as_str()))\n .cloned()\n .collect()\n}\n\n" + "name": "qdrant.rs", + "source_summary": "use async_trait::async_trait;\nuse qdrant_client::{\n Qdrant,\n qdrant::{\n Condition, CreateCollection, DeletePoints, Distance, FieldCondition, Filter, GetPoints,\n Match, PointId, PointStruct, PointsIdsList, PointsSelector, Range, ScoredPoint,\n ScrollPoints, SearchPoints, UpsertPoints, VectorParams, VectorsConfig, condition, r#match,\n point_id, points_selector, vectors_config,\n },\n};\nuse std::collections::HashMap;\nuse tracing::{debug, error, info, warn};\n\nuse crate::{\n config::QdrantConfig,\n error::{MemoryError, Result},\n types::{Filters, Memory, MemoryMetadata, ScoredMemory},\n vector_store::VectorStore,\n};\n\n/// Qdrant vector store implementation\npub struct QdrantVectorStore {\n client: Qdrant,\n collection_name: String,\n embedding_dim: Option,\n}\n\nimpl QdrantVectorStore {\n /// Create a new Qdrant vector store\n pub async fn new(config: &QdrantConfig) -> Result {\n let client = Qdrant::from_url(&config.url)\n .build()\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let store = Self {\n client,\n collection_name: config.collection_name.clone(),\n embedding_dim: config.embedding_dim,\n };\n\n Ok(store)\n }\n\n /// Create a new Qdrant vector store with auto-detected embedding dimension\n pub async fn new_with_llm_client(\n config: &QdrantConfig,\n llm_client: &dyn crate::llm::LLMClient,\n ) -> Result {\n let client = Qdrant::from_url(&config.url)\n .build()\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let mut store = Self {\n client,\n collection_name: config.collection_name.clone(),\n embedding_dim: config.embedding_dim,\n };\n\n // Auto-detect embedding dimension if not specified\n if store.embedding_dim.is_none() {\n info!(\"Auto-detecting embedding dimension...\");\n let test_embedding = llm_client.embed(\"test\").await?;\n let detected_dim = test_embedding.len();\n info!(\"Detected embedding dimension: {}\", detected_dim);\n store.embedding_dim = Some(detected_dim);\n }\n\n // Ensure collection exists with correct dimension\n store.ensure_collection().await?;\n\n Ok(store)\n }\n\n /// Ensure the collection exists, create if not\n async fn ensure_collection(&self) -> Result<()> {\n let collections = self\n .client\n .list_collections()\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let collection_exists = collections\n .collections\n .iter()\n .any(|c| c.name == self.collection_name);\n\n if !collection_exists {\n let embedding_dim = self.embedding_dim.ok_or_else(|| {\n MemoryError::config(\n \"Embedding dimension not set. Use new_with_llm_client for auto-detection.\",\n )\n })?;\n\n info!(\n \"Creating collection: {} with dimension: {}\",\n self.collection_name, embedding_dim\n );\n\n let vectors_config = VectorsConfig {\n config: Some(vectors_config::Config::Params(VectorParams {\n size: embedding_dim as u64,\n distance: Distance::Cosine.into(),\n ..Default::default()\n })),\n };\n\n self.client\n .create_collection(CreateCollection {\n collection_name: self.collection_name.clone(),\n vectors_config: Some(vectors_config),\n ..Default::default()\n })\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n info!(\"Collection created successfully: {}\", self.collection_name);\n } else {\n debug!(\"Collection already exists: {}\", self.collection_name);\n\n // Verify dimension compatibility if collection exists\n if let Some(expected_dim) = self.embedding_dim {\n if let Err(e) = self.verify_collection_dimension(expected_dim).await {\n warn!(\"Collection dimension verification failed: {}\", e);\n }\n }\n }\n\n Ok(())\n }\n\n /// Verify that the existing collection has the expected dimension\n async fn verify_collection_dimension(&self, expected_dim: usize) -> Result<()> {\n let collection_info = self\n .client\n .collection_info(&self.collection_name)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n if let Some(collection_config) = collection_info.result {\n if let Some(config) = collection_config.config {\n if let Some(params) = config.params {\n if let Some(vectors_config) = params.vectors_config {\n if let Some(vectors_config::Config::Params(vector_params)) =\n vectors_config.config\n {\n let actual_dim = vector_params.size as usize;\n if actual_dim != expected_dim {\n return Err(MemoryError::config(format!(\n \"Collection '{}' has dimension {} but expected {}. Please delete the collection or use a compatible embedding model.\",\n self.collection_name, actual_dim, expected_dim\n )));\n }\n }\n }\n }\n }\n }\n\n Ok(())\n }\n\n /// Convert Memory to Qdrant PointStruct\n fn memory_to_point(&self, memory: &Memory) -> PointStruct {\n let mut payload = HashMap::new();\n\n // Basic fields\n payload.insert(\"content\".to_string(), memory.content.clone().into());\n payload.insert(\n \"created_at\".to_string(),\n memory.created_at.to_rfc3339().into(),\n );\n payload.insert(\n \"updated_at\".to_string(),\n memory.updated_at.to_rfc3339().into(),\n );\n\n // Metadata fields\n if let Some(user_id) = &memory.metadata.user_id {\n payload.insert(\"user_id\".to_string(), user_id.clone().into());\n }\n if let Some(agent_id) = &memory.metadata.agent_id {\n payload.insert(\"agent_id\".to_string(), agent_id.clone().into());\n }\n if let Some(run_id) = &memory.metadata.run_id {\n payload.insert(\"run_id\".to_string(), run_id.clone().into());\n }\n if let Some(actor_id) = &memory.metadata.actor_id {\n payload.insert(\"actor_id\".to_string(), actor_id.clone().into());\n }\n if let Some(role) = &memory.metadata.role {\n payload.insert(\"role\".to_string(), role.clone().into());\n }\n\n let memory_type_str = format!(\"{:?}\", memory.metadata.memory_type);\n debug!(\"Storing memory type as string: '{}'\", memory_type_str);\n payload.insert(\"memory_type\".to_string(), memory_type_str.into());\n payload.insert(\"hash\".to_string(), memory.metadata.hash.clone().into());\n payload.insert(\n \"importance_score\".to_string(),\n memory.metadata.importance_score.into(),\n );\n\n // Store entities and topics as arrays\n if !memory.metadata.entities.is_empty() {\n let entities_json =\n serde_json::to_string(&memory.metadata.entities).unwrap_or_default();\n payload.insert(\"entities\".to_string(), entities_json.into());\n }\n\n if !memory.metadata.topics.is_empty() {\n let topics_json = serde_json::to_string(&memory.metadata.topics).unwrap_or_default();\n payload.insert(\"topics\".to_string(), topics_json.into());\n }\n\n // Custom metadata\n for (key, value) in &memory.metadata.custom {\n payload.insert(format!(\"custom_{}\", key), value.to_string().into());\n }\n\n PointStruct::new(memory.id.clone(), memory.embedding.clone(), payload)\n }\n\n /// Convert filters to Qdrant filter\n fn filters_to_qdrant_filter(&self, filters: &Filters) -> Option {\n let mut conditions = Vec::new();\n\n if let Some(user_id) = &filters.user_id {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"user_id\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Keyword(user_id.clone())),\n }),\n ..Default::default()\n })),\n });\n }\n\n if let Some(agent_id) = &filters.agent_id {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"agent_id\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Keyword(agent_id.clone())),\n }),\n ..Default::default()\n })),\n });\n }\n\n if let Some(run_id) = &filters.run_id {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"run_id\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Keyword(run_id.clone())),\n }),\n ..Default::default()\n })),\n });\n }\n\n if let Some(memory_type) = &filters.memory_type {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"memory_type\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Keyword(format!(\n \"{:?}\",\n memory_type\n ))),\n }),\n ..Default::default()\n })),\n });\n }\n\n // Filter by topics - check if any of the requested topics are present\n if let Some(topics) = &filters.topics {\n if !topics.is_empty() {\n let topic_conditions: Vec = topics\n .iter()\n .map(|topic| Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"topics\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Text(topic.clone())),\n }),\n ..Default::default()\n })),\n })\n .collect();\n\n if !topic_conditions.is_empty() {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Filter(Filter {\n should: topic_conditions,\n ..Default::default()\n })),\n });\n }\n }\n }\n\n // Filter by entities - check if any of the requested entities are present\n if let Some(entities) = &filters.entities {\n if !entities.is_empty() {\n let entity_conditions: Vec = entities\n .iter()\n .map(|entity| Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"entities\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Text(entity.clone())),\n }),\n ..Default::default()\n })),\n })\n .collect();\n\n if !entity_conditions.is_empty() {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Filter(Filter {\n should: entity_conditions,\n ..Default::default()\n })),\n });\n }\n }\n }\n\n // Filter by importance score (salience)\n if let Some(min_importance) = filters.min_importance {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"importance_score\".to_string(),\n range: Some(Range {\n gt: None,\n gte: Some(min_importance as f64),\n lt: None,\n lte: None,\n }),\n ..Default::default()\n })),\n });\n }\n\n if let Some(max_importance) = filters.max_importance {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"importance_score\".to_string(),\n range: Some(Range {\n gt: None,\n gte: None,\n lt: Some(max_importance as f64),\n lte: None,\n }),\n ..Default::default()\n })),\n });\n }\n\n // Filter by custom fields (including keywords)\n for (key, value) in &filters.custom {\n if let Some(keywords_array) = value.as_array() {\n // Handle keywords array\n let keyword_conditions: Vec = keywords_array\n .iter()\n .filter_map(|kw| kw.as_str())\n .map(|keyword| Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: format!(\"custom_{}\", key),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Text(keyword.to_string())),\n }),\n ..Default::default()\n })),\n })\n .collect();\n\n if !keyword_conditions.is_empty() {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Filter(Filter {\n should: keyword_conditions,\n ..Default::default()\n })),\n });\n }\n } else if let Some(keyword_str) = value.as_str() {\n // Handle single string value\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: format!(\"custom_{}\", key),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Text(keyword_str.to_string())),\n }),\n ..Default::default()\n })),\n });\n }\n }\n\n if conditions.is_empty() {\n None\n } else {\n Some(Filter {\n must: conditions,\n ..Default::default()\n })\n }\n }\n\n /// Convert Qdrant point to Memory\n fn point_to_memory(&self, point: &ScoredPoint) -> Result {\n let payload = &point.payload;\n\n let id = match &point.id {\n Some(PointId {\n point_id_options: Some(point_id),\n }) => match point_id {\n point_id::PointIdOptions::Uuid(uuid) => uuid.clone(),\n point_id::PointIdOptions::Num(num) => num.to_string(),\n },\n _ => return Err(MemoryError::Parse(\"Invalid point ID\".to_string())),\n };\n\n let content = payload\n .get(\"content\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .ok_or_else(|| MemoryError::Parse(\"Missing content field\".to_string()))?\n .to_string();\n\n // For now, we'll use a dummy embedding since parsing vectors is complex\n let embedding_dim = self.embedding_dim.unwrap_or(1024); // Default fallback\n let embedding = vec![0.0; embedding_dim];\n\n let created_at = payload\n .get(\"created_at\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())\n .map(|dt| dt.with_timezone(&chrono::Utc))\n .ok_or_else(|| MemoryError::Parse(\"Invalid created_at timestamp\".to_string()))?;\n\n let updated_at = payload\n .get(\"updated_at\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())\n .map(|dt| dt.with_timezone(&chrono::Utc))\n .ok_or_else(|| MemoryError::Parse(\"Invalid updated_at timestamp\".to_string()))?;\n\n let memory_type = payload\n .get(\"memory_type\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .map(|s| {\n debug!(\"Parsing memory type from string: '{}'\", s);\n crate::types::MemoryType::parse(s)\n })\n .unwrap_or_else(|| {\n warn!(\"No memory type found in payload, defaulting to Conversational\");\n crate::types::MemoryType::Conversational\n });\n\n let hash = payload\n .get(\"hash\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .map(|s| s.to_string())\n .unwrap_or_default();\n\n let mut custom = HashMap::new();\n for (key, value) in payload {\n if key.starts_with(\"custom_\") {\n let custom_key = key.strip_prefix(\"custom_\").unwrap().to_string();\n custom.insert(custom_key, serde_json::Value::String(value.to_string()));\n }\n }\n\n let metadata = MemoryMetadata {\n user_id: payload.get(\"user_id\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n agent_id: payload.get(\"agent_id\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n run_id: payload.get(\"run_id\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n actor_id: payload.get(\"actor_id\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n role: payload.get(\"role\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n memory_type,\n hash,\n importance_score: payload\n .get(\"importance_score\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::DoubleValue(d)),\n } => Some(*d),\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::IntegerValue(i)),\n } => Some(*i as f64),\n _ => None,\n })\n .map(|f| f as f32)\n .unwrap_or(0.5),\n entities: payload\n .get(\"entities\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .and_then(|s| serde_json::from_str(s).ok())\n .unwrap_or_default(),\n topics: payload\n .get(\"topics\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .and_then(|s| serde_json::from_str(s).ok())\n .unwrap_or_default(),\n custom,\n };\n\n Ok(Memory {\n id,\n content,\n embedding,\n metadata,\n created_at,\n updated_at,\n })\n }\n}\n\nimpl Clone for QdrantVectorStore {\n fn clone(&self) -> Self {\n Self {\n client: self.client.clone(),\n collection_name: self.collection_name.clone(),\n embedding_dim: self.embedding_dim,\n }\n }\n}\n\nimpl QdrantVectorStore {\n /// Get the embedding dimension\n pub fn embedding_dim(&self) -> Option {\n self.embedding_dim\n }\n\n /// Set the embedding dimension (used for auto-detection)\n pub fn set_embedding_dim(&mut self, dim: usize) {\n self.embedding_dim = Some(dim);\n }\n}\n\n#[async_trait]\nimpl VectorStore for QdrantVectorStore {\n async fn insert(&self, memory: &Memory) -> Result<()> {\n let point = self.memory_to_point(memory);\n\n let upsert_request = UpsertPoints {\n collection_name: self.collection_name.clone(),\n points: vec![point],\n ..Default::default()\n };\n\n self.client\n .upsert_points(upsert_request)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n debug!(\"Inserted memory with ID: {}\", memory.id);\n Ok(())\n }\n\n async fn search(\n &self,\n query_vector: &[f32],\n filters: &Filters,\n limit: usize,\n ) -> Result> {\n self.search_with_threshold(query_vector, filters, limit, None)\n .await\n }\n\n /// Search with optional similarity threshold filtering\n async fn search_with_threshold(\n &self,\n query_vector: &[f32],\n filters: &Filters,\n limit: usize,\n score_threshold: Option,\n ) -> Result> {\n let filter = self.filters_to_qdrant_filter(filters);\n\n let search_points = SearchPoints {\n collection_name: self.collection_name.clone(),\n vector: query_vector.to_vec(),\n limit: limit as u64,\n filter,\n with_payload: Some(true.into()),\n with_vectors: Some(true.into()),\n score_threshold: score_threshold.map(|t| t as f32), // Set score threshold if provided\n ..Default::default()\n };\n\n let response = self\n .client\n .search_points(search_points)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let mut results = Vec::new();\n for point in response.result {\n match self.point_to_memory(&point) {\n Ok(memory) => {\n results.push(ScoredMemory {\n memory,\n score: point.score,\n });\n }\n Err(e) => {\n warn!(\"Failed to parse memory from point: {}\", e);\n }\n }\n }\n\n debug!(\n \"Found {} memories for search query with threshold {:?}\",\n results.len(),\n score_threshold\n );\n Ok(results)\n }\n\n async fn update(&self, memory: &Memory) -> Result<()> {\n // For Qdrant, update is the same as insert (upsert)\n self.insert(memory).await\n }\n\n async fn delete(&self, id: &str) -> Result<()> {\n let point_id = PointId {\n point_id_options: Some(point_id::PointIdOptions::Uuid(id.to_string())),\n };\n\n let points_selector = PointsSelector {\n points_selector_one_of: Some(points_selector::PointsSelectorOneOf::Points(\n PointsIdsList {\n ids: vec![point_id],\n },\n )),\n };\n\n let delete_request = DeletePoints {\n collection_name: self.collection_name.clone(),\n points: Some(points_selector),\n ..Default::default()\n };\n\n self.client\n .delete_points(delete_request)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n debug!(\"Deleted memory with ID: {}\", id);\n Ok(())\n }\n\n async fn get(&self, id: &str) -> Result> {\n let point_id = PointId {\n point_id_options: Some(point_id::PointIdOptions::Uuid(id.to_string())),\n };\n\n let get_request = GetPoints {\n collection_name: self.collection_name.clone(),\n ids: vec![point_id],\n with_payload: Some(true.into()),\n with_vectors: Some(true.into()),\n ..Default::default()\n };\n\n let response = self\n .client\n .get_points(get_request)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n if let Some(point) = response.result.first() {\n // Convert RetrievedPoint to ScoredPoint for parsing\n let scored_point = ScoredPoint {\n id: point.id.clone(),\n payload: point.payload.clone(),\n score: 1.0, // Not relevant for get operation\n vectors: point.vectors.clone(),\n shard_key: None,\n order_value: None,\n version: 0,\n };\n\n match self.point_to_memory(&scored_point) {\n Ok(memory) => Ok(Some(memory)),\n Err(e) => {\n error!(\"Failed to parse memory from point: {}\", e);\n Err(e)\n }\n }\n } else {\n Ok(None)\n }\n }\n\n async fn list(&self, filters: &Filters, limit: Option) -> Result> {\n let filter = self.filters_to_qdrant_filter(filters);\n let limit = limit.unwrap_or(100) as u32;\n\n let scroll_points = ScrollPoints {\n collection_name: self.collection_name.clone(),\n filter,\n limit: Some(limit),\n with_payload: Some(true.into()),\n with_vectors: Some(true.into()),\n ..Default::default()\n };\n\n let response = self\n .client\n .scroll(scroll_points)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let mut results = Vec::new();\n for point in response.result {\n // Convert RetrievedPoint to ScoredPoint for parsing\n let scored_point = ScoredPoint {\n id: point.id.clone(),\n payload: point.payload.clone(),\n score: 1.0, // Not relevant for list operation\n vectors: point.vectors.clone(),\n shard_key: None,\n order_value: None,\n version: 0,\n };\n\n match self.point_to_memory(&scored_point) {\n Ok(memory) => results.push(memory),\n Err(e) => {\n warn!(\"Failed to parse memory from point: {}\", e);\n }\n }\n }\n\n debug!(\"Listed {} memories\", results.len());\n Ok(results)\n }\n\n async fn health_check(&self) -> Result {\n match self.client.health_check().await {\n Ok(_) => Ok(true),\n Err(e) => {\n error!(\"Qdrant health check failed: {}\", e);\n Ok(false)\n }\n }\n }\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 14.0, - "lines_of_code": 216, + "cyclomatic_complexity": 75.0, + "lines_of_code": 813, "number_of_classes": 1, - "number_of_functions": 8 + "number_of_functions": 20 }, "dependencies": [ { - "dependency_type": "std", + "dependency_type": "library", + "is_external": true, + "line_number": null, + "name": "async_trait", + "path": null, + "version": null + }, + { + "dependency_type": "library", + "is_external": true, + "line_number": null, + "name": "qdrant_client", + "path": null, + "version": null + }, + { + "dependency_type": "library", + "is_external": true, + "line_number": null, + "name": "tracing", + "path": null, + "version": null + }, + { + "dependency_type": "internal", "is_external": false, "line_number": null, - "name": "std::collections::HashMap", - "path": "std::collections::HashMap", + "name": "crate::config::QdrantConfig", + "path": "cortex-mem-core/src/config/mod.rs", "version": null }, { - "dependency_type": "crate", - "is_external": true, + "dependency_type": "internal", + "is_external": false, "line_number": null, - "name": "serde", - "path": "serde", + "name": "crate::error::MemoryError", + "path": "cortex-mem-core/src/error/mod.rs", "version": null }, { - "dependency_type": "crate", - "is_external": true, + "dependency_type": "internal", + "is_external": false, "line_number": null, - "name": "tracing", - "path": "tracing", + "name": "crate::types::Filters", + "path": "cortex-mem-core/src/types/mod.rs", "version": null }, { - "dependency_type": "crate", - "is_external": true, + "dependency_type": "internal", + "is_external": false, "line_number": null, - "name": "regex", - "path": "regex", + "name": "crate::types::Memory", + "path": "cortex-mem-core/src/types/mod.rs", + "version": null + }, + { + "dependency_type": "internal", + "is_external": false, + "line_number": null, + "name": "crate::types::MemoryMetadata", + "path": "cortex-mem-core/src/types/mod.rs", + "version": null + }, + { + "dependency_type": "internal", + "is_external": false, + "line_number": null, + "name": "crate::types::ScoredMemory", + "path": "cortex-mem-core/src/types/mod.rs", + "version": null + }, + { + "dependency_type": "internal", + "is_external": false, + "line_number": null, + "name": "crate::vector_store::VectorStore", + "path": "cortex-mem-core/src/vector_store/mod.rs", + "version": null + }, + { + "dependency_type": "internal", + "is_external": false, + "line_number": null, + "name": "crate::llm::LLMClient", + "path": "cortex-mem-core/src/llm/mod.rs", "version": null } ], - "detailed_description": "This utility module provides a collection of pure helper functions for preprocessing and transforming text and message data within a memory processing system. Key functionalities include: (1) removing Markdown-style code blocks and thinking markers from text; (2) extracting JSON content from code fences; (3) detecting natural language based on Unicode ranges; (4) formatting conversation messages into a string representation; (5) sanitizing text for safe use in Cypher database queries by replacing special characters; and (6) filtering message histories by role(s). The functions are designed to be stateless and reusable across different components of the system, particularly in preprocessing pipelines for memory storage and retrieval.", + "detailed_description": "This component implements a Qdrant-based vector store for managing memory data in a vector database. It provides full CRUD operations for memory records, including insertion, retrieval, updating, deletion, and similarity-based search. The implementation handles schema management by ensuring the target collection exists with correct dimensionality, supports rich filtering capabilities based on user, agent, run, memory type, topics, entities, importance score, and custom metadata. It converts between domain Memory objects and Qdrant PointStruct format, handling payload serialization/deserialization with proper type mapping. The component also provides health checking functionality and supports both manual and LLM-client-assisted embedding dimension detection.", "interfaces": [ { - "description": "Represents detected language information with code, name, and confidence score", - "interface_type": "struct", - "name": "LanguageInfo", + "description": "Main trait implementation that defines the vector store interface for memory operations", + "interface_type": "trait", + "name": "VectorStore", "parameters": [], "return_type": null, "visibility": "public" }, { - "description": "Removes code blocks and thinking patterns from text", + "description": "Create a new Qdrant vector store instance", "interface_type": "function", - "name": "remove_code_blocks", + "name": "new", "parameters": [ { - "description": "Input text that may contain code blocks", + "description": "Configuration for connecting to Qdrant", "is_optional": false, - "name": "content", - "param_type": "&str" + "name": "config", + "param_type": "&QdrantConfig" } ], - "return_type": "String", + "return_type": "Result", "visibility": "public" }, { - "description": "Extracts JSON content from text, removing surrounding code fences", + "description": "Create a new Qdrant vector store with auto-detected embedding dimension", "interface_type": "function", - "name": "extract_json", + "name": "new_with_llm_client", "parameters": [ { - "description": "Input text that may contain JSON in code blocks", + "description": "Configuration for connecting to Qdrant", "is_optional": false, - "name": "text", - "param_type": "&str" + "name": "config", + "param_type": "&QdrantConfig" + }, + { + "description": "LLM client for embedding dimension auto-detection", + "is_optional": false, + "name": "llm_client", + "param_type": "&dyn crate::llm::LLMClient" } ], - "return_type": "String", + "return_type": "Result", "visibility": "public" }, { - "description": "Detects the language of text based on Unicode character ranges", + "description": "Insert a memory record into the vector store", "interface_type": "function", - "name": "detect_language", + "name": "insert", "parameters": [ { - "description": "Input text to analyze", + "description": "Memory object to insert", "is_optional": false, - "name": "text", - "param_type": "&str" + "name": "memory", + "param_type": "&Memory" } ], - "return_type": "LanguageInfo", + "return_type": "Result<()>", "visibility": "public" }, { - "description": "Formats conversation messages into a string representation", + "description": "Search for similar memories based on query vector and filters", "interface_type": "function", - "name": "parse_messages", + "name": "search", "parameters": [ { - "description": "Slice of message objects", + "description": "Query embedding vector", "is_optional": false, - "name": "messages", - "param_type": "&[crate::types::Message]" + "name": "query_vector", + "param_type": "&[f32]" + }, + { + "description": "Filter criteria", + "is_optional": false, + "name": "filters", + "param_type": "&Filters" + }, + { + "description": "Maximum number of results", + "is_optional": false, + "name": "limit", + "param_type": "usize" } ], - "return_type": "String", + "return_type": "Result>", "visibility": "public" }, { - "description": "Sanitizes text for safe use in Cypher database queries", + "description": "Search for similar memories with optional similarity threshold", "interface_type": "function", - "name": "sanitize_for_cypher", + "name": "search_with_threshold", "parameters": [ { - "description": "Input text to sanitize", + "description": "Query embedding vector", "is_optional": false, - "name": "text", - "param_type": "&str" + "name": "query_vector", + "param_type": "&[f32]" + }, + { + "description": "Filter criteria", + "is_optional": false, + "name": "filters", + "param_type": "&Filters" + }, + { + "description": "Maximum number of results", + "is_optional": false, + "name": "limit", + "param_type": "usize" + }, + { + "description": "Minimum similarity score threshold", + "is_optional": true, + "name": "score_threshold", + "param_type": "Option" } ], - "return_type": "String", + "return_type": "Result>", "visibility": "public" }, { - "description": "Filters messages by a single role", + "description": "Update a memory record (upsert operation)", "interface_type": "function", - "name": "filter_messages_by_role", + "name": "update", "parameters": [ { - "description": "Slice of message objects", + "description": "Memory object to update", "is_optional": false, - "name": "messages", - "param_type": "&[crate::types::Message]" - }, + "name": "memory", + "param_type": "&Memory" + } + ], + "return_type": "Result<()>", + "visibility": "public" + }, + { + "description": "Delete a memory record by ID", + "interface_type": "function", + "name": "delete", + "parameters": [ { - "description": "Role to filter by", + "description": "ID of memory to delete", "is_optional": false, - "name": "role", + "name": "id", "param_type": "&str" } ], - "return_type": "Vec", + "return_type": "Result<()>", "visibility": "public" }, { - "description": "Filters messages by multiple roles", + "description": "Retrieve a memory record by ID", "interface_type": "function", - "name": "filter_messages_by_roles", + "name": "get", "parameters": [ { - "description": "Slice of message objects", + "description": "ID of memory to retrieve", "is_optional": false, - "name": "messages", - "param_type": "&[crate::types::Message]" + "name": "id", + "param_type": "&str" + } + ], + "return_type": "Result>", + "visibility": "public" + }, + { + "description": "List memory records with optional filtering and limiting", + "interface_type": "function", + "name": "list", + "parameters": [ + { + "description": "Filter criteria", + "is_optional": false, + "name": "filters", + "param_type": "&Filters" }, { - "description": "Slice of roles to filter by", + "description": "Maximum number of results", + "is_optional": true, + "name": "limit", + "param_type": "Option" + } + ], + "return_type": "Result>", + "visibility": "public" + }, + { + "description": "Check the health status of the Qdrant connection", + "interface_type": "function", + "name": "health_check", + "parameters": [], + "return_type": "Result", + "visibility": "public" + }, + { + "description": "Get the current embedding dimension", + "interface_type": "function", + "name": "embedding_dim", + "parameters": [], + "return_type": "Option", + "visibility": "public" + }, + { + "description": "Set the embedding dimension (for auto-detection)", + "interface_type": "function", + "name": "set_embedding_dim", + "parameters": [ + { + "description": "New embedding dimension to set", "is_optional": false, - "name": "roles", - "param_type": "&[&str]" + "name": "dim", + "param_type": "usize" } ], - "return_type": "Vec", + "return_type": null, "visibility": "public" } ], "responsibilities": [ - "Provide text preprocessing utilities for code block and thinking pattern removal", - "Extract and clean JSON content from formatted text", - "Detect natural language from text based on Unicode character ranges", - "Format and filter conversation message histories", - "Sanitize text for safe use in Cypher database queries" + "Manage connection and interaction with Qdrant vector database", + "Implement CRUD operations for memory data with proper error handling", + "Handle schema management including collection creation and dimension validation", + "Convert between domain Memory objects and Qdrant storage format", + "Support complex filtering and similarity search operations" ] }, { "code_dossier": { "code_purpose": "types", - "description": "Defines a comprehensive error enumeration and associated result type for memory-related operations in the system.", - "file_path": "cortex-mem-core/src/error.rs", - "functions": [ - "config", - "validation", - "embedding", - "parse" - ], + "description": "Defines the VectorStore trait and re-exports QdrantVectorStore implementation for managing vector-based memory storage operations.", + "file_path": "cortex-mem-core/src/vector_store/mod.rs", + "functions": [], "importance_score": 0.8, "interfaces": [ - "MemoryError", - "Result" + "VectorStore::insert", + "VectorStore::search", + "VectorStore::search_with_threshold", + "VectorStore::update", + "VectorStore::delete", + "VectorStore::get", + "VectorStore::list", + "VectorStore::health_check" ], - "name": "error.rs", - "source_summary": "use thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum MemoryError {\n #[error(\"Vector store error: {0}\")]\n VectorStore(#[from] qdrant_client::QdrantError),\n \n #[error(\"LLM error: {0}\")]\n LLM(String),\n \n #[error(\"Serialization error: {0}\")]\n Serialization(#[from] serde_json::Error),\n \n #[error(\"HTTP client error: {0}\")]\n Http(#[from] reqwest::Error),\n \n #[error(\"Memory not found: {id}\")]\n NotFound { id: String },\n \n #[error(\"Invalid memory action: {action}\")]\n InvalidAction { action: String },\n \n #[error(\"Configuration error: {0}\")]\n Config(String),\n \n #[error(\"Validation error: {0}\")]\n Validation(String),\n \n #[error(\"Embedding error: {0}\")]\n Embedding(String),\n \n #[error(\"Parse error: {0}\")]\n Parse(String),\n}\n\npub type Result = std::result::Result;\n\nimpl MemoryError {\n pub fn config>(msg: S) -> Self {\n Self::Config(msg.into())\n }\n \n pub fn validation>(msg: S) -> Self {\n Self::Validation(msg.into())\n }\n \n pub fn embedding>(msg: S) -> Self {\n Self::Embedding(msg.into())\n }\n \n pub fn parse>(msg: S) -> Self {\n Self::Parse(msg.into())\n }\n}" + "name": "mod.rs", + "source_summary": "pub mod qdrant;\n\nuse crate::{\n error::Result,\n types::{Filters, Memory, ScoredMemory},\n};\nuse async_trait::async_trait;\n\npub use qdrant::QdrantVectorStore;\n\n/// Trait for vector store operations\n#[async_trait]\npub trait VectorStore: Send + Sync + dyn_clone::DynClone {\n /// Insert a memory into the vector store\n async fn insert(&self, memory: &Memory) -> Result<()>;\n\n /// Search for similar memories\n async fn search(\n &self,\n query_vector: &[f32],\n filters: &Filters,\n limit: usize,\n ) -> Result>;\n\n /// Search for similar memories with similarity threshold\n async fn search_with_threshold(\n &self,\n query_vector: &[f32],\n filters: &Filters,\n limit: usize,\n score_threshold: Option,\n ) -> Result>;\n\n /// Update an existing memory\n async fn update(&self, memory: &Memory) -> Result<()>;\n\n /// Delete a memory by ID\n async fn delete(&self, id: &str) -> Result<()>;\n\n /// Get a memory by ID\n async fn get(&self, id: &str) -> Result>;\n\n /// List all memories with optional filters\n async fn list(&self, filters: &Filters, limit: Option) -> Result>;\n\n /// Check if the vector store is healthy\n async fn health_check(&self) -> Result;\n}\n\ndyn_clone::clone_trait_object!(VectorStore);\n" }, "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 54, + "cyclomatic_complexity": 5.0, + "lines_of_code": 50, "number_of_classes": 0, - "number_of_functions": 4 + "number_of_functions": 8 }, "dependencies": [ - { - "dependency_type": "library", - "is_external": true, - "line_number": 1, - "name": "thiserror", - "path": null, - "version": null - }, { "dependency_type": "library", "is_external": true, "line_number": 5, - "name": "qdrant_client", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": 12, - "name": "serde_json", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": 15, - "name": "reqwest", + "name": "async_trait", "path": null, "version": null } ], - "detailed_description": "This component defines a centralized error handling mechanism for memory-related operations in the Cortex-MEM system. The `MemoryError` enum encapsulates various error conditions including vector store failures, LLM communication issues, serialization problems, HTTP client errors, missing memory entries, invalid actions, configuration issues, validation failures, embedding processing errors, and parsing problems. Each variant includes appropriate error source propagation using `#[from]` where applicable. The component also defines a type alias `Result` that uses `MemoryError` as the error type, promoting consistent error handling across the codebase. Additional helper constructor methods are provided for common error types to simplify error creation with custom messages.", + "detailed_description": "This component defines a comprehensive trait interface `VectorStore` that abstracts vector database operations for memory management in an AI/ML context. It enables insertion, retrieval, search (with and without thresholds), update, deletion, listing, and health checking of memory entries. The trait is designed to be object-safe using `dyn_clone::DynClone`, allowing dynamic dispatch while maintaining ownership semantics. It leverages async/await for non-blocking I/O operations and integrates with the crate's error handling and data model via `Result`, `Memory`, and `ScoredMemory` types. The module also re-exports the Qdrant-specific implementation, indicating it as the primary or default backend.", "interfaces": [ { - "description": "An enumeration of all possible error types that can occur in memory operations, using thiserror for automatic Display implementation and error source chaining.", - "interface_type": "enum", - "name": "MemoryError", + "description": "Core trait defining vector store operations for memory management", + "interface_type": "trait", + "name": "VectorStore", "parameters": [], "return_type": null, "visibility": "public" }, { - "description": "A type alias for Result that uses MemoryError as the error type, simplifying function signatures across the codebase.", - "interface_type": "type_alias", - "name": "Result", - "parameters": [], - "return_type": "std::result::Result", - "visibility": "public" - }, - { - "description": "Creates a Configuration error variant with the provided message.", - "interface_type": "function", - "name": "config", + "description": "Inserts a new memory into the vector store", + "interface_type": "method", + "name": "VectorStore::insert", "parameters": [ { - "description": "Error message that can be converted into a String", + "description": "Reference to the memory to insert", "is_optional": false, - "name": "msg", - "param_type": "S" + "name": "memory", + "param_type": "&Memory" } ], - "return_type": "MemoryError", + "return_type": "Result<()>", "visibility": "public" }, { - "description": "Creates a Validation error variant with the provided message.", - "interface_type": "function", - "name": "validation", + "description": "Searches for similar memories based on query vector and filters", + "interface_type": "method", + "name": "VectorStore::search", "parameters": [ { - "description": "Error message that can be converted into a String", + "description": "The query embedding vector", "is_optional": false, - "name": "msg", - "param_type": "S" + "name": "query_vector", + "param_type": "&[f32]" + }, + { + "description": "Filter criteria for the search", + "is_optional": false, + "name": "filters", + "param_type": "&Filters" + }, + { + "description": "Maximum number of results to return", + "is_optional": false, + "name": "limit", + "param_type": "usize" } ], - "return_type": "MemoryError", + "return_type": "Result>", "visibility": "public" }, { - "description": "Creates an Embedding error variant with the provided message.", - "interface_type": "function", - "name": "embedding", + "description": "Searches for similar memories with an optional similarity score threshold", + "interface_type": "method", + "name": "VectorStore::search_with_threshold", "parameters": [ { - "description": "Error message that can be converted into a String", + "description": "The query embedding vector", "is_optional": false, - "name": "msg", - "param_type": "S" + "name": "query_vector", + "param_type": "&[f32]" + }, + { + "description": "Filter criteria for the search", + "is_optional": false, + "name": "filters", + "param_type": "&Filters" + }, + { + "description": "Maximum number of results to return", + "is_optional": false, + "name": "limit", + "param_type": "usize" + }, + { + "description": "Minimum similarity score required", + "is_optional": true, + "name": "score_threshold", + "param_type": "Option" } ], - "return_type": "MemoryError", + "return_type": "Result>", "visibility": "public" }, { - "description": "Creates a Parse error variant with the provided message.", - "interface_type": "function", - "name": "parse", + "description": "Updates an existing memory in the vector store", + "interface_type": "method", + "name": "VectorStore::update", "parameters": [ { - "description": "Error message that can be converted into a String", + "description": "Reference to the updated memory", "is_optional": false, - "name": "msg", - "param_type": "S" + "name": "memory", + "param_type": "&Memory" } ], - "return_type": "MemoryError", + "return_type": "Result<()>", "visibility": "public" - } - ], - "responsibilities": [ - "Defines a unified error type for all memory-related operations in the system", - "Provides automatic error conversion from external dependencies such as Qdrant, serde_json, and reqwest", - "Offers convenient constructor methods for common error types with custom messages", - "Establishes a standard result type (Result) for consistent return value handling", - "Enables comprehensive error reporting with contextual information for debugging" - ] - }, - { - "code_dossier": { - "code_purpose": "tool", - "description": "Initializes and configures a logging system using tracing_subscriber with file output based on provided configuration, supporting timestamped log files and configurable log levels.", - "file_path": "cortex-mem-core/src/logging.rs", - "functions": [ - "init_logging" - ], - "importance_score": 0.8, - "interfaces": [ - "init_logging" - ], - "name": "logging.rs", - "source_summary": "use anyhow::Result;\nuse chrono::{DateTime, Local};\nuse std::fs;\nuse std::path::Path;\nuse tracing::info;\nuse tracing_subscriber::{\n EnvFilter, Layer, fmt, fmt::time::ChronoLocal, layer::SubscriberExt, util::SubscriberInitExt,\n};\n\n/// 初始化日志系统\npub fn init_logging(config: &cortex_mem_config::LoggingConfig) -> Result<()> {\n if !config.enabled {\n // 如果日志未启用,不设置任何tracing层\n tracing_subscriber::registry().try_init().ok(); // 避免重复初始化错误\n return Ok(());\n }\n\n // 创建日志目录(如果不存在)\n fs::create_dir_all(&config.log_directory)?;\n\n // 生成带时间戳的日志文件名\n let local_time: DateTime = Local::now();\n let log_file_name = format!(\"cortex-memo-{}.log\", local_time.format(\"%Y-%m-%d-%H-%M-%S\"));\n let log_file_path = Path::new(&config.log_directory).join(log_file_name);\n\n // 创建文件写入器\n let file_writer = std::fs::File::create(&log_file_path)?;\n\n // 根据配置的日志级别设置过滤器\n let level_filter = match config.level.to_lowercase().as_str() {\n \"error\" => \"error\",\n \"warn\" => \"warn\",\n \"info\" => \"info\",\n \"debug\" => \"debug\",\n \"trace\" => \"trace\",\n _ => \"info\", // 默认为info级别\n };\n\n // 只配置文件输出,不配置控制台输出\n let file_filter =\n EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level_filter));\n let file_layer = fmt::layer()\n .with_target(false)\n .with_ansi(false)\n .with_writer(std::sync::Mutex::new(file_writer))\n .with_timer(ChronoLocal::new(\"%Y-%m-%d %H:%M:%S%.3f\".into()))\n .with_filter(file_filter);\n\n // 初始化tracing订阅者,只添加文件层,不添加控制台层\n tracing_subscriber::registry().with(file_layer).try_init()?;\n\n info!(\"Logging initialized. Log file: {}\", log_file_path.display());\n Ok(())\n}\n" - }, - "complexity_metrics": { - "cyclomatic_complexity": 3.0, - "lines_of_code": 54, - "number_of_classes": 0, - "number_of_functions": 1 - }, - "dependencies": [ - { - "dependency_type": "use", - "is_external": true, - "line_number": 1, - "name": "anyhow", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": 2, - "name": "chrono", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": false, - "line_number": 3, - "name": "std::fs", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": false, - "line_number": 4, - "name": "std::path::Path", - "path": null, - "version": null }, { - "dependency_type": "use", - "is_external": true, - "line_number": 5, - "name": "tracing", - "path": null, - "version": null + "description": "Deletes a memory entry by its ID", + "interface_type": "method", + "name": "VectorStore::delete", + "parameters": [ + { + "description": "ID of the memory to delete", + "is_optional": false, + "name": "id", + "param_type": "&str" + } + ], + "return_type": "Result<()>", + "visibility": "public" }, { - "dependency_type": "use", - "is_external": true, - "line_number": 6, - "name": "tracing_subscriber", - "path": null, - "version": null - } - ], - "detailed_description": "The component is responsible for initializing a structured logging system using the `tracing` and `tracing_subscriber` crates. It reads logging configuration including whether logging is enabled, the log level, and the output directory. If logging is enabled, it creates the log directory if it does not exist, generates a timestamped log file name, creates the log file, and sets up a tracing subscriber that writes formatted logs to the file. The log format excludes targets and ANSI colors, uses local time formatting, and applies a filter based on the configured log level. When disabled, it performs a minimal initialization to prevent other components from failing due to missing subscribers. The function logs a confirmation message upon successful initialization.", - "interfaces": [ + "description": "Retrieves a memory entry by its ID", + "interface_type": "method", + "name": "VectorStore::get", + "parameters": [ + { + "description": "ID of the memory to retrieve", + "is_optional": false, + "name": "id", + "param_type": "&str" + } + ], + "return_type": "Result>", + "visibility": "public" + }, { - "description": "Initializes the logging system. Returns Ok if setup succeeds or if logging is disabled; returns Err on filesystem or initialization errors.", - "interface_type": "function", - "name": "init_logging", + "description": "Lists all memories with optional filtering and limiting", + "interface_type": "method", + "name": "VectorStore::list", "parameters": [ { - "description": "Reference to logging configuration containing enabled flag, log level, and log directory path", + "description": "Filter criteria for listing", "is_optional": false, - "name": "config", - "param_type": "&cortex_mem_config::LoggingConfig" + "name": "filters", + "param_type": "&Filters" + }, + { + "description": "Maximum number of results to return", + "is_optional": true, + "name": "limit", + "param_type": "Option" } ], - "return_type": "Result<()>", - "visibility": "pub" + "return_type": "Result>", + "visibility": "public" + }, + { + "description": "Checks if the vector store backend is healthy and responsive", + "interface_type": "method", + "name": "VectorStore::health_check", + "parameters": [], + "return_type": "Result", + "visibility": "public" } ], "responsibilities": [ - "Initialize the tracing logging system based on runtime configuration", - "Create log directory and timestamped log file for persistent logging", - "Configure tracing subscriber with file output, time formatting, and level filtering", - "Support disabling logging while safely avoiding double-initialization errors", - "Provide observability into system behavior through structured file-based logs" + "Define a standardized interface for vector-based memory storage operations", + "Enable polymorphic behavior through trait object cloning with dyn_clone", + "Provide asynchronous methods for non-blocking interaction with vector databases", + "Support similarity search with filtering and thresholding capabilities", + "Expose health check functionality for monitoring storage backend status" ] }, { "code_dossier": { - "code_purpose": "dao", - "description": "Qdrant vector store implementation for memory data persistence and retrieval.", - "file_path": "cortex-mem-core/src/vector_store/qdrant.rs", + "code_purpose": "types", + "description": "Defines common data structures and enums for memory operation payloads, responses, and parameter extraction in a memory management system.", + "file_path": "cortex-mem-tools/src/types.rs", "functions": [ - "new", - "new_with_llm_client", - "ensure_collection", - "verify_collection_dimension", - "memory_to_point", - "filters_to_qdrant_filter", - "point_to_memory", - "embedding_dim", - "set_embedding_dim", - "insert", - "search", - "search_with_threshold", - "update", - "delete", - "get", - "list", - "health_check" + "MemoryOperationResponse::success", + "MemoryOperationResponse::success_with_data", + "MemoryOperationResponse::error", + "QueryParams::from_payload", + "StoreParams::from_payload", + "FilterParams::from_payload" ], "importance_score": 0.8, "interfaces": [ - "VectorStore" + "MemoryOperationPayload", + "MemoryOperationType", + "MemoryOperationResponse", + "QueryParams", + "StoreParams", + "FilterParams" ], - "name": "qdrant.rs", - "source_summary": "use async_trait::async_trait;\nuse qdrant_client::{\n Qdrant,\n qdrant::{\n Condition, CreateCollection, DeletePoints, Distance, FieldCondition, Filter, GetPoints,\n Match, PointId, PointStruct, PointsIdsList, PointsSelector, Range, ScoredPoint,\n ScrollPoints, SearchPoints, UpsertPoints, VectorParams, VectorsConfig, condition, r#match,\n point_id, points_selector, vectors_config,\n },\n};\nuse std::collections::HashMap;\nuse tracing::{debug, error, info, warn};\n\nuse crate::{\n config::QdrantConfig,\n error::{MemoryError, Result},\n types::{Filters, Memory, MemoryMetadata, ScoredMemory},\n vector_store::VectorStore,\n};\n\n/// Qdrant vector store implementation\npub struct QdrantVectorStore {\n client: Qdrant,\n collection_name: String,\n embedding_dim: Option,\n}\n\nimpl QdrantVectorStore {\n /// Create a new Qdrant vector store\n pub async fn new(config: &QdrantConfig) -> Result {\n let client = Qdrant::from_url(&config.url)\n .build()\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let store = Self {\n client,\n collection_name: config.collection_name.clone(),\n embedding_dim: config.embedding_dim,\n };\n\n Ok(store)\n }\n\n /// Create a new Qdrant vector store with auto-detected embedding dimension\n pub async fn new_with_llm_client(\n config: &QdrantConfig,\n llm_client: &dyn crate::llm::LLMClient,\n ) -> Result {\n let client = Qdrant::from_url(&config.url)\n .build()\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let mut store = Self {\n client,\n collection_name: config.collection_name.clone(),\n embedding_dim: config.embedding_dim,\n };\n\n // Auto-detect embedding dimension if not specified\n if store.embedding_dim.is_none() {\n info!(\"Auto-detecting embedding dimension...\");\n let test_embedding = llm_client.embed(\"test\").await?;\n let detected_dim = test_embedding.len();\n info!(\"Detected embedding dimension: {}\", detected_dim);\n store.embedding_dim = Some(detected_dim);\n }\n\n // Ensure collection exists with correct dimension\n store.ensure_collection().await?;\n\n Ok(store)\n }\n\n /// Ensure the collection exists, create if not\n async fn ensure_collection(&self) -> Result<()> {\n let collections = self\n .client\n .list_collections()\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let collection_exists = collections\n .collections\n .iter()\n .any(|c| c.name == self.collection_name);\n\n if !collection_exists {\n let embedding_dim = self.embedding_dim.ok_or_else(|| {\n MemoryError::config(\n \"Embedding dimension not set. Use new_with_llm_client for auto-detection.\",\n )\n })?;\n\n info!(\n \"Creating collection: {} with dimension: {}\",\n self.collection_name, embedding_dim\n );\n\n let vectors_config = VectorsConfig {\n config: Some(vectors_config::Config::Params(VectorParams {\n size: embedding_dim as u64,\n distance: Distance::Cosine.into(),\n ..Default::default()\n })),\n };\n\n self.client\n .create_collection(CreateCollection {\n collection_name: self.collection_name.clone(),\n vectors_config: Some(vectors_config),\n ..Default::default()\n })\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n info!(\"Collection created successfully: {}\", self.collection_name);\n } else {\n debug!(\"Collection already exists: {}\", self.collection_name);\n\n // Verify dimension compatibility if collection exists\n if let Some(expected_dim) = self.embedding_dim {\n if let Err(e) = self.verify_collection_dimension(expected_dim).await {\n warn!(\"Collection dimension verification failed: {}\", e);\n }\n }\n }\n\n Ok(())\n }\n\n /// Verify that the existing collection has the expected dimension\n async fn verify_collection_dimension(&self, expected_dim: usize) -> Result<()> {\n let collection_info = self\n .client\n .collection_info(&self.collection_name)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n if let Some(collection_config) = collection_info.result {\n if let Some(config) = collection_config.config {\n if let Some(params) = config.params {\n if let Some(vectors_config) = params.vectors_config {\n if let Some(vectors_config::Config::Params(vector_params)) =\n vectors_config.config\n {\n let actual_dim = vector_params.size as usize;\n if actual_dim != expected_dim {\n return Err(MemoryError::config(format!(\n \"Collection '{}' has dimension {} but expected {}. Please delete the collection or use a compatible embedding model.\",\n self.collection_name, actual_dim, expected_dim\n )));\n }\n }\n }\n }\n }\n }\n\n Ok(())\n }\n\n /// Convert Memory to Qdrant PointStruct\n fn memory_to_point(&self, memory: &Memory) -> PointStruct {\n let mut payload = HashMap::new();\n\n // Basic fields\n payload.insert(\"content\".to_string(), memory.content.clone().into());\n payload.insert(\n \"created_at\".to_string(),\n memory.created_at.to_rfc3339().into(),\n );\n payload.insert(\n \"updated_at\".to_string(),\n memory.updated_at.to_rfc3339().into(),\n );\n\n // Metadata fields\n if let Some(user_id) = &memory.metadata.user_id {\n payload.insert(\"user_id\".to_string(), user_id.clone().into());\n }\n if let Some(agent_id) = &memory.metadata.agent_id {\n payload.insert(\"agent_id\".to_string(), agent_id.clone().into());\n }\n if let Some(run_id) = &memory.metadata.run_id {\n payload.insert(\"run_id\".to_string(), run_id.clone().into());\n }\n if let Some(actor_id) = &memory.metadata.actor_id {\n payload.insert(\"actor_id\".to_string(), actor_id.clone().into());\n }\n if let Some(role) = &memory.metadata.role {\n payload.insert(\"role\".to_string(), role.clone().into());\n }\n\n let memory_type_str = format!(\"{:?}\", memory.metadata.memory_type);\n debug!(\"Storing memory type as string: '{}'\", memory_type_str);\n payload.insert(\"memory_type\".to_string(), memory_type_str.into());\n payload.insert(\"hash\".to_string(), memory.metadata.hash.clone().into());\n payload.insert(\n \"importance_score\".to_string(),\n memory.metadata.importance_score.into(),\n );\n\n // Store entities and topics as arrays\n if !memory.metadata.entities.is_empty() {\n let entities_json =\n serde_json::to_string(&memory.metadata.entities).unwrap_or_default();\n payload.insert(\"entities\".to_string(), entities_json.into());\n }\n\n if !memory.metadata.topics.is_empty() {\n let topics_json = serde_json::to_string(&memory.metadata.topics).unwrap_or_default();\n payload.insert(\"topics\".to_string(), topics_json.into());\n }\n\n // Custom metadata\n for (key, value) in &memory.metadata.custom {\n payload.insert(format!(\"custom_{}\", key), value.to_string().into());\n }\n\n PointStruct::new(memory.id.clone(), memory.embedding.clone(), payload)\n }\n\n /// Convert filters to Qdrant filter\n fn filters_to_qdrant_filter(&self, filters: &Filters) -> Option {\n let mut conditions = Vec::new();\n\n if let Some(user_id) = &filters.user_id {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"user_id\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Keyword(user_id.clone())),\n }),\n ..Default::default()\n })),\n });\n }\n\n if let Some(agent_id) = &filters.agent_id {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"agent_id\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Keyword(agent_id.clone())),\n }),\n ..Default::default()\n })),\n });\n }\n\n if let Some(run_id) = &filters.run_id {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"run_id\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Keyword(run_id.clone())),\n }),\n ..Default::default()\n })),\n });\n }\n\n if let Some(memory_type) = &filters.memory_type {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"memory_type\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Keyword(format!(\n \"{:?}\",\n memory_type\n ))),\n }),\n ..Default::default()\n })),\n });\n }\n\n // Filter by topics - check if any of the requested topics are present\n if let Some(topics) = &filters.topics {\n if !topics.is_empty() {\n let topic_conditions: Vec = topics\n .iter()\n .map(|topic| Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"topics\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Text(topic.clone())),\n }),\n ..Default::default()\n })),\n })\n .collect();\n\n if !topic_conditions.is_empty() {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Filter(Filter {\n should: topic_conditions,\n ..Default::default()\n })),\n });\n }\n }\n }\n\n // Filter by entities - check if any of the requested entities are present\n if let Some(entities) = &filters.entities {\n if !entities.is_empty() {\n let entity_conditions: Vec = entities\n .iter()\n .map(|entity| Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"entities\".to_string(),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Text(entity.clone())),\n }),\n ..Default::default()\n })),\n })\n .collect();\n\n if !entity_conditions.is_empty() {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Filter(Filter {\n should: entity_conditions,\n ..Default::default()\n })),\n });\n }\n }\n }\n\n // Filter by importance score (salience)\n if let Some(min_importance) = filters.min_importance {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"importance_score\".to_string(),\n range: Some(Range {\n gt: None,\n gte: Some(min_importance as f64),\n lt: None,\n lte: None,\n }),\n ..Default::default()\n })),\n });\n }\n\n if let Some(max_importance) = filters.max_importance {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: \"importance_score\".to_string(),\n range: Some(Range {\n gt: None,\n gte: None,\n lt: Some(max_importance as f64),\n lte: None,\n }),\n ..Default::default()\n })),\n });\n }\n\n // Filter by custom fields (including keywords)\n for (key, value) in &filters.custom {\n if let Some(keywords_array) = value.as_array() {\n // Handle keywords array\n let keyword_conditions: Vec = keywords_array\n .iter()\n .filter_map(|kw| kw.as_str())\n .map(|keyword| Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: format!(\"custom_{}\", key),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Text(keyword.to_string())),\n }),\n ..Default::default()\n })),\n })\n .collect();\n\n if !keyword_conditions.is_empty() {\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Filter(Filter {\n should: keyword_conditions,\n ..Default::default()\n })),\n });\n }\n } else if let Some(keyword_str) = value.as_str() {\n // Handle single string value\n conditions.push(Condition {\n condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition {\n key: format!(\"custom_{}\", key),\n r#match: Some(Match {\n match_value: Some(r#match::MatchValue::Text(keyword_str.to_string())),\n }),\n ..Default::default()\n })),\n });\n }\n }\n\n if conditions.is_empty() {\n None\n } else {\n Some(Filter {\n must: conditions,\n ..Default::default()\n })\n }\n }\n\n /// Convert Qdrant point to Memory\n fn point_to_memory(&self, point: &ScoredPoint) -> Result {\n let payload = &point.payload;\n\n let id = match &point.id {\n Some(PointId {\n point_id_options: Some(point_id),\n }) => match point_id {\n point_id::PointIdOptions::Uuid(uuid) => uuid.clone(),\n point_id::PointIdOptions::Num(num) => num.to_string(),\n },\n _ => return Err(MemoryError::Parse(\"Invalid point ID\".to_string())),\n };\n\n let content = payload\n .get(\"content\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .ok_or_else(|| MemoryError::Parse(\"Missing content field\".to_string()))?\n .to_string();\n\n // For now, we'll use a dummy embedding since parsing vectors is complex\n let embedding_dim = self.embedding_dim.unwrap_or(1024); // Default fallback\n let embedding = vec![0.0; embedding_dim];\n\n let created_at = payload\n .get(\"created_at\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())\n .map(|dt| dt.with_timezone(&chrono::Utc))\n .ok_or_else(|| MemoryError::Parse(\"Invalid created_at timestamp\".to_string()))?;\n\n let updated_at = payload\n .get(\"updated_at\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())\n .map(|dt| dt.with_timezone(&chrono::Utc))\n .ok_or_else(|| MemoryError::Parse(\"Invalid updated_at timestamp\".to_string()))?;\n\n let memory_type = payload\n .get(\"memory_type\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .map(|s| {\n debug!(\"Parsing memory type from string: '{}'\", s);\n crate::types::MemoryType::parse(s)\n })\n .unwrap_or_else(|| {\n warn!(\"No memory type found in payload, defaulting to Conversational\");\n crate::types::MemoryType::Conversational\n });\n\n let hash = payload\n .get(\"hash\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .map(|s| s.to_string())\n .unwrap_or_default();\n\n let mut custom = HashMap::new();\n for (key, value) in payload {\n if key.starts_with(\"custom_\") {\n let custom_key = key.strip_prefix(\"custom_\").unwrap().to_string();\n custom.insert(custom_key, serde_json::Value::String(value.to_string()));\n }\n }\n\n let metadata = MemoryMetadata {\n user_id: payload.get(\"user_id\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n agent_id: payload.get(\"agent_id\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n run_id: payload.get(\"run_id\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n actor_id: payload.get(\"actor_id\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n role: payload.get(\"role\").and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.to_string()),\n _ => None,\n }),\n memory_type,\n hash,\n importance_score: payload\n .get(\"importance_score\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::DoubleValue(d)),\n } => Some(*d),\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::IntegerValue(i)),\n } => Some(*i as f64),\n _ => None,\n })\n .map(|f| f as f32)\n .unwrap_or(0.5),\n entities: payload\n .get(\"entities\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .and_then(|s| serde_json::from_str(s).ok())\n .unwrap_or_default(),\n topics: payload\n .get(\"topics\")\n .and_then(|v| match v {\n qdrant_client::qdrant::Value {\n kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)),\n } => Some(s.as_str()),\n _ => None,\n })\n .and_then(|s| serde_json::from_str(s).ok())\n .unwrap_or_default(),\n custom,\n };\n\n Ok(Memory {\n id,\n content,\n embedding,\n metadata,\n created_at,\n updated_at,\n })\n }\n}\n\nimpl Clone for QdrantVectorStore {\n fn clone(&self) -> Self {\n Self {\n client: self.client.clone(),\n collection_name: self.collection_name.clone(),\n embedding_dim: self.embedding_dim,\n }\n }\n}\n\nimpl QdrantVectorStore {\n /// Get the embedding dimension\n pub fn embedding_dim(&self) -> Option {\n self.embedding_dim\n }\n\n /// Set the embedding dimension (used for auto-detection)\n pub fn set_embedding_dim(&mut self, dim: usize) {\n self.embedding_dim = Some(dim);\n }\n}\n\n#[async_trait]\nimpl VectorStore for QdrantVectorStore {\n async fn insert(&self, memory: &Memory) -> Result<()> {\n let point = self.memory_to_point(memory);\n\n let upsert_request = UpsertPoints {\n collection_name: self.collection_name.clone(),\n points: vec![point],\n ..Default::default()\n };\n\n self.client\n .upsert_points(upsert_request)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n debug!(\"Inserted memory with ID: {}\", memory.id);\n Ok(())\n }\n\n async fn search(\n &self,\n query_vector: &[f32],\n filters: &Filters,\n limit: usize,\n ) -> Result> {\n self.search_with_threshold(query_vector, filters, limit, None)\n .await\n }\n\n /// Search with optional similarity threshold filtering\n async fn search_with_threshold(\n &self,\n query_vector: &[f32],\n filters: &Filters,\n limit: usize,\n score_threshold: Option,\n ) -> Result> {\n let filter = self.filters_to_qdrant_filter(filters);\n\n let search_points = SearchPoints {\n collection_name: self.collection_name.clone(),\n vector: query_vector.to_vec(),\n limit: limit as u64,\n filter,\n with_payload: Some(true.into()),\n with_vectors: Some(true.into()),\n score_threshold: score_threshold.map(|t| t as f32), // Set score threshold if provided\n ..Default::default()\n };\n\n let response = self\n .client\n .search_points(search_points)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let mut results = Vec::new();\n for point in response.result {\n match self.point_to_memory(&point) {\n Ok(memory) => {\n results.push(ScoredMemory {\n memory,\n score: point.score,\n });\n }\n Err(e) => {\n warn!(\"Failed to parse memory from point: {}\", e);\n }\n }\n }\n\n debug!(\n \"Found {} memories for search query with threshold {:?}\",\n results.len(),\n score_threshold\n );\n Ok(results)\n }\n\n async fn update(&self, memory: &Memory) -> Result<()> {\n // For Qdrant, update is the same as insert (upsert)\n self.insert(memory).await\n }\n\n async fn delete(&self, id: &str) -> Result<()> {\n let point_id = PointId {\n point_id_options: Some(point_id::PointIdOptions::Uuid(id.to_string())),\n };\n\n let points_selector = PointsSelector {\n points_selector_one_of: Some(points_selector::PointsSelectorOneOf::Points(\n PointsIdsList {\n ids: vec![point_id],\n },\n )),\n };\n\n let delete_request = DeletePoints {\n collection_name: self.collection_name.clone(),\n points: Some(points_selector),\n ..Default::default()\n };\n\n self.client\n .delete_points(delete_request)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n debug!(\"Deleted memory with ID: {}\", id);\n Ok(())\n }\n\n async fn get(&self, id: &str) -> Result> {\n let point_id = PointId {\n point_id_options: Some(point_id::PointIdOptions::Uuid(id.to_string())),\n };\n\n let get_request = GetPoints {\n collection_name: self.collection_name.clone(),\n ids: vec![point_id],\n with_payload: Some(true.into()),\n with_vectors: Some(true.into()),\n ..Default::default()\n };\n\n let response = self\n .client\n .get_points(get_request)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n if let Some(point) = response.result.first() {\n // Convert RetrievedPoint to ScoredPoint for parsing\n let scored_point = ScoredPoint {\n id: point.id.clone(),\n payload: point.payload.clone(),\n score: 1.0, // Not relevant for get operation\n vectors: point.vectors.clone(),\n shard_key: None,\n order_value: None,\n version: 0,\n };\n\n match self.point_to_memory(&scored_point) {\n Ok(memory) => Ok(Some(memory)),\n Err(e) => {\n error!(\"Failed to parse memory from point: {}\", e);\n Err(e)\n }\n }\n } else {\n Ok(None)\n }\n }\n\n async fn list(&self, filters: &Filters, limit: Option) -> Result> {\n let filter = self.filters_to_qdrant_filter(filters);\n let limit = limit.unwrap_or(100) as u32;\n\n let scroll_points = ScrollPoints {\n collection_name: self.collection_name.clone(),\n filter,\n limit: Some(limit),\n with_payload: Some(true.into()),\n with_vectors: Some(true.into()),\n ..Default::default()\n };\n\n let response = self\n .client\n .scroll(scroll_points)\n .await\n .map_err(|e| MemoryError::VectorStore(e))?;\n\n let mut results = Vec::new();\n for point in response.result {\n // Convert RetrievedPoint to ScoredPoint for parsing\n let scored_point = ScoredPoint {\n id: point.id.clone(),\n payload: point.payload.clone(),\n score: 1.0, // Not relevant for list operation\n vectors: point.vectors.clone(),\n shard_key: None,\n order_value: None,\n version: 0,\n };\n\n match self.point_to_memory(&scored_point) {\n Ok(memory) => results.push(memory),\n Err(e) => {\n warn!(\"Failed to parse memory from point: {}\", e);\n }\n }\n }\n\n debug!(\"Listed {} memories\", results.len());\n Ok(results)\n }\n\n async fn health_check(&self) -> Result {\n match self.client.health_check().await {\n Ok(_) => Ok(true),\n Err(e) => {\n error!(\"Qdrant health check failed: {}\", e);\n Ok(false)\n }\n }\n }\n}\n" + "name": "types.rs", + "source_summary": "use serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\n\n/// Common data structure for memory operation payloads\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct MemoryOperationPayload {\n /// The content to store (for store operations)\n pub content: Option,\n\n /// The query string (for search/query operations)\n pub query: Option,\n\n /// Memory ID (for get operations)\n pub memory_id: Option,\n\n /// User ID for filtering\n pub user_id: Option,\n\n /// Agent ID for filtering\n pub agent_id: Option,\n\n /// Type of memory\n pub memory_type: Option,\n\n /// Topics to filter by\n pub topics: Option>,\n\n /// Keywords to filter by\n pub keywords: Option>,\n\n /// Maximum number of results\n pub limit: Option,\n\n /// Minimum salience/importance score\n pub min_salience: Option,\n\n /// Maximum number of results (alias for limit)\n pub k: Option,\n\n /// Additional metadata\n pub metadata: Option>,\n}\n\nimpl Default for MemoryOperationPayload {\n fn default() -> Self {\n Self {\n content: None,\n query: None,\n memory_id: None,\n user_id: None,\n agent_id: None,\n memory_type: None,\n topics: None,\n keywords: None,\n limit: None,\n min_salience: None,\n k: None,\n metadata: None,\n }\n }\n}\n\n/// Memory operation types supported by the tools\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\n#[serde(rename_all = \"lowercase\")]\npub enum MemoryOperationType {\n Store,\n Query,\n List,\n Get,\n}\n\n/// Common response structure for memory operations\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct MemoryOperationResponse {\n /// Whether the operation was successful\n pub success: bool,\n\n /// Message describing the result\n pub message: String,\n\n /// Optional data payload\n pub data: Option,\n\n /// Optional error details\n pub error: Option,\n}\n\nimpl MemoryOperationResponse {\n /// Create a successful response\n pub fn success(message: impl Into) -> Self {\n Self {\n success: true,\n message: message.into(),\n data: None,\n error: None,\n }\n }\n\n /// Create a successful response with data\n pub fn success_with_data(message: impl Into, data: serde_json::Value) -> Self {\n Self {\n success: true,\n message: message.into(),\n data: Some(data),\n error: None,\n }\n }\n\n /// Create an error response\n pub fn error(error: impl Into) -> Self {\n Self {\n success: false,\n message: \"Operation failed\".to_string(),\n data: None,\n error: Some(error.into()),\n }\n }\n}\n\n/// Helper struct to extract query parameters\npub struct QueryParams {\n pub query: String,\n pub limit: usize,\n pub min_salience: Option,\n pub memory_type: Option,\n pub topics: Option>,\n pub user_id: Option,\n pub agent_id: Option,\n}\n\nimpl QueryParams {\n pub fn from_payload(payload: &MemoryOperationPayload, default_limit: usize) -> crate::errors::MemoryToolsResult {\n let query = payload.query.as_ref()\n .ok_or_else(|| crate::errors::MemoryToolsError::InvalidInput(\"Query is required\".to_string()))?\n .clone();\n\n let limit = payload.limit\n .or(payload.k)\n .unwrap_or(default_limit);\n\n Ok(Self {\n query,\n limit,\n min_salience: payload.min_salience,\n memory_type: payload.memory_type.clone(),\n topics: payload.topics.clone(),\n user_id: payload.user_id.clone(),\n agent_id: payload.agent_id.clone(),\n })\n }\n}\n\n/// Helper struct to extract store parameters\npub struct StoreParams {\n pub content: String,\n pub user_id: String,\n pub agent_id: Option,\n pub memory_type: String,\n pub topics: Option>,\n}\n\nimpl StoreParams {\n pub fn from_payload(payload: &MemoryOperationPayload, default_user_id: Option, default_agent_id: Option) -> crate::errors::MemoryToolsResult {\n let content = payload.content.as_ref()\n .ok_or_else(|| crate::errors::MemoryToolsError::InvalidInput(\"Content is required\".to_string()))?\n .clone();\n\n let user_id = payload.user_id\n .clone()\n .or(default_user_id)\n .ok_or_else(|| crate::errors::MemoryToolsError::InvalidInput(\"User ID is required\".to_string()))?;\n\n let agent_id = payload.agent_id.clone().or(default_agent_id);\n\n let memory_type = payload.memory_type\n .clone()\n .unwrap_or_else(|| \"conversational\".to_string());\n\n Ok(Self {\n content,\n user_id,\n agent_id,\n memory_type,\n topics: payload.topics.clone(),\n })\n }\n}\n\n/// Helper struct to extract filter parameters\npub struct FilterParams {\n pub user_id: Option,\n pub agent_id: Option,\n pub memory_type: Option,\n pub limit: usize,\n}\n\nimpl FilterParams {\n pub fn from_payload(payload: &MemoryOperationPayload, default_limit: usize) -> crate::errors::MemoryToolsResult {\n let limit = payload.limit.or(payload.k).unwrap_or(default_limit);\n\n Ok(Self {\n user_id: payload.user_id.clone(),\n agent_id: payload.agent_id.clone(),\n memory_type: payload.memory_type.clone(),\n limit,\n })\n }\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 75.0, - "lines_of_code": 813, - "number_of_classes": 1, - "number_of_functions": 20 + "cyclomatic_complexity": 10.0, + "lines_of_code": 209, + "number_of_classes": 6, + "number_of_functions": 6 }, "dependencies": [ { "dependency_type": "library", "is_external": true, - "line_number": null, - "name": "async_trait", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "qdrant_client", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "tracing", + "line_number": 1, + "name": "serde", "path": null, "version": null }, { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "crate::config::QdrantConfig", - "path": "cortex-mem-core/src/config/mod.rs", - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "crate::error::MemoryError", - "path": "cortex-mem-core/src/error/mod.rs", - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "crate::types::Filters", - "path": "cortex-mem-core/src/types/mod.rs", - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "crate::types::Memory", - "path": "cortex-mem-core/src/types/mod.rs", - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "crate::types::MemoryMetadata", - "path": "cortex-mem-core/src/types/mod.rs", - "version": null - }, - { - "dependency_type": "internal", + "dependency_type": "standard_library", "is_external": false, - "line_number": null, - "name": "crate::types::ScoredMemory", - "path": "cortex-mem-core/src/types/mod.rs", + "line_number": 2, + "name": "std::collections::HashMap", + "path": null, "version": null - }, + } + ], + "detailed_description": "This component defines the core types used across the memory tools module. It includes a flexible payload structure (MemoryOperationPayload) that supports various memory operations such as store, query, list, and get. The payload uses optional fields to maintain flexibility while supporting multiple operation types. Three helper structs (QueryParams, StoreParams, FilterParams) provide typed parameter extraction from the generic payload with validation logic, reducing boilerplate in business logic. The MemoryOperationResponse provides standardized success/error responses with message and optional data. The MemoryOperationType enum safely represents supported operations. These types enable type-safe, serializable communication between components, especially in API boundaries and inter-service communication.", + "interfaces": [ { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "crate::vector_store::VectorStore", - "path": "cortex-mem-core/src/vector_store/mod.rs", - "version": null + "description": "Generic payload container for all memory operations with optional fields for different operation types", + "interface_type": "struct", + "name": "MemoryOperationPayload", + "parameters": [ + { + "description": "Content to store in memory", + "is_optional": true, + "name": "content", + "param_type": "Option" + }, + { + "description": "Search query string", + "is_optional": true, + "name": "query", + "param_type": "Option" + }, + { + "description": "Specific memory entry identifier", + "is_optional": true, + "name": "memory_id", + "param_type": "Option" + }, + { + "description": "User identifier for filtering", + "is_optional": true, + "name": "user_id", + "param_type": "Option" + }, + { + "description": "Agent identifier for filtering", + "is_optional": true, + "name": "agent_id", + "param_type": "Option" + }, + { + "description": "Type/category of memory", + "is_optional": true, + "name": "memory_type", + "param_type": "Option" + }, + { + "description": "Topics associated with memory", + "is_optional": true, + "name": "topics", + "param_type": "Option>" + }, + { + "description": "Keywords for indexing and search", + "is_optional": true, + "name": "keywords", + "param_type": "Option>" + }, + { + "description": "Maximum number of results", + "is_optional": true, + "name": "limit", + "param_type": "Option" + }, + { + "description": "Minimum importance score filter", + "is_optional": true, + "name": "min_salience", + "param_type": "Option" + }, + { + "description": "Alias for limit parameter", + "is_optional": true, + "name": "k", + "param_type": "Option" + }, + { + "description": "Additional extensible metadata", + "is_optional": true, + "name": "metadata", + "param_type": "Option>" + } + ], + "return_type": null, + "visibility": "pub" }, { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "crate::llm::LLMClient", - "path": "cortex-mem-core/src/llm/mod.rs", - "version": null - } - ], - "detailed_description": "This component implements a Qdrant-based vector store for managing memory data in a vector database. It provides full CRUD operations for memory records, including insertion, retrieval, updating, deletion, and similarity-based search. The implementation handles schema management by ensuring the target collection exists with correct dimensionality, supports rich filtering capabilities based on user, agent, run, memory type, topics, entities, importance score, and custom metadata. It converts between domain Memory objects and Qdrant PointStruct format, handling payload serialization/deserialization with proper type mapping. The component also provides health checking functionality and supports both manual and LLM-client-assisted embedding dimension detection.", - "interfaces": [ - { - "description": "Main trait implementation that defines the vector store interface for memory operations", - "interface_type": "trait", - "name": "VectorStore", + "description": "Enumeration of supported memory operation types", + "interface_type": "enum", + "name": "MemoryOperationType", "parameters": [], "return_type": null, - "visibility": "public" + "visibility": "pub" }, { - "description": "Create a new Qdrant vector store instance", - "interface_type": "function", - "name": "new", + "description": "Standardized response format for memory operations", + "interface_type": "struct", + "name": "MemoryOperationResponse", "parameters": [ { - "description": "Configuration for connecting to Qdrant", + "description": "Indicates if operation was successful", "is_optional": false, - "name": "config", - "param_type": "&QdrantConfig" + "name": "success", + "param_type": "bool" + }, + { + "description": "Result description message", + "is_optional": false, + "name": "message", + "param_type": "String" + }, + { + "description": "Optional payload data", + "is_optional": true, + "name": "data", + "param_type": "Option" + }, + { + "description": "Error details if operation failed", + "is_optional": true, + "name": "error", + "param_type": "Option" } ], - "return_type": "Result", - "visibility": "public" + "return_type": null, + "visibility": "pub" }, { - "description": "Create a new Qdrant vector store with auto-detected embedding dimension", + "description": "Extracts and validates query parameters from a payload with default fallback", "interface_type": "function", - "name": "new_with_llm_client", + "name": "QueryParams::from_payload", "parameters": [ { - "description": "Configuration for connecting to Qdrant", + "description": "Source payload to extract from", "is_optional": false, - "name": "config", - "param_type": "&QdrantConfig" + "name": "payload", + "param_type": "&MemoryOperationPayload" }, { - "description": "LLM client for embedding dimension auto-detection", + "description": "Default limit if not specified in payload", "is_optional": false, - "name": "llm_client", - "param_type": "&dyn crate::llm::LLMClient" + "name": "default_limit", + "param_type": "usize" } ], - "return_type": "Result", - "visibility": "public" + "return_type": "crate::errors::MemoryToolsResult", + "visibility": "pub" }, { - "description": "Insert a memory record into the vector store", + "description": "Extracts and validates store parameters from payload with default values", "interface_type": "function", - "name": "insert", + "name": "StoreParams::from_payload", "parameters": [ { - "description": "Memory object to insert", + "description": "Source payload to extract from", "is_optional": false, - "name": "memory", - "param_type": "&Memory" + "name": "payload", + "param_type": "&MemoryOperationPayload" + }, + { + "description": "Default user ID if not in payload", + "is_optional": true, + "name": "default_user_id", + "param_type": "Option" + }, + { + "description": "Default agent ID if not in payload", + "is_optional": true, + "name": "default_agent_id", + "param_type": "Option" } ], - "return_type": "Result<()>", - "visibility": "public" + "return_type": "crate::errors::MemoryToolsResult", + "visibility": "pub" }, { - "description": "Search for similar memories based on query vector and filters", + "description": "Extracts filter parameters from payload with default limit", "interface_type": "function", - "name": "search", + "name": "FilterParams::from_payload", "parameters": [ { - "description": "Query embedding vector", - "is_optional": false, - "name": "query_vector", - "param_type": "&[f32]" - }, - { - "description": "Filter criteria", + "description": "Source payload to extract from", "is_optional": false, - "name": "filters", - "param_type": "&Filters" + "name": "payload", + "param_type": "&MemoryOperationPayload" }, { - "description": "Maximum number of results", + "description": "Default limit if not specified", "is_optional": false, - "name": "limit", + "name": "default_limit", "param_type": "usize" } ], - "return_type": "Result>", - "visibility": "public" + "return_type": "crate::errors::MemoryToolsResult", + "visibility": "pub" + } + ], + "responsibilities": [ + "Define standardized data structures for memory operation requests and responses", + "Provide type-safe parameter extraction and validation from generic payloads", + "Enable serialization and deserialization of memory operation data through Serde", + "Support flexible filtering and querying capabilities via optional parameters", + "Ensure type safety and compile-time correctness for memory operations" + ] + }, + { + "code_dossier": { + "code_purpose": "tool", + "description": "Provides tool definitions and utility functions for MCP (Memory Control Protocol) operations, including memory storage, querying, listing, and retrieval by ID. Maps MCP arguments to internal payload format and handles error translation.", + "file_path": "cortex-mem-tools/src/mcp_tools.rs", + "functions": [ + "get_mcp_tool_definitions", + "map_mcp_arguments_to_payload", + "tools_error_to_mcp_error_code", + "get_tool_error_message" + ], + "importance_score": 0.8, + "interfaces": [ + "McpToolDefinition", + "get_mcp_tool_definitions", + "map_mcp_arguments_to_payload", + "tools_error_to_mcp_error_code", + "get_tool_error_message" + ], + "name": "mcp_tools.rs", + "source_summary": "use serde_json::{Map, Value, json};\nuse crate::{MemoryOperationPayload, MemoryToolsError};\n\n/// MCP工具定义\npub struct McpToolDefinition {\n pub name: String,\n pub title: Option,\n pub description: Option,\n pub input_schema: Value,\n pub output_schema: Option,\n}\n\n/// 获取所有MCP工具的定义\npub fn get_mcp_tool_definitions() -> Vec {\n vec![\n McpToolDefinition {\n name: \"store_memory\".into(),\n title: Some(\"Store Memory\".into()),\n description: Some(\"Store a new memory in the system with specified content and optional metadata.\".into()),\n input_schema: json!({\n \"type\": \"object\",\n \"properties\": {\n \"content\": {\n \"type\": \"string\",\n \"description\": \"The content of the memory to store\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"User ID associated with the memory (required unless --agent was specified on startup)\"\n },\n \"agent_id\": {\n \"type\": \"string\",\n \"description\": \"Agent ID associated with the memory (optional, defaults to configured agent)\"\n },\n \"memory_type\": {\n \"type\": \"string\",\n \"enum\": [\"conversational\", \"procedural\", \"factual\", \"semantic\", \"episodic\", \"personal\"],\n \"description\": \"Type of memory\",\n \"default\": \"conversational\"\n },\n \"topics\": {\n \"type\": \"array\",\n \"items\": {\"type\": \"string\"},\n \"description\": \"Topics to associate with the memory\"\n }\n },\n \"required\": [\"content\"]\n }),\n output_schema: Some(json!({\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"memory_id\": {\"type\": \"string\"},\n \"message\": {\"type\": \"string\"}\n },\n \"required\": [\"success\", \"memory_id\", \"message\"]\n })),\n },\n McpToolDefinition {\n name: \"query_memory\".into(),\n title: Some(\"Query Memory\".into()),\n description: Some(\"Search memories using semantic similarity and filters.\".into()),\n input_schema: json!({\n \"type\": \"object\",\n \"properties\": {\n \"query\": {\n \"type\": \"string\",\n \"description\": \"Query string for semantic search\"\n },\n \"k\": {\n \"type\": \"integer\",\n \"description\": \"Maximum number of results to return\",\n \"default\": 10\n },\n \"memory_type\": {\n \"type\": \"string\",\n \"enum\": [\"conversational\", \"procedural\", \"factual\", \"semantic\", \"episodic\", \"personal\"],\n \"description\": \"Type of memory to filter by\"\n },\n \"min_salience\": {\n \"type\": \"number\",\n \"description\": \"Minimum salience/importance score threshold (0-1)\",\n \"minimum\": 0,\n \"maximum\": 1\n },\n \"topics\": {\n \"type\": \"array\",\n \"items\": {\"type\": \"string\"},\n \"description\": \"Topics to filter memories by\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"User ID to filter memories (optional, defaults to configured agent's user)\"\n },\n \"agent_id\": {\n \"type\": \"string\",\n \"description\": \"Agent ID to filter memories (optional, defaults to configured agent)\"\n }\n },\n \"required\": [\"query\"]\n }),\n output_schema: Some(json!({\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"count\": {\"type\": \"number\"},\n \"memories\": {\"type\": \"array\", \"items\": {\"type\": \"object\"}}\n },\n \"required\": [\"success\", \"count\", \"memories\"]\n })),\n },\n McpToolDefinition {\n name: \"list_memories\".into(),\n title: Some(\"List Memories\".into()),\n description: Some(\"Retrieve memories with optional filtering. Adjust the limit parameter to control the number of results returned (default: 100, max: 1000).\".into()),\n input_schema: json!({\n \"type\": \"object\",\n \"properties\": {\n \"limit\": {\n \"type\": \"integer\",\n \"description\": \"Maximum number of memories to return (default: 100, max: 1000)\",\n \"default\": 100,\n \"maximum\": 1000\n },\n \"memory_type\": {\n \"type\": \"string\",\n \"enum\": [\"conversational\", \"procedural\", \"factual\", \"semantic\", \"episodic\", \"personal\"],\n \"description\": \"Type of memory to filter by\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"User ID to filter memories (optional, defaults to configured agent's user)\"\n },\n \"agent_id\": {\n \"type\": \"string\",\n \"description\": \"Agent ID to filter memories (optional, defaults to configured agent)\"\n }\n }\n }),\n output_schema: Some(json!({\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"count\": {\"type\": \"number\"},\n \"memories\": {\"type\": \"array\", \"items\": {\"type\": \"object\"}}\n },\n \"required\": [\"success\", \"count\", \"memories\"]\n })),\n },\n McpToolDefinition {\n name: \"get_memory\".into(),\n title: Some(\"Get Memory by ID\".into()),\n description: Some(\"Retrieve a specific memory by its exact ID.\".into()),\n input_schema: json!({\n \"type\": \"object\",\n \"properties\": {\n \"memory_id\": {\n \"type\": \"string\",\n \"description\": \"Exact ID of the memory to retrieve (required)\"\n }\n },\n \"required\": [\"memory_id\"]\n }),\n output_schema: Some(json!({\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"memory\": {\"type\": \"object\"}\n },\n \"required\": [\"success\", \"memory\"]\n })),\n },\n ]\n}\n\n/// 将MCP参数映射到MemoryOperationPayload\npub fn map_mcp_arguments_to_payload(\n arguments: &Map,\n default_agent_id: &Option,\n) -> MemoryOperationPayload {\n let mut payload = MemoryOperationPayload::default();\n\n // 提取公共字段\n if let Some(content) = arguments.get(\"content\").and_then(|v| v.as_str()) {\n payload.content = Some(content.to_string());\n }\n\n if let Some(query) = arguments.get(\"query\").and_then(|v| v.as_str()) {\n payload.query = Some(query.to_string());\n }\n\n if let Some(memory_id) = arguments.get(\"memory_id\").and_then(|v| v.as_str()) {\n payload.memory_id = Some(memory_id.to_string());\n }\n\n // User ID可以从参数提供,或从agent ID派生\n if let Some(user_id) = arguments.get(\"user_id\").and_then(|v| v.as_str()) {\n payload.user_id = Some(user_id.to_string());\n } else if let Some(agent_id) = default_agent_id {\n // 如果设置了agent_id,从中派生user_id\n payload.user_id = Some(format!(\"user_of_{}\", agent_id));\n }\n\n // Agent ID可以从参数提供,或使用默认值\n if let Some(agent_id) = arguments.get(\"agent_id\").and_then(|v| v.as_str()) {\n payload.agent_id = Some(agent_id.to_string());\n } else {\n payload.agent_id = default_agent_id.clone();\n }\n\n if let Some(memory_type) = arguments.get(\"memory_type\").and_then(|v| v.as_str()) {\n payload.memory_type = Some(memory_type.to_string());\n }\n\n if let Some(topics) = arguments.get(\"topics\").and_then(|v| v.as_array()) {\n payload.topics = Some(\n topics\n .iter()\n .filter_map(|v| v.as_str())\n .map(String::from)\n .collect(),\n );\n }\n\n if let Some(keywords) = arguments.get(\"keywords\").and_then(|v| v.as_array()) {\n payload.keywords = Some(\n keywords\n .iter()\n .filter_map(|v| v.as_str())\n .map(String::from)\n .collect(),\n );\n }\n\n if let Some(limit) = arguments.get(\"limit\").and_then(|v| v.as_u64()) {\n payload.limit = Some(limit as usize);\n }\n\n if let Some(k) = arguments.get(\"k\").and_then(|v| v.as_u64()) {\n payload.k = Some(k as usize);\n }\n\n if let Some(min_salience) = arguments.get(\"min_salience\").and_then(|v| v.as_f64()) {\n payload.min_salience = Some(min_salience);\n }\n\n payload\n}\n\n/// 将MemoryToolsError转换为MCP错误代码\npub fn tools_error_to_mcp_error_code(error: &MemoryToolsError) -> i32 {\n use MemoryToolsError::*;\n \n match error {\n InvalidInput(_) => -32602, // Invalid params\n Runtime(_) => -32603, // Internal error\n MemoryNotFound(_) => -32601, // Method not found (for memory not found)\n Serialization(_) => -32603, // Internal error\n Core(_) => -32603, // Internal error\n }\n}\n\n/// 获取工具的错误消息\npub fn get_tool_error_message(error: &MemoryToolsError) -> String {\n use MemoryToolsError::*;\n \n match error {\n InvalidInput(msg) => msg.clone(),\n Runtime(msg) => msg.clone(),\n MemoryNotFound(msg) => msg.clone(),\n Serialization(e) => format!(\"Serialization error: {}\", e),\n Core(e) => format!(\"Core error: {}\", e),\n }\n}" + }, + "complexity_metrics": { + "cyclomatic_complexity": 17.0, + "lines_of_code": 274, + "number_of_classes": 1, + "number_of_functions": 5 + }, + "dependencies": [ + { + "dependency_type": "import", + "is_external": true, + "line_number": 1, + "name": "serde_json", + "path": "serde_json::{Map, Value, json}", + "version": null }, { - "description": "Search for similar memories with optional similarity threshold", - "interface_type": "function", - "name": "search_with_threshold", + "dependency_type": "import", + "is_external": false, + "line_number": 2, + "name": "MemoryOperationPayload", + "path": "crate::{MemoryOperationPayload, MemoryToolsError}", + "version": null + }, + { + "dependency_type": "import", + "is_external": false, + "line_number": 2, + "name": "MemoryToolsError", + "path": "crate::{MemoryOperationPayload, MemoryToolsError}", + "version": null + } + ], + "detailed_description": "This component defines the interface contracts for memory operations exposed via MCP (Memory Control Protocol). It provides standardized tool definitions for storing, querying, listing, and retrieving memories with rich metadata filtering. The `McpToolDefinition` struct describes each tool's name, description, input/output schema following JSON Schema规范. The `get_mcp_tool_definitions` function returns configurations for four key operations: 'store_memory', 'query_memory', 'list_memories', and 'get_memory'. Each operation supports various parameters like user_id, agent_id, memory_type, topics, and salience thresholds. The `map_mcp_arguments_to_payload` function transforms MCP input arguments into an internal `MemoryOperationPayload` structure used by the core system. Error handling utilities convert internal `MemoryToolsError` types to standardized MCP error codes (-32600 series) and generate appropriate error messages, ensuring consistent API responses.", + "interfaces": [ + { + "description": "Represents a tool definition for MCP protocol with name, title, description, and JSON schemas for input/output validation", + "interface_type": "struct", + "name": "McpToolDefinition", "parameters": [ { - "description": "Query embedding vector", - "is_optional": false, - "name": "query_vector", - "param_type": "&[f32]" - }, - { - "description": "Filter criteria", + "description": null, "is_optional": false, - "name": "filters", - "param_type": "&Filters" + "name": "name", + "param_type": "String" }, { - "description": "Maximum number of results", - "is_optional": false, - "name": "limit", - "param_type": "usize" + "description": null, + "is_optional": true, + "name": "title", + "param_type": "Option" }, { - "description": "Minimum similarity score threshold", + "description": null, "is_optional": true, - "name": "score_threshold", - "param_type": "Option" - } - ], - "return_type": "Result>", - "visibility": "public" - }, - { - "description": "Update a memory record (upsert operation)", - "interface_type": "function", - "name": "update", - "parameters": [ + "name": "description", + "param_type": "Option" + }, { - "description": "Memory object to update", + "description": null, "is_optional": false, - "name": "memory", - "param_type": "&Memory" + "name": "input_schema", + "param_type": "Value" + }, + { + "description": null, + "is_optional": true, + "name": "output_schema", + "param_type": "Option" } ], - "return_type": "Result<()>", + "return_type": null, "visibility": "public" }, { - "description": "Delete a memory record by ID", + "description": "Returns a vector of McpToolDefinition instances for all supported memory operations", "interface_type": "function", - "name": "delete", - "parameters": [ - { - "description": "ID of memory to delete", - "is_optional": false, - "name": "id", - "param_type": "&str" - } - ], - "return_type": "Result<()>", + "name": "get_mcp_tool_definitions", + "parameters": [], + "return_type": "Vec", "visibility": "public" }, { - "description": "Retrieve a memory record by ID", + "description": "Converts MCP arguments map and default agent ID into a MemoryOperationPayload structure", "interface_type": "function", - "name": "get", + "name": "map_mcp_arguments_to_payload", "parameters": [ { - "description": "ID of memory to retrieve", + "description": null, "is_optional": false, - "name": "id", - "param_type": "&str" + "name": "arguments", + "param_type": "&Map" + }, + { + "description": null, + "is_optional": false, + "name": "default_agent_id", + "param_type": "&Option" } ], - "return_type": "Result>", + "return_type": "MemoryOperationPayload", "visibility": "public" }, { - "description": "List memory records with optional filtering and limiting", + "description": "Converts MemoryToolsError to standardized JSON-RPC error codes used in MCP protocol", "interface_type": "function", - "name": "list", + "name": "tools_error_to_mcp_error_code", "parameters": [ { - "description": "Filter criteria", + "description": null, "is_optional": false, - "name": "filters", - "param_type": "&Filters" - }, - { - "description": "Maximum number of results", - "is_optional": true, - "name": "limit", - "param_type": "Option" + "name": "error", + "param_type": "&MemoryToolsError" } ], - "return_type": "Result>", - "visibility": "public" - }, - { - "description": "Check the health status of the Qdrant connection", - "interface_type": "function", - "name": "health_check", - "parameters": [], - "return_type": "Result", - "visibility": "public" - }, - { - "description": "Get the current embedding dimension", - "interface_type": "function", - "name": "embedding_dim", - "parameters": [], - "return_type": "Option", + "return_type": "i32", "visibility": "public" }, { - "description": "Set the embedding dimension (for auto-detection)", + "description": "Generates user-friendly error message from MemoryToolsError instance", "interface_type": "function", - "name": "set_embedding_dim", + "name": "get_tool_error_message", "parameters": [ { - "description": "New embedding dimension to set", + "description": null, "is_optional": false, - "name": "dim", - "param_type": "usize" + "name": "error", + "param_type": "&MemoryToolsError" } ], - "return_type": null, + "return_type": "String", "visibility": "public" } ], "responsibilities": [ - "Manage connection and interaction with Qdrant vector database", - "Implement CRUD operations for memory data with proper error handling", - "Handle schema management including collection creation and dimension validation", - "Convert between domain Memory objects and Qdrant storage format", - "Support complex filtering and similarity search operations" + "Define standardized tool interfaces for memory operations following MCP protocol", + "Map external MCP arguments to internal memory operation payload structure", + "Translate internal error types to standardized MCP-compliant error codes", + "Provide comprehensive error messaging for client-facing responses", + "Support flexible memory querying with semantic search, filtering, and pagination" ] }, { "code_dossier": { - "code_purpose": "types", - "description": "Defines the VectorStore trait and re-exports QdrantVectorStore implementation for managing vector-based memory storage operations.", - "file_path": "cortex-mem-core/src/vector_store/mod.rs", - "functions": [], + "code_purpose": "specificfeature", + "description": "Core operations handler for memory tools including storing, querying, listing, and retrieving memories with filtering and metadata management.", + "file_path": "cortex-mem-tools/src/operations.rs", + "functions": [ + "new", + "store_memory", + "query_memory", + "list_memories", + "get_memory" + ], "importance_score": 0.8, "interfaces": [ - "VectorStore::insert", - "VectorStore::search", - "VectorStore::search_with_threshold", - "VectorStore::update", - "VectorStore::delete", - "VectorStore::get", - "VectorStore::list", - "VectorStore::health_check" + "MemoryOperations", + "store_memory", + "query_memory", + "list_memories", + "get_memory", + "memory_to_json" ], - "name": "mod.rs", - "source_summary": "pub mod qdrant;\n\nuse crate::{\n error::Result,\n types::{Filters, Memory, ScoredMemory},\n};\nuse async_trait::async_trait;\n\npub use qdrant::QdrantVectorStore;\n\n/// Trait for vector store operations\n#[async_trait]\npub trait VectorStore: Send + Sync + dyn_clone::DynClone {\n /// Insert a memory into the vector store\n async fn insert(&self, memory: &Memory) -> Result<()>;\n\n /// Search for similar memories\n async fn search(\n &self,\n query_vector: &[f32],\n filters: &Filters,\n limit: usize,\n ) -> Result>;\n\n /// Search for similar memories with similarity threshold\n async fn search_with_threshold(\n &self,\n query_vector: &[f32],\n filters: &Filters,\n limit: usize,\n score_threshold: Option,\n ) -> Result>;\n\n /// Update an existing memory\n async fn update(&self, memory: &Memory) -> Result<()>;\n\n /// Delete a memory by ID\n async fn delete(&self, id: &str) -> Result<()>;\n\n /// Get a memory by ID\n async fn get(&self, id: &str) -> Result>;\n\n /// List all memories with optional filters\n async fn list(&self, filters: &Filters, limit: Option) -> Result>;\n\n /// Check if the vector store is healthy\n async fn health_check(&self) -> Result;\n}\n\ndyn_clone::clone_trait_object!(VectorStore);\n" + "name": "operations.rs", + "source_summary": "use crate::errors::{MemoryToolsError, MemoryToolsResult};\nuse crate::types::{\n MemoryOperationPayload, MemoryOperationResponse, QueryParams,\n StoreParams, FilterParams\n};\nuse cortex_mem_core::{\n memory::MemoryManager, Memory, MemoryType, MemoryMetadata\n};\nuse serde_json::{json, Value};\nuse tracing::{error, info};\n\n/// Core operations handler for memory tools\npub struct MemoryOperations {\n memory_manager: std::sync::Arc,\n default_user_id: Option,\n default_agent_id: Option,\n default_limit: usize,\n}\n\nimpl MemoryOperations {\n /// Create a new MemoryOperations instance\n pub fn new(\n memory_manager: std::sync::Arc,\n default_user_id: Option,\n default_agent_id: Option,\n default_limit: usize,\n ) -> Self {\n Self {\n memory_manager,\n default_user_id,\n default_agent_id,\n default_limit,\n }\n }\n\n /// Store a new memory\n pub async fn store_memory(&self, payload: MemoryOperationPayload) -> MemoryToolsResult {\n let params = StoreParams::from_payload(\n &payload,\n self.default_user_id.clone(),\n self.default_agent_id.clone(),\n )?;\n\n info!(\"Storing memory for user: {}\", params.user_id);\n\n let memory_type = MemoryType::parse_with_result(¶ms.memory_type)\n .map_err(|e| MemoryToolsError::InvalidInput(format!(\"Invalid memory_type: {}\", e)))?;\n\n let mut metadata = MemoryMetadata::new(memory_type);\n metadata.user_id = Some(params.user_id.clone());\n metadata.agent_id = params.agent_id.clone();\n\n if let Some(topics) = params.topics {\n metadata.topics = topics;\n }\n\n match self.memory_manager.store(params.content, metadata).await {\n Ok(memory_id) => {\n info!(\"Memory stored successfully with ID: {}\", memory_id);\n let data = json!({\n \"memory_id\": memory_id,\n \"user_id\": params.user_id,\n \"agent_id\": params.agent_id\n });\n\n Ok(MemoryOperationResponse::success_with_data(\n \"Memory stored successfully\",\n data,\n ))\n }\n Err(e) => {\n error!(\"Failed to store memory: {}\", e);\n Err(MemoryToolsError::Runtime(format!(\"Failed to store memory: {}\", e)))\n }\n }\n }\n\n /// Query memories based on semantic similarity\n pub async fn query_memory(&self, payload: MemoryOperationPayload) -> MemoryToolsResult {\n let params = QueryParams::from_payload(&payload, self.default_limit)?;\n\n info!(\"Querying memories with query: {}\", params.query);\n\n let memory_type = params.memory_type\n .map(|t| MemoryType::parse_with_result(&t))\n .transpose()\n .map_err(|e| MemoryToolsError::InvalidInput(format!(\"Invalid memory_type: {}\", e)))?;\n\n // Convert parameters to Filters\n let mut filters = cortex_mem_core::types::Filters::default();\n\n if let Some(user_id) = params.user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = params.agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(memory_type) = memory_type {\n filters.memory_type = Some(memory_type);\n }\n\n if let Some(topics) = params.topics {\n filters.topics = Some(topics);\n }\n\n match self.memory_manager.search(\n ¶ms.query,\n &filters,\n params.limit,\n ).await {\n Ok(memories) => {\n let count = memories.len();\n info!(\"Found {} memories\", count);\n\n let memories_json: Vec = memories\n .into_iter()\n .map(|scored_memory| memory_to_json(&scored_memory.memory))\n .collect();\n\n let data = json!({\n \"count\": count,\n \"memories\": memories_json\n });\n\n Ok(MemoryOperationResponse::success_with_data(\n \"Query completed successfully\",\n data,\n ))\n }\n Err(e) => {\n error!(\"Failed to query memories: {}\", e);\n Err(MemoryToolsError::Runtime(format!(\"Failed to query memories: {}\", e)))\n }\n }\n }\n\n /// List memories with filtering\n pub async fn list_memories(&self, payload: MemoryOperationPayload) -> MemoryToolsResult {\n let params = FilterParams::from_payload(&payload, self.default_limit)?;\n\n info!(\"Listing memories with filters\");\n\n // Convert parameters to Filters\n let mut filters = cortex_mem_core::types::Filters::default();\n\n if let Some(user_id) = params.user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = params.agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(memory_type) = params.memory_type {\n if let Ok(mt) = cortex_mem_core::types::MemoryType::parse_with_result(&memory_type) {\n filters.memory_type = Some(mt);\n }\n }\n\n match self.memory_manager.list(&filters, Some(params.limit)).await {\n Ok(memories) => {\n let count = memories.len();\n info!(\"Listed {} memories\", count);\n\n let memories_json: Vec = memories\n .into_iter()\n .map(|memory| memory_to_json(&memory))\n .collect();\n\n let data = json!({\n \"count\": count,\n \"memories\": memories_json\n });\n\n Ok(MemoryOperationResponse::success_with_data(\n \"List completed successfully\",\n data,\n ))\n }\n Err(e) => {\n error!(\"Failed to list memories: {}\", e);\n Err(MemoryToolsError::Runtime(format!(\"Failed to list memories: {}\", e)))\n }\n }\n }\n\n /// Get a specific memory by ID\n pub async fn get_memory(&self, payload: MemoryOperationPayload) -> MemoryToolsResult {\n let memory_id = payload.memory_id\n .ok_or_else(|| MemoryToolsError::InvalidInput(\"Memory ID is required\".to_string()))?;\n\n info!(\"Getting memory with ID: {}\", memory_id);\n\n match self.memory_manager.get(&memory_id).await {\n Ok(Some(memory)) => {\n let memory_json = memory_to_json(&memory);\n let data = json!({\n \"memory\": memory_json\n });\n\n Ok(MemoryOperationResponse::success_with_data(\n \"Memory retrieved successfully\",\n data,\n ))\n }\n Ok(None) => {\n error!(\"Memory not found: {}\", memory_id);\n Err(MemoryToolsError::MemoryNotFound(memory_id))\n }\n Err(e) => {\n error!(\"Failed to get memory: {}\", e);\n Err(MemoryToolsError::Runtime(format!(\"Failed to get memory: {}\", e)))\n }\n }\n }\n}\n\n/// Convert a Memory object to JSON\nfn memory_to_json(memory: &Memory) -> Value {\n let mut metadata_obj = json!({});\n\n if let Some(user_id) = &memory.metadata.user_id {\n metadata_obj[\"user_id\"] = Value::String(user_id.clone());\n }\n\n if let Some(agent_id) = &memory.metadata.agent_id {\n metadata_obj[\"agent_id\"] = Value::String(agent_id.clone());\n }\n\n if let Some(run_id) = &memory.metadata.run_id {\n metadata_obj[\"run_id\"] = Value::String(run_id.clone());\n }\n\n if let Some(actor_id) = &memory.metadata.actor_id {\n metadata_obj[\"actor_id\"] = Value::String(actor_id.clone());\n }\n\n if let Some(role) = &memory.metadata.role {\n metadata_obj[\"role\"] = Value::String(role.clone());\n }\n\n metadata_obj[\"memory_type\"] = Value::String(format!(\"{:?}\", memory.metadata.memory_type));\n\n metadata_obj[\"hash\"] = Value::String(memory.metadata.hash.clone());\n\n metadata_obj[\"importance_score\"] = Value::Number(serde_json::Number::from_f64(memory.metadata.importance_score as f64).unwrap());\n\n if !memory.metadata.entities.is_empty() {\n metadata_obj[\"entities\"] = Value::Array(\n memory.metadata.entities.iter()\n .map(|e| Value::String(e.clone()))\n .collect()\n );\n }\n\n if !memory.metadata.topics.is_empty() {\n metadata_obj[\"topics\"] = Value::Array(\n memory.metadata.topics.iter()\n .map(|t| Value::String(t.clone()))\n .collect()\n );\n }\n\n if !memory.metadata.custom.is_empty() {\n metadata_obj[\"custom\"] = Value::Object(\n memory.metadata.custom.iter()\n .map(|(k, v)| (k.clone(), v.clone()))\n .collect()\n );\n }\n\n json!({\n \"id\": memory.id,\n \"content\": memory.content,\n \"created_at\": memory.created_at.to_rfc3339(),\n \"updated_at\": memory.updated_at.to_rfc3339(),\n \"metadata\": metadata_obj\n })\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 5.0, - "lines_of_code": 50, - "number_of_classes": 0, - "number_of_functions": 8 + "cyclomatic_complexity": 24.0, + "lines_of_code": 281, + "number_of_classes": 1, + "number_of_functions": 6 }, "dependencies": [ { - "dependency_type": "library", + "dependency_type": "error", + "is_external": false, + "line_number": null, + "name": "MemoryToolsError", + "path": "crate::errors", + "version": null + }, + { + "dependency_type": "type", + "is_external": false, + "line_number": null, + "name": "MemoryOperationPayload", + "path": "crate::types", + "version": null + }, + { + "dependency_type": "core", "is_external": true, - "line_number": 5, - "name": "async_trait", - "path": null, + "line_number": null, + "name": "MemoryManager", + "path": "cortex_mem_core::memory", "version": null } ], - "detailed_description": "This component defines a comprehensive trait interface `VectorStore` that abstracts vector database operations for memory management in an AI/ML context. It enables insertion, retrieval, search (with and without thresholds), update, deletion, listing, and health checking of memory entries. The trait is designed to be object-safe using `dyn_clone::DynClone`, allowing dynamic dispatch while maintaining ownership semantics. It leverages async/await for non-blocking I/O operations and integrates with the crate's error handling and data model via `Result`, `Memory`, and `ScoredMemory` types. The module also re-exports the Qdrant-specific implementation, indicating it as the primary or default backend.", + "detailed_description": "This component implements the core business logic for a memory management system, providing operations to store, query, list, and retrieve memory entries. It acts as an intermediary layer between external requests (via MemoryOperationPayload) and the underlying MemoryManager from cortex-mem-core. Each operation validates input, constructs appropriate parameters, interacts with the memory manager, and returns structured responses. The component handles metadata enrichment, logging, error mapping, and JSON serialization for API responses. It supports semantic search through query operations with filters for user_id, agent_id, memory_type, and topics.", "interfaces": [ { - "description": "Core trait defining vector store operations for memory management", - "interface_type": "trait", - "name": "VectorStore", + "description": "Main struct that encapsulates memory operations with dependency injection of MemoryManager", + "interface_type": "struct", + "name": "MemoryOperations", "parameters": [], "return_type": null, "visibility": "public" }, { - "description": "Inserts a new memory into the vector store", + "description": "Constructor for MemoryOperations with dependency injection", "interface_type": "method", - "name": "VectorStore::insert", + "name": "new", "parameters": [ { - "description": "Reference to the memory to insert", + "description": null, "is_optional": false, - "name": "memory", - "param_type": "&Memory" - } - ], - "return_type": "Result<()>", - "visibility": "public" - }, - { - "description": "Searches for similar memories based on query vector and filters", - "interface_type": "method", - "name": "VectorStore::search", - "parameters": [ + "name": "memory_manager", + "param_type": "std::sync::Arc" + }, { - "description": "The query embedding vector", - "is_optional": false, - "name": "query_vector", - "param_type": "&[f32]" + "description": null, + "is_optional": true, + "name": "default_user_id", + "param_type": "Option" }, { - "description": "Filter criteria for the search", - "is_optional": false, - "name": "filters", - "param_type": "&Filters" + "description": null, + "is_optional": true, + "name": "default_agent_id", + "param_type": "Option" }, { - "description": "Maximum number of results to return", + "description": null, "is_optional": false, - "name": "limit", + "name": "default_limit", "param_type": "usize" } ], - "return_type": "Result>", + "return_type": "Self", "visibility": "public" }, { - "description": "Searches for similar memories with an optional similarity score threshold", + "description": "Stores a new memory entry with metadata", "interface_type": "method", - "name": "VectorStore::search_with_threshold", + "name": "store_memory", "parameters": [ { - "description": "The query embedding vector", - "is_optional": false, - "name": "query_vector", - "param_type": "&[f32]" - }, - { - "description": "Filter criteria for the search", + "description": null, "is_optional": false, - "name": "filters", - "param_type": "&Filters" - }, + "name": "payload", + "param_type": "MemoryOperationPayload" + } + ], + "return_type": "MemoryToolsResult", + "visibility": "public" + }, + { + "description": "Queries memories based on semantic similarity with filters", + "interface_type": "method", + "name": "query_memory", + "parameters": [ { - "description": "Maximum number of results to return", + "description": null, "is_optional": false, - "name": "limit", - "param_type": "usize" - }, + "name": "payload", + "param_type": "MemoryOperationPayload" + } + ], + "return_type": "MemoryToolsResult", + "visibility": "public" + }, + { + "description": "Lists memories with filtering options", + "interface_type": "method", + "name": "list_memories", + "parameters": [ { - "description": "Minimum similarity score required", - "is_optional": true, - "name": "score_threshold", - "param_type": "Option" + "description": null, + "is_optional": false, + "name": "payload", + "param_type": "MemoryOperationPayload" } ], - "return_type": "Result>", + "return_type": "MemoryToolsResult", "visibility": "public" }, { - "description": "Updates an existing memory in the vector store", + "description": "Retrieves a specific memory by ID", "interface_type": "method", - "name": "VectorStore::update", + "name": "get_memory", "parameters": [ { - "description": "Reference to the updated memory", + "description": null, "is_optional": false, - "name": "memory", - "param_type": "&Memory" + "name": "payload", + "param_type": "MemoryOperationPayload" } ], - "return_type": "Result<()>", + "return_type": "MemoryToolsResult", "visibility": "public" }, { - "description": "Deletes a memory entry by its ID", - "interface_type": "method", - "name": "VectorStore::delete", + "description": "Converts a Memory object to JSON representation", + "interface_type": "function", + "name": "memory_to_json", "parameters": [ { - "description": "ID of the memory to delete", + "description": null, "is_optional": false, - "name": "id", - "param_type": "&str" + "name": "memory", + "param_type": "&Memory" } ], - "return_type": "Result<()>", + "return_type": "Value", + "visibility": "private" + } + ], + "responsibilities": [ + "Handle storage of new memory entries with proper metadata handling", + "Execute semantic queries on memories with filtering capabilities", + "List memories based on various filter criteria and limits", + "Retrieve specific memory entries by ID with proper error handling", + "Convert internal memory structures to JSON representation for API responses" + ] + }, + { + "code_dossier": { + "code_purpose": "specificfeature", + "description": "A processor responsible for passively learning from conversations. This component should be used by the application/framework layer after each conversation turn to automatically update memories in the background.", + "file_path": "cortex-mem-rig/src/processor.rs", + "functions": [ + "new", + "process_turn" + ], + "importance_score": 0.8, + "interfaces": [ + "ConversationProcessor", + "process_turn", + "new" + ], + "name": "processor.rs", + "source_summary": "use std::sync::Arc;\nuse tracing::error;\n\nuse cortex_mem_core::{\n memory::MemoryManager,\n types::{MemoryMetadata, MemoryResult, Message},\n Result,\n};\n\n/// A processor responsible for passively learning from conversations.\n/// This component should be used by the application/framework layer after each\n/// conversation turn to automatically update memories in the background.\npub struct ConversationProcessor {\n memory_manager: Arc,\n}\n\nimpl ConversationProcessor {\n /// Creates a new `ConversationProcessor`.\n ///\n /// # Arguments\n ///\n /// * `memory_manager` - An `Arc` wrapped `MemoryManager` from `cortex-mem-core`.\n pub fn new(memory_manager: Arc) -> Self {\n Self { memory_manager }\n }\n\n /// Processes a conversation turn, allowing the memory system to learn from it.\n ///\n /// This method invokes the core `add_memory` function, which triggers the\n /// \"extract-retrieve-reason-act\" pipeline to intelligently update the knowledge base.\n ///\n /// # Arguments\n ///\n /// * `messages` - A slice of `cortex_mem_core::types::Message` representing the conversation turn.\n /// * `metadata` - Metadata associated with the memory, such as `user_id` or `agent_id`.\n ///\n /// # Returns\n ///\n /// A `Result` containing a `Vec` which details the actions\n /// (`Create`, `Update`, `Delete`, etc.) performed by the memory system.\n pub async fn process_turn(\n &self,\n messages: &[Message],\n metadata: MemoryMetadata,\n ) -> Result> {\n match self.memory_manager.add_memory(messages, metadata).await {\n Ok(results) => Ok(results),\n Err(e) => {\n error!(\"Failed to process conversation turn for memory: {}\", e);\n Err(e)\n }\n }\n }\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 4.0, + "lines_of_code": 54, + "number_of_classes": 1, + "number_of_functions": 2 + }, + "dependencies": [ + { + "dependency_type": "logging", + "is_external": true, + "line_number": 2, + "name": "tracing", + "path": null, + "version": null + }, + { + "dependency_type": "library", + "is_external": false, + "line_number": 4, + "name": "cortex_mem_core", + "path": null, + "version": null + } + ], + "detailed_description": "The ConversationProcessor struct is designed to facilitate passive learning from conversation turns by interfacing with a MemoryManager to update the system's knowledge base. It wraps the core memory management logic and exposes a clean API for processing conversation slices and associated metadata. The primary method, `process_turn`, asynchronously invokes the `add_memory` function on the MemoryManager, which triggers an 'extract-retrieve-reason-act' pipeline to determine necessary memory operations (create, update, delete). Error handling is implemented via structured logging using the `tracing` crate, ensuring failures are logged at the error level while preserving the original error for upstream handling. This component acts as a bridge between the application logic and the core memory engine, abstracting complex memory update workflows into a simple, reusable interface.", + "interfaces": [ + { + "description": "Main processor type that encapsulates memory learning logic", + "interface_type": "struct", + "name": "ConversationProcessor", + "parameters": [], + "return_type": null, "visibility": "public" }, { - "description": "Retrieves a memory entry by its ID", + "description": "Constructs a new ConversationProcessor with a shared MemoryManager", "interface_type": "method", - "name": "VectorStore::get", + "name": "new", "parameters": [ { - "description": "ID of the memory to retrieve", + "description": "Shared reference to the core memory management system", "is_optional": false, - "name": "id", - "param_type": "&str" + "name": "memory_manager", + "param_type": "Arc" } ], - "return_type": "Result>", + "return_type": "ConversationProcessor", "visibility": "public" }, { - "description": "Lists all memories with optional filtering and limiting", + "description": "Processes a conversation turn to update memories using the core pipeline", "interface_type": "method", - "name": "VectorStore::list", + "name": "process_turn", "parameters": [ { - "description": "Filter criteria for listing", + "description": "Slice of messages representing the conversation turn", "is_optional": false, - "name": "filters", - "param_type": "&Filters" + "name": "messages", + "param_type": "&[Message]" }, { - "description": "Maximum number of results to return", - "is_optional": true, - "name": "limit", - "param_type": "Option" + "description": "Metadata associated with the memory (e.g., user_id, agent_id)", + "is_optional": false, + "name": "metadata", + "param_type": "MemoryMetadata" } ], - "return_type": "Result>", - "visibility": "public" - }, - { - "description": "Checks if the vector store backend is healthy and responsive", - "interface_type": "method", - "name": "VectorStore::health_check", - "parameters": [], - "return_type": "Result", + "return_type": "Result>", "visibility": "public" } ], "responsibilities": [ - "Define a standardized interface for vector-based memory storage operations", - "Enable polymorphic behavior through trait object cloning with dyn_clone", - "Provide asynchronous methods for non-blocking interaction with vector databases", - "Support similarity search with filtering and thresholding capabilities", - "Expose health check functionality for monitoring storage backend status" + "Orchestrate background memory updates after each conversation turn", + "Interface with MemoryManager to trigger the 'extract-retrieve-reason-act' learning pipeline", + "Handle error logging and propagation during memory processing", + "Provide a thread-safe, reusable component via Arc-wrapped dependencies" ] }, { "code_dossier": { - "code_purpose": "types", - "description": "Defines common data structures and enums for memory operation payloads, responses, and parameter extraction in a memory management system.", - "file_path": "cortex-mem-tools/src/types.rs", + "code_purpose": "tool", + "description": "Functional tool code for interacting with memory operations via a set of standardized tools (Store, Query, List, Get Memory). Acts as a bridge between external tool systems (like MCP/RIG) and the core memory manager.", + "file_path": "cortex-mem-rig/src/tool.rs", "functions": [ - "MemoryOperationResponse::success", - "MemoryOperationResponse::success_with_data", - "MemoryOperationResponse::error", - "QueryParams::from_payload", - "StoreParams::from_payload", - "FilterParams::from_payload" + "new", + "definition", + "call", + "store_memory", + "query_memory", + "list_memories", + "get_memory", + "create_memory_tools" ], "importance_score": 0.8, "interfaces": [ - "MemoryOperationPayload", - "MemoryOperationType", - "MemoryOperationResponse", - "QueryParams", - "StoreParams", - "FilterParams" + "StoreMemoryTool", + "QueryMemoryTool", + "ListMemoriesTool", + "GetMemoryTool", + "MemoryTools", + "MemoryToolsBase" ], - "name": "types.rs", - "source_summary": "use serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\n\n/// Common data structure for memory operation payloads\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct MemoryOperationPayload {\n /// The content to store (for store operations)\n pub content: Option,\n\n /// The query string (for search/query operations)\n pub query: Option,\n\n /// Memory ID (for get operations)\n pub memory_id: Option,\n\n /// User ID for filtering\n pub user_id: Option,\n\n /// Agent ID for filtering\n pub agent_id: Option,\n\n /// Type of memory\n pub memory_type: Option,\n\n /// Topics to filter by\n pub topics: Option>,\n\n /// Keywords to filter by\n pub keywords: Option>,\n\n /// Maximum number of results\n pub limit: Option,\n\n /// Minimum salience/importance score\n pub min_salience: Option,\n\n /// Maximum number of results (alias for limit)\n pub k: Option,\n\n /// Additional metadata\n pub metadata: Option>,\n}\n\nimpl Default for MemoryOperationPayload {\n fn default() -> Self {\n Self {\n content: None,\n query: None,\n memory_id: None,\n user_id: None,\n agent_id: None,\n memory_type: None,\n topics: None,\n keywords: None,\n limit: None,\n min_salience: None,\n k: None,\n metadata: None,\n }\n }\n}\n\n/// Memory operation types supported by the tools\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\n#[serde(rename_all = \"lowercase\")]\npub enum MemoryOperationType {\n Store,\n Query,\n List,\n Get,\n}\n\n/// Common response structure for memory operations\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct MemoryOperationResponse {\n /// Whether the operation was successful\n pub success: bool,\n\n /// Message describing the result\n pub message: String,\n\n /// Optional data payload\n pub data: Option,\n\n /// Optional error details\n pub error: Option,\n}\n\nimpl MemoryOperationResponse {\n /// Create a successful response\n pub fn success(message: impl Into) -> Self {\n Self {\n success: true,\n message: message.into(),\n data: None,\n error: None,\n }\n }\n\n /// Create a successful response with data\n pub fn success_with_data(message: impl Into, data: serde_json::Value) -> Self {\n Self {\n success: true,\n message: message.into(),\n data: Some(data),\n error: None,\n }\n }\n\n /// Create an error response\n pub fn error(error: impl Into) -> Self {\n Self {\n success: false,\n message: \"Operation failed\".to_string(),\n data: None,\n error: Some(error.into()),\n }\n }\n}\n\n/// Helper struct to extract query parameters\npub struct QueryParams {\n pub query: String,\n pub limit: usize,\n pub min_salience: Option,\n pub memory_type: Option,\n pub topics: Option>,\n pub user_id: Option,\n pub agent_id: Option,\n}\n\nimpl QueryParams {\n pub fn from_payload(payload: &MemoryOperationPayload, default_limit: usize) -> crate::errors::MemoryToolsResult {\n let query = payload.query.as_ref()\n .ok_or_else(|| crate::errors::MemoryToolsError::InvalidInput(\"Query is required\".to_string()))?\n .clone();\n\n let limit = payload.limit\n .or(payload.k)\n .unwrap_or(default_limit);\n\n Ok(Self {\n query,\n limit,\n min_salience: payload.min_salience,\n memory_type: payload.memory_type.clone(),\n topics: payload.topics.clone(),\n user_id: payload.user_id.clone(),\n agent_id: payload.agent_id.clone(),\n })\n }\n}\n\n/// Helper struct to extract store parameters\npub struct StoreParams {\n pub content: String,\n pub user_id: String,\n pub agent_id: Option,\n pub memory_type: String,\n pub topics: Option>,\n}\n\nimpl StoreParams {\n pub fn from_payload(payload: &MemoryOperationPayload, default_user_id: Option, default_agent_id: Option) -> crate::errors::MemoryToolsResult {\n let content = payload.content.as_ref()\n .ok_or_else(|| crate::errors::MemoryToolsError::InvalidInput(\"Content is required\".to_string()))?\n .clone();\n\n let user_id = payload.user_id\n .clone()\n .or(default_user_id)\n .ok_or_else(|| crate::errors::MemoryToolsError::InvalidInput(\"User ID is required\".to_string()))?;\n\n let agent_id = payload.agent_id.clone().or(default_agent_id);\n\n let memory_type = payload.memory_type\n .clone()\n .unwrap_or_else(|| \"conversational\".to_string());\n\n Ok(Self {\n content,\n user_id,\n agent_id,\n memory_type,\n topics: payload.topics.clone(),\n })\n }\n}\n\n/// Helper struct to extract filter parameters\npub struct FilterParams {\n pub user_id: Option,\n pub agent_id: Option,\n pub memory_type: Option,\n pub limit: usize,\n}\n\nimpl FilterParams {\n pub fn from_payload(payload: &MemoryOperationPayload, default_limit: usize) -> crate::errors::MemoryToolsResult {\n let limit = payload.limit.or(payload.k).unwrap_or(default_limit);\n\n Ok(Self {\n user_id: payload.user_id.clone(),\n agent_id: payload.agent_id.clone(),\n memory_type: payload.memory_type.clone(),\n limit,\n })\n }\n}\n" + "name": "tool.rs", + "source_summary": "use cortex_mem_config::Config;\nuse cortex_mem_core::MemoryManager;\nuse cortex_mem_tools::{MemoryOperations, get_mcp_tool_definitions, map_mcp_arguments_to_payload};\nuse rig::{completion::ToolDefinition, tool::Tool};\nuse serde::{Deserialize, Serialize};\nuse serde_json::{Map, Value, json};\nuse std::sync::Arc;\nuse tracing::{error, info};\n\n// Re-export the error type from cortex-mem-tools for backward compatibility\npub use cortex_mem_tools::MemoryToolsError as MemoryToolError;\n\n/// Memory tool configuration\npub struct MemoryToolConfig {\n pub default_user_id: Option,\n pub default_agent_id: Option,\n pub max_search_results: Option,\n pub auto_enhance: Option,\n pub search_similarity_threshold: Option,\n}\n\n/// Store Memory tool arguments\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct StoreMemoryArgs {\n pub content: String,\n pub user_id: Option,\n pub agent_id: Option,\n pub memory_type: Option,\n pub topics: Option>,\n}\n\n/// Query Memory tool arguments\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct QueryMemoryArgs {\n pub query: String,\n pub k: Option,\n pub memory_type: Option,\n pub min_salience: Option,\n pub topics: Option>,\n pub user_id: Option,\n pub agent_id: Option,\n}\n\n/// List Memories tool arguments\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ListMemoriesArgs {\n pub limit: Option,\n pub memory_type: Option,\n pub user_id: Option,\n pub agent_id: Option,\n}\n\n/// Get Memory tool arguments\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct GetMemoryArgs {\n pub memory_id: String,\n}\n\n/// Common tool output\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct MemoryToolOutput {\n pub success: bool,\n pub message: String,\n pub data: Option,\n}\n\n/// Base struct for memory tools that shares common functionality\npub struct MemoryToolsBase {\n operations: MemoryOperations,\n config: MemoryToolConfig,\n}\n\nimpl MemoryToolsBase {\n /// Create a new memory tools base with the provided memory manager and configuration\n pub fn new(\n memory_manager: Arc,\n global_config: &Config,\n custom_config: Option,\n ) -> Self {\n let mut config = MemoryToolConfig::default();\n\n // Apply custom config overrides if provided\n if let Some(custom) = custom_config {\n config.default_user_id = custom.default_user_id.or(config.default_user_id);\n config.default_agent_id = custom.default_agent_id.or(config.default_agent_id);\n config.max_search_results = custom.max_search_results.or(config.max_search_results);\n config.auto_enhance = custom.auto_enhance.or(config.auto_enhance);\n config.search_similarity_threshold = custom\n .search_similarity_threshold\n .or(config.search_similarity_threshold);\n }\n\n // Fallback to values from global config if not set in custom\n if config.max_search_results.is_none() {\n config.max_search_results = Some(global_config.memory.max_search_results);\n }\n if config.auto_enhance.is_none() {\n config.auto_enhance = Some(global_config.memory.auto_enhance);\n }\n if config.search_similarity_threshold.is_none() {\n config.search_similarity_threshold = global_config.memory.search_similarity_threshold;\n }\n\n // Create operations handler\n let operations = MemoryOperations::new(\n memory_manager.clone(),\n config.default_user_id.clone(),\n config.default_agent_id.clone(),\n config.max_search_results.unwrap_or(10),\n );\n\n Self { operations, config }\n }\n\n /// Convert JSON values to a Map for the map_mcp_arguments_to_payload function\n fn args_to_map(&self, args: &serde_json::Value) -> Map {\n if let Value::Object(map) = args {\n map.clone()\n } else {\n Map::new()\n }\n }\n}\n\n/// Store Memory Tool\npub struct StoreMemoryTool {\n base: Arc,\n}\n\nimpl StoreMemoryTool {\n pub fn new(base: Arc) -> Self {\n Self { base }\n }\n}\n\nimpl Tool for StoreMemoryTool {\n const NAME: &'static str = \"store_memory\";\n\n type Error = MemoryToolError;\n type Args = StoreMemoryArgs;\n type Output = MemoryToolOutput;\n\n fn definition(\n &self,\n _prompt: String,\n ) -> impl std::future::Future + Send + Sync {\n async move {\n // Get tool definition from MCP definitions\n let tool_definitions = get_mcp_tool_definitions();\n let def = tool_definitions\n .iter()\n .find(|d| d.name == \"store_memory\")\n .expect(\" store_memory tool definition should exist\");\n\n ToolDefinition {\n name: Self::NAME.to_string(),\n description: def.description.clone().unwrap_or_default(),\n parameters: def.input_schema.clone(),\n }\n }\n }\n\n fn call(\n &self,\n args: Self::Args,\n ) -> impl std::future::Future> + Send {\n async move {\n // Convert args to JSON Value\n let args_json = json!(args);\n let arguments = self.base.args_to_map(&args_json);\n\n // Map to payload using shared function\n let payload =\n map_mcp_arguments_to_payload(&arguments, &self.base.config.default_agent_id);\n\n match self.base.operations.store_memory(payload).await {\n Ok(response) => {\n info!(\"Memory stored via rig tool\");\n Ok(MemoryToolOutput {\n success: response.success,\n message: response.message,\n data: response.data,\n })\n }\n Err(e) => {\n error!(\"Failed to store memory via rig tool: {}\", e);\n Err(e)\n }\n }\n }\n }\n}\n\n/// Query Memory Tool\npub struct QueryMemoryTool {\n base: Arc,\n}\n\nimpl QueryMemoryTool {\n pub fn new(base: Arc) -> Self {\n Self { base }\n }\n}\n\nimpl Tool for QueryMemoryTool {\n const NAME: &'static str = \"query_memory\";\n\n type Error = MemoryToolError;\n type Args = QueryMemoryArgs;\n type Output = MemoryToolOutput;\n\n fn definition(\n &self,\n _prompt: String,\n ) -> impl std::future::Future + Send + Sync {\n async move {\n // Get tool definition from MCP definitions\n let tool_definitions = get_mcp_tool_definitions();\n let def = tool_definitions\n .iter()\n .find(|d| d.name == \"query_memory\")\n .expect(\"query_memory tool definition should exist\");\n\n ToolDefinition {\n name: Self::NAME.to_string(),\n description: def.description.clone().unwrap_or_default(),\n parameters: def.input_schema.clone(),\n }\n }\n }\n\n fn call(\n &self,\n args: Self::Args,\n ) -> impl std::future::Future> + Send {\n async move {\n // Convert args to JSON Value\n let args_json = json!(args);\n let arguments = self.base.args_to_map(&args_json);\n\n // Map to payload using shared function\n let payload =\n map_mcp_arguments_to_payload(&arguments, &self.base.config.default_agent_id);\n\n match self.base.operations.query_memory(payload).await {\n Ok(response) => Ok(MemoryToolOutput {\n success: response.success,\n message: response.message,\n data: response.data,\n }),\n Err(e) => {\n error!(\"Failed to query memories via rig tool: {}\", e);\n Err(e)\n }\n }\n }\n }\n}\n\n/// List Memories Tool\npub struct ListMemoriesTool {\n base: Arc,\n}\n\nimpl ListMemoriesTool {\n pub fn new(base: Arc) -> Self {\n Self { base }\n }\n}\n\nimpl Tool for ListMemoriesTool {\n const NAME: &'static str = \"list_memories\";\n\n type Error = MemoryToolError;\n type Args = ListMemoriesArgs;\n type Output = MemoryToolOutput;\n\n fn definition(\n &self,\n _prompt: String,\n ) -> impl std::future::Future + Send + Sync {\n async move {\n // Get tool definition from MCP definitions\n let tool_definitions = get_mcp_tool_definitions();\n let def = tool_definitions\n .iter()\n .find(|d| d.name == \"list_memories\")\n .expect(\"list_memories tool definition should exist\");\n\n ToolDefinition {\n name: Self::NAME.to_string(),\n description: def.description.clone().unwrap_or_default(),\n parameters: def.input_schema.clone(),\n }\n }\n }\n\n fn call(\n &self,\n args: Self::Args,\n ) -> impl std::future::Future> + Send {\n async move {\n // Convert args to JSON Value\n let args_json = json!(args);\n let arguments = self.base.args_to_map(&args_json);\n\n // Map to payload using shared function\n let payload =\n map_mcp_arguments_to_payload(&arguments, &self.base.config.default_agent_id);\n\n match self.base.operations.list_memories(payload).await {\n Ok(response) => Ok(MemoryToolOutput {\n success: response.success,\n message: response.message,\n data: response.data,\n }),\n Err(e) => {\n error!(\"Failed to list memories via rig tool: {}\", e);\n Err(e)\n }\n }\n }\n }\n}\n\n/// Get Memory Tool\npub struct GetMemoryTool {\n base: Arc,\n}\n\nimpl GetMemoryTool {\n pub fn new(base: Arc) -> Self {\n Self { base }\n }\n}\n\nimpl Tool for GetMemoryTool {\n const NAME: &'static str = \"get_memory\";\n\n type Error = MemoryToolError;\n type Args = GetMemoryArgs;\n type Output = MemoryToolOutput;\n\n fn definition(\n &self,\n _prompt: String,\n ) -> impl std::future::Future + Send + Sync {\n async move {\n // Get tool definition from MCP definitions\n let tool_definitions = get_mcp_tool_definitions();\n let def = tool_definitions\n .iter()\n .find(|d| d.name == \"get_memory\")\n .expect(\"get_memory tool definition should exist\");\n\n ToolDefinition {\n name: Self::NAME.to_string(),\n description: def.description.clone().unwrap_or_default(),\n parameters: def.input_schema.clone(),\n }\n }\n }\n\n fn call(\n &self,\n args: Self::Args,\n ) -> impl std::future::Future> + Send {\n async move {\n // Convert args to JSON Value\n let args_json = json!(args);\n let arguments = self.base.args_to_map(&args_json);\n\n // Map to payload using shared function\n let payload =\n map_mcp_arguments_to_payload(&arguments, &self.base.config.default_agent_id);\n\n match self.base.operations.get_memory(payload).await {\n Ok(response) => Ok(MemoryToolOutput {\n success: response.success,\n message: response.message,\n data: response.data,\n }),\n Err(e) => {\n error!(\"Failed to get memory via rig tool: {}\", e);\n Err(e)\n }\n }\n }\n }\n}\n\n/// MemoryTools struct that provides all memory tools\npub struct MemoryTools {\n base: Arc,\n}\n\nimpl MemoryTools {\n /// Create new memory tools with the provided memory manager and configuration\n pub fn new(\n memory_manager: Arc,\n global_config: &Config,\n custom_config: Option,\n ) -> Self {\n let base = Arc::new(MemoryToolsBase::new(\n memory_manager,\n global_config,\n custom_config,\n ));\n Self { base }\n }\n\n /// Get the store memory tool\n pub fn store_memory(&self) -> StoreMemoryTool {\n StoreMemoryTool::new(self.base.clone())\n }\n\n /// Get the query memory tool\n pub fn query_memory(&self) -> QueryMemoryTool {\n QueryMemoryTool::new(self.base.clone())\n }\n\n /// Get the list memories tool\n pub fn list_memories(&self) -> ListMemoriesTool {\n ListMemoriesTool::new(self.base.clone())\n }\n\n /// Get the get memory tool\n pub fn get_memory(&self) -> GetMemoryTool {\n GetMemoryTool::new(self.base.clone())\n }\n}\n\nimpl Default for MemoryToolConfig {\n fn default() -> Self {\n Self {\n default_user_id: None,\n default_agent_id: None,\n max_search_results: None, // Will be taken from global config\n auto_enhance: None, // Will be taken from global config\n search_similarity_threshold: None, // Will be taken from global config\n }\n }\n}\n\n/// Create memory tools with default configuration\npub fn create_memory_tools(\n memory_manager: Arc,\n global_config: &Config,\n custom_config: Option,\n) -> MemoryTools {\n MemoryTools::new(memory_manager, global_config, custom_config)\n}" }, "complexity_metrics": { - "cyclomatic_complexity": 10.0, - "lines_of_code": 209, - "number_of_classes": 6, - "number_of_functions": 6 + "cyclomatic_complexity": 20.0, + "lines_of_code": 452, + "number_of_classes": 11, + "number_of_functions": 22 }, "dependencies": [ { - "dependency_type": "library", - "is_external": true, - "line_number": 1, - "name": "serde", + "dependency_type": "use", + "is_external": false, + "line_number": null, + "name": "cortex_mem_config::Config", "path": null, "version": null }, { - "dependency_type": "standard_library", + "dependency_type": "use", "is_external": false, - "line_number": 2, - "name": "std::collections::HashMap", + "line_number": null, + "name": "cortex_mem_core::MemoryManager", + "path": null, + "version": null + }, + { + "dependency_type": "use", + "is_external": false, + "line_number": null, + "name": "cortex_mem_tools", + "path": "cortex_mem_tools", + "version": null + }, + { + "dependency_type": "use", + "is_external": true, + "line_number": null, + "name": "rig", + "path": "rig", + "version": null + }, + { + "dependency_type": "use", + "is_external": true, + "line_number": null, + "name": "serde", + "path": "serde", + "version": null + }, + { + "dependency_type": "use", + "is_external": true, + "line_number": null, + "name": "serde_json", + "path": "serde_json", + "version": null + }, + { + "dependency_type": "use", + "is_external": true, + "line_number": null, + "name": "std::sync::Arc", "path": null, "version": null + }, + { + "dependency_type": "use", + "is_external": true, + "line_number": null, + "name": "tracing", + "path": "tracing", + "version": null } ], - "detailed_description": "This component defines the core types used across the memory tools module. It includes a flexible payload structure (MemoryOperationPayload) that supports various memory operations such as store, query, list, and get. The payload uses optional fields to maintain flexibility while supporting multiple operation types. Three helper structs (QueryParams, StoreParams, FilterParams) provide typed parameter extraction from the generic payload with validation logic, reducing boilerplate in business logic. The MemoryOperationResponse provides standardized success/error responses with message and optional data. The MemoryOperationType enum safely represents supported operations. These types enable type-safe, serializable communication between components, especially in API boundaries and inter-service communication.", + "detailed_description": "This component implements a suite of memory interaction tools designed for integration with external agent/tool frameworks (e.g., MCP via RIG). It provides four primary tools: StoreMemoryTool, QueryMemoryTool, ListMemoriesTool, and GetMemoryTool. Each tool wraps a corresponding operation from the MemoryOperations service. The core of the implementation is the MemoryToolsBase struct, which holds shared state including a MemoryOperations instance and a configuration object (MemoryToolConfig). This base is shared among all tool instances via Arc, promoting code reuse and consistent configuration. The tools follow a uniform pattern: they retrieve standardized tool definitions (name, description, parameters) from an MCP source and implement the 'call' method to execute the underlying memory operation. Arguments are converted from JSON to a payload format using shared utility functions from cortex_mem_tools. The top-level MemoryTools struct acts as a factory, providing access to all individual tool instances. Error handling is consistent, logging errors via tracing and propagating MemoryToolError. The code is highly structured, leveraging Rust's type system with serde for serialization and async/await for non-blocking operations.", "interfaces": [ { - "description": "Generic payload container for all memory operations with optional fields for different operation types", + "description": "Arguments for storing a new memory.", "interface_type": "struct", - "name": "MemoryOperationPayload", + "name": "StoreMemoryArgs", "parameters": [ { - "description": "Content to store in memory", - "is_optional": true, + "description": "The content of the memory to store.", + "is_optional": false, "name": "content", - "param_type": "Option" - }, - { - "description": "Search query string", - "is_optional": true, - "name": "query", - "param_type": "Option" - }, - { - "description": "Specific memory entry identifier", - "is_optional": true, - "name": "memory_id", - "param_type": "Option" + "param_type": "String" }, { - "description": "User identifier for filtering", + "description": "Optional user ID associated with the memory.", "is_optional": true, "name": "user_id", "param_type": "Option" }, { - "description": "Agent identifier for filtering", + "description": "Optional agent ID associated with the memory.", "is_optional": true, "name": "agent_id", "param_type": "Option" }, { - "description": "Type/category of memory", + "description": "Optional type/category of the memory.", "is_optional": true, "name": "memory_type", "param_type": "Option" }, { - "description": "Topics associated with memory", + "description": "Optional list of topics associated with the memory.", "is_optional": true, "name": "topics", "param_type": "Option>" + } + ], + "return_type": null, + "visibility": "public" + }, + { + "description": "Arguments for querying memories based on a text query.", + "interface_type": "struct", + "name": "QueryMemoryArgs", + "parameters": [ + { + "description": "The search query string.", + "is_optional": false, + "name": "query", + "param_type": "String" }, { - "description": "Keywords for indexing and search", + "description": "Optional number of results to return.", "is_optional": true, - "name": "keywords", - "param_type": "Option>" + "name": "k", + "param_type": "Option" }, { - "description": "Maximum number of results", + "description": "Optional filter by memory type.", "is_optional": true, - "name": "limit", - "param_type": "Option" + "name": "memory_type", + "param_type": "Option" }, { - "description": "Minimum importance score filter", + "description": "Optional minimum salience score.", "is_optional": true, "name": "min_salience", "param_type": "Option" }, { - "description": "Alias for limit parameter", + "description": "Optional list of topics to filter by.", "is_optional": true, - "name": "k", - "param_type": "Option" + "name": "topics", + "param_type": "Option>" }, { - "description": "Additional extensible metadata", + "description": "Optional filter by user ID.", "is_optional": true, - "name": "metadata", - "param_type": "Option>" + "name": "user_id", + "param_type": "Option" + }, + { + "description": "Optional filter by agent ID.", + "is_optional": true, + "name": "agent_id", + "param_type": "Option" } ], "return_type": null, - "visibility": "pub" - }, - { - "description": "Enumeration of supported memory operation types", - "interface_type": "enum", - "name": "MemoryOperationType", - "parameters": [], - "return_type": null, - "visibility": "pub" + "visibility": "public" }, { - "description": "Standardized response format for memory operations", + "description": "Arguments for listing memories.", "interface_type": "struct", - "name": "MemoryOperationResponse", + "name": "ListMemoriesArgs", "parameters": [ { - "description": "Indicates if operation was successful", - "is_optional": false, - "name": "success", - "param_type": "bool" + "description": "Optional maximum number of memories to return.", + "is_optional": true, + "name": "limit", + "param_type": "Option" }, { - "description": "Result description message", - "is_optional": false, - "name": "message", - "param_type": "String" + "description": "Optional filter by memory type.", + "is_optional": true, + "name": "memory_type", + "param_type": "Option" }, { - "description": "Optional payload data", + "description": "Optional filter by user ID.", "is_optional": true, - "name": "data", - "param_type": "Option" + "name": "user_id", + "param_type": "Option" }, { - "description": "Error details if operation failed", + "description": "Optional filter by agent ID.", "is_optional": true, - "name": "error", + "name": "agent_id", "param_type": "Option" } ], "return_type": null, - "visibility": "pub" + "visibility": "public" }, { - "description": "Extracts and validates query parameters from a payload with default fallback", - "interface_type": "function", - "name": "QueryParams::from_payload", + "description": "Arguments for retrieving a single memory by ID.", + "interface_type": "struct", + "name": "GetMemoryArgs", "parameters": [ { - "description": "Source payload to extract from", - "is_optional": false, - "name": "payload", - "param_type": "&MemoryOperationPayload" - }, - { - "description": "Default limit if not specified in payload", + "description": "The unique identifier of the memory to retrieve.", "is_optional": false, - "name": "default_limit", - "param_type": "usize" + "name": "memory_id", + "param_type": "String" } ], - "return_type": "crate::errors::MemoryToolsResult", - "visibility": "pub" + "return_type": null, + "visibility": "public" }, { - "description": "Extracts and validates store parameters from payload with default values", - "interface_type": "function", - "name": "StoreParams::from_payload", + "description": "Standardized output structure for all memory tool operations.", + "interface_type": "struct", + "name": "MemoryToolOutput", "parameters": [ { - "description": "Source payload to extract from", + "description": "Indicates if the operation was successful.", "is_optional": false, - "name": "payload", - "param_type": "&MemoryOperationPayload" - }, - { - "description": "Default user ID if not in payload", - "is_optional": true, - "name": "default_user_id", - "param_type": "Option" + "name": "success", + "param_type": "bool" }, { - "description": "Default agent ID if not in payload", - "is_optional": true, - "name": "default_agent_id", - "param_type": "Option" - } - ], - "return_type": "crate::errors::MemoryToolsResult", - "visibility": "pub" - }, - { - "description": "Extracts filter parameters from payload with default limit", - "interface_type": "function", - "name": "FilterParams::from_payload", - "parameters": [ - { - "description": "Source payload to extract from", + "description": "A human-readable message describing the result.", "is_optional": false, - "name": "payload", - "param_type": "&MemoryOperationPayload" + "name": "message", + "param_type": "String" }, { - "description": "Default limit if not specified", - "is_optional": false, - "name": "default_limit", - "param_type": "usize" + "description": "Optional payload data returned by the operation.", + "is_optional": true, + "name": "data", + "param_type": "Option" } ], - "return_type": "crate::errors::MemoryToolsResult", - "visibility": "pub" - } - ], - "responsibilities": [ - "Define standardized data structures for memory operation requests and responses", - "Provide type-safe parameter extraction and validation from generic payloads", - "Enable serialization and deserialization of memory operation data through Serde", - "Support flexible filtering and querying capabilities via optional parameters", - "Ensure type safety and compile-time correctness for memory operations" - ] - }, - { - "code_dossier": { - "code_purpose": "tool", - "description": "Provides tool definitions and utility functions for MCP (Memory Control Protocol) operations, including memory storage, querying, listing, and retrieval by ID. Maps MCP arguments to internal payload format and handles error translation.", - "file_path": "cortex-mem-tools/src/mcp_tools.rs", - "functions": [ - "get_mcp_tool_definitions", - "map_mcp_arguments_to_payload", - "tools_error_to_mcp_error_code", - "get_tool_error_message" - ], - "importance_score": 0.8, - "interfaces": [ - "McpToolDefinition", - "get_mcp_tool_definitions", - "map_mcp_arguments_to_payload", - "tools_error_to_mcp_error_code", - "get_tool_error_message" - ], - "name": "mcp_tools.rs", - "source_summary": "use serde_json::{Map, Value, json};\nuse crate::{MemoryOperationPayload, MemoryToolsError};\n\n/// MCP工具定义\npub struct McpToolDefinition {\n pub name: String,\n pub title: Option,\n pub description: Option,\n pub input_schema: Value,\n pub output_schema: Option,\n}\n\n/// 获取所有MCP工具的定义\npub fn get_mcp_tool_definitions() -> Vec {\n vec![\n McpToolDefinition {\n name: \"store_memory\".into(),\n title: Some(\"Store Memory\".into()),\n description: Some(\"Store a new memory in the system with specified content and optional metadata.\".into()),\n input_schema: json!({\n \"type\": \"object\",\n \"properties\": {\n \"content\": {\n \"type\": \"string\",\n \"description\": \"The content of the memory to store\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"User ID associated with the memory (required unless --agent was specified on startup)\"\n },\n \"agent_id\": {\n \"type\": \"string\",\n \"description\": \"Agent ID associated with the memory (optional, defaults to configured agent)\"\n },\n \"memory_type\": {\n \"type\": \"string\",\n \"enum\": [\"conversational\", \"procedural\", \"factual\", \"semantic\", \"episodic\", \"personal\"],\n \"description\": \"Type of memory\",\n \"default\": \"conversational\"\n },\n \"topics\": {\n \"type\": \"array\",\n \"items\": {\"type\": \"string\"},\n \"description\": \"Topics to associate with the memory\"\n }\n },\n \"required\": [\"content\"]\n }),\n output_schema: Some(json!({\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"memory_id\": {\"type\": \"string\"},\n \"message\": {\"type\": \"string\"}\n },\n \"required\": [\"success\", \"memory_id\", \"message\"]\n })),\n },\n McpToolDefinition {\n name: \"query_memory\".into(),\n title: Some(\"Query Memory\".into()),\n description: Some(\"Search memories using semantic similarity and filters.\".into()),\n input_schema: json!({\n \"type\": \"object\",\n \"properties\": {\n \"query\": {\n \"type\": \"string\",\n \"description\": \"Query string for semantic search\"\n },\n \"k\": {\n \"type\": \"integer\",\n \"description\": \"Maximum number of results to return\",\n \"default\": 10\n },\n \"memory_type\": {\n \"type\": \"string\",\n \"enum\": [\"conversational\", \"procedural\", \"factual\", \"semantic\", \"episodic\", \"personal\"],\n \"description\": \"Type of memory to filter by\"\n },\n \"min_salience\": {\n \"type\": \"number\",\n \"description\": \"Minimum salience/importance score threshold (0-1)\",\n \"minimum\": 0,\n \"maximum\": 1\n },\n \"topics\": {\n \"type\": \"array\",\n \"items\": {\"type\": \"string\"},\n \"description\": \"Topics to filter memories by\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"User ID to filter memories (optional, defaults to configured agent's user)\"\n },\n \"agent_id\": {\n \"type\": \"string\",\n \"description\": \"Agent ID to filter memories (optional, defaults to configured agent)\"\n }\n },\n \"required\": [\"query\"]\n }),\n output_schema: Some(json!({\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"count\": {\"type\": \"number\"},\n \"memories\": {\"type\": \"array\", \"items\": {\"type\": \"object\"}}\n },\n \"required\": [\"success\", \"count\", \"memories\"]\n })),\n },\n McpToolDefinition {\n name: \"list_memories\".into(),\n title: Some(\"List Memories\".into()),\n description: Some(\"Retrieve memories with optional filtering. Adjust the limit parameter to control the number of results returned (default: 100, max: 1000).\".into()),\n input_schema: json!({\n \"type\": \"object\",\n \"properties\": {\n \"limit\": {\n \"type\": \"integer\",\n \"description\": \"Maximum number of memories to return (default: 100, max: 1000)\",\n \"default\": 100,\n \"maximum\": 1000\n },\n \"memory_type\": {\n \"type\": \"string\",\n \"enum\": [\"conversational\", \"procedural\", \"factual\", \"semantic\", \"episodic\", \"personal\"],\n \"description\": \"Type of memory to filter by\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"User ID to filter memories (optional, defaults to configured agent's user)\"\n },\n \"agent_id\": {\n \"type\": \"string\",\n \"description\": \"Agent ID to filter memories (optional, defaults to configured agent)\"\n }\n }\n }),\n output_schema: Some(json!({\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"count\": {\"type\": \"number\"},\n \"memories\": {\"type\": \"array\", \"items\": {\"type\": \"object\"}}\n },\n \"required\": [\"success\", \"count\", \"memories\"]\n })),\n },\n McpToolDefinition {\n name: \"get_memory\".into(),\n title: Some(\"Get Memory by ID\".into()),\n description: Some(\"Retrieve a specific memory by its exact ID.\".into()),\n input_schema: json!({\n \"type\": \"object\",\n \"properties\": {\n \"memory_id\": {\n \"type\": \"string\",\n \"description\": \"Exact ID of the memory to retrieve (required)\"\n }\n },\n \"required\": [\"memory_id\"]\n }),\n output_schema: Some(json!({\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"memory\": {\"type\": \"object\"}\n },\n \"required\": [\"success\", \"memory\"]\n })),\n },\n ]\n}\n\n/// 将MCP参数映射到MemoryOperationPayload\npub fn map_mcp_arguments_to_payload(\n arguments: &Map,\n default_agent_id: &Option,\n) -> MemoryOperationPayload {\n let mut payload = MemoryOperationPayload::default();\n\n // 提取公共字段\n if let Some(content) = arguments.get(\"content\").and_then(|v| v.as_str()) {\n payload.content = Some(content.to_string());\n }\n\n if let Some(query) = arguments.get(\"query\").and_then(|v| v.as_str()) {\n payload.query = Some(query.to_string());\n }\n\n if let Some(memory_id) = arguments.get(\"memory_id\").and_then(|v| v.as_str()) {\n payload.memory_id = Some(memory_id.to_string());\n }\n\n // User ID可以从参数提供,或从agent ID派生\n if let Some(user_id) = arguments.get(\"user_id\").and_then(|v| v.as_str()) {\n payload.user_id = Some(user_id.to_string());\n } else if let Some(agent_id) = default_agent_id {\n // 如果设置了agent_id,从中派生user_id\n payload.user_id = Some(format!(\"user_of_{}\", agent_id));\n }\n\n // Agent ID可以从参数提供,或使用默认值\n if let Some(agent_id) = arguments.get(\"agent_id\").and_then(|v| v.as_str()) {\n payload.agent_id = Some(agent_id.to_string());\n } else {\n payload.agent_id = default_agent_id.clone();\n }\n\n if let Some(memory_type) = arguments.get(\"memory_type\").and_then(|v| v.as_str()) {\n payload.memory_type = Some(memory_type.to_string());\n }\n\n if let Some(topics) = arguments.get(\"topics\").and_then(|v| v.as_array()) {\n payload.topics = Some(\n topics\n .iter()\n .filter_map(|v| v.as_str())\n .map(String::from)\n .collect(),\n );\n }\n\n if let Some(keywords) = arguments.get(\"keywords\").and_then(|v| v.as_array()) {\n payload.keywords = Some(\n keywords\n .iter()\n .filter_map(|v| v.as_str())\n .map(String::from)\n .collect(),\n );\n }\n\n if let Some(limit) = arguments.get(\"limit\").and_then(|v| v.as_u64()) {\n payload.limit = Some(limit as usize);\n }\n\n if let Some(k) = arguments.get(\"k\").and_then(|v| v.as_u64()) {\n payload.k = Some(k as usize);\n }\n\n if let Some(min_salience) = arguments.get(\"min_salience\").and_then(|v| v.as_f64()) {\n payload.min_salience = Some(min_salience);\n }\n\n payload\n}\n\n/// 将MemoryToolsError转换为MCP错误代码\npub fn tools_error_to_mcp_error_code(error: &MemoryToolsError) -> i32 {\n use MemoryToolsError::*;\n \n match error {\n InvalidInput(_) => -32602, // Invalid params\n Runtime(_) => -32603, // Internal error\n MemoryNotFound(_) => -32601, // Method not found (for memory not found)\n Serialization(_) => -32603, // Internal error\n Core(_) => -32603, // Internal error\n }\n}\n\n/// 获取工具的错误消息\npub fn get_tool_error_message(error: &MemoryToolsError) -> String {\n use MemoryToolsError::*;\n \n match error {\n InvalidInput(msg) => msg.clone(),\n Runtime(msg) => msg.clone(),\n MemoryNotFound(msg) => msg.clone(),\n Serialization(e) => format!(\"Serialization error: {}\", e),\n Core(e) => format!(\"Core error: {}\", e),\n }\n}" - }, - "complexity_metrics": { - "cyclomatic_complexity": 17.0, - "lines_of_code": 274, - "number_of_classes": 1, - "number_of_functions": 5 - }, - "dependencies": [ - { - "dependency_type": "import", - "is_external": true, - "line_number": 1, - "name": "serde_json", - "path": "serde_json::{Map, Value, json}", - "version": null - }, - { - "dependency_type": "import", - "is_external": false, - "line_number": 2, - "name": "MemoryOperationPayload", - "path": "crate::{MemoryOperationPayload, MemoryToolsError}", - "version": null - }, - { - "dependency_type": "import", - "is_external": false, - "line_number": 2, - "name": "MemoryToolsError", - "path": "crate::{MemoryOperationPayload, MemoryToolsError}", - "version": null - } - ], - "detailed_description": "This component defines the interface contracts for memory operations exposed via MCP (Memory Control Protocol). It provides standardized tool definitions for storing, querying, listing, and retrieving memories with rich metadata filtering. The `McpToolDefinition` struct describes each tool's name, description, input/output schema following JSON Schema规范. The `get_mcp_tool_definitions` function returns configurations for four key operations: 'store_memory', 'query_memory', 'list_memories', and 'get_memory'. Each operation supports various parameters like user_id, agent_id, memory_type, topics, and salience thresholds. The `map_mcp_arguments_to_payload` function transforms MCP input arguments into an internal `MemoryOperationPayload` structure used by the core system. Error handling utilities convert internal `MemoryToolsError` types to standardized MCP error codes (-32600 series) and generate appropriate error messages, ensuring consistent API responses.", - "interfaces": [ + "return_type": null, + "visibility": "public" + }, { - "description": "Represents a tool definition for MCP protocol with name, title, description, and JSON schemas for input/output validation", + "description": "Configuration parameters for the memory tools.", "interface_type": "struct", - "name": "McpToolDefinition", + "name": "MemoryToolConfig", "parameters": [ { - "description": null, - "is_optional": false, - "name": "name", - "param_type": "String" + "description": "Default user ID to use if not provided in a call.", + "is_optional": true, + "name": "default_user_id", + "param_type": "Option" }, { - "description": null, + "description": "Default agent ID to use if not provided in a call.", "is_optional": true, - "name": "title", + "name": "default_agent_id", "param_type": "Option" }, { - "description": null, + "description": "Maximum number of results for search queries.", "is_optional": true, - "name": "description", - "param_type": "Option" + "name": "max_search_results", + "param_type": "Option" }, { - "description": null, - "is_optional": false, - "name": "input_schema", - "param_type": "Value" + "description": "Whether to automatically enhance stored content.", + "is_optional": true, + "name": "auto_enhance", + "param_type": "Option" }, { - "description": null, + "description": "Minimum similarity score for search results.", "is_optional": true, - "name": "output_schema", - "param_type": "Option" + "name": "search_similarity_threshold", + "param_type": "Option" } ], "return_type": null, "visibility": "public" }, { - "description": "Returns a vector of McpToolDefinition instances for all supported memory operations", - "interface_type": "function", - "name": "get_mcp_tool_definitions", + "description": "Base struct holding shared state and configuration for all memory tools.", + "interface_type": "struct", + "name": "MemoryToolsBase", "parameters": [], - "return_type": "Vec", + "return_type": null, "visibility": "public" }, { - "description": "Converts MCP arguments map and default agent ID into a MemoryOperationPayload structure", - "interface_type": "function", - "name": "map_mcp_arguments_to_payload", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "arguments", - "param_type": "&Map" - }, - { - "description": null, - "is_optional": false, - "name": "default_agent_id", - "param_type": "&Option" - } - ], - "return_type": "MemoryOperationPayload", + "description": "Tool for storing new memories. Implements the RIG Tool trait.", + "interface_type": "struct", + "name": "StoreMemoryTool", + "parameters": [], + "return_type": null, "visibility": "public" }, { - "description": "Converts MemoryToolsError to standardized JSON-RPC error codes used in MCP protocol", - "interface_type": "function", - "name": "tools_error_to_mcp_error_code", - "parameters": [ - { - "description": null, - "is_optional": false, - "name": "error", - "param_type": "&MemoryToolsError" - } - ], - "return_type": "i32", + "description": "Tool for querying memories by text. Implements the RIG Tool trait.", + "interface_type": "struct", + "name": "QueryMemoryTool", + "parameters": [], + "return_type": null, "visibility": "public" }, { - "description": "Generates user-friendly error message from MemoryToolsError instance", + "description": "Tool for listing memories. Implements the RIG Tool trait.", + "interface_type": "struct", + "name": "ListMemoriesTool", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Tool for retrieving a single memory by ID. Implements the RIG Tool trait.", + "interface_type": "struct", + "name": "GetMemoryTool", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Main factory struct for creating and accessing all memory tools.", + "interface_type": "struct", + "name": "MemoryTools", + "parameters": [], + "return_type": null, + "visibility": "public" + }, + { + "description": "Convenience function to create a new MemoryTools instance.", "interface_type": "function", - "name": "get_tool_error_message", + "name": "create_memory_tools", "parameters": [ { - "description": null, + "description": "The memory manager instance to use.", "is_optional": false, - "name": "error", - "param_type": "&MemoryToolsError" + "name": "memory_manager", + "param_type": "Arc" + }, + { + "description": "The global application configuration.", + "is_optional": false, + "name": "global_config", + "param_type": "&Config" + }, + { + "description": "Optional custom configuration to override defaults.", + "is_optional": true, + "name": "custom_config", + "param_type": "Option" } ], - "return_type": "String", + "return_type": "MemoryTools", "visibility": "public" } ], "responsibilities": [ - "Define standardized tool interfaces for memory operations following MCP protocol", - "Map external MCP arguments to internal memory operation payload structure", - "Translate internal error types to standardized MCP-compliant error codes", - "Provide comprehensive error messaging for client-facing responses", - "Support flexible memory querying with semantic search, filtering, and pagination" + "Provide a standardized, tool-oriented interface (Store, Query, List, Get) for external systems to interact with the memory subsystem.", + "Manage tool lifecycle and configuration by encapsulating shared state (MemoryOperations, config) in MemoryToolsBase and providing factory methods via MemoryTools.", + "Translate between external tool call arguments (JSON) and internal service payloads using the map_mcp_arguments_to_payload utility, ensuring protocol compatibility.", + "Integrate with the MCP specification by dynamically retrieving tool definitions (name, description, schema) to ensure consistency with external tool registries.", + "Handle errors consistently across all tools by logging failures with tracing and propagating domain-specific MemoryToolError." ] }, { "code_dossier": { - "code_purpose": "specificfeature", - "description": "Core operations handler for memory tools including storing, querying, listing, and retrieving memories with filtering and metadata management.", - "file_path": "cortex-mem-tools/src/operations.rs", + "code_purpose": "controller", + "description": "Handles HTTP requests for memory operations including creation, retrieval, update, deletion, search, and batch operations. Also provides health checks for the memory system and LLM services.", + "file_path": "cortex-mem-service/src/handlers.rs", "functions": [ - "new", - "store_memory", - "query_memory", + "health_check", + "create_memory", + "get_memory", + "update_memory", + "delete_memory", + "search_memories", "list_memories", - "get_memory" + "batch_delete_memories", + "batch_update_memories", + "get_llm_status", + "llm_health_check", + "parse_conversation_content" ], "importance_score": 0.8, "interfaces": [ - "MemoryOperations", - "store_memory", - "query_memory", - "list_memories", + "health_check", + "create_memory", "get_memory", - "memory_to_json" + "update_memory", + "delete_memory", + "search_memories", + "list_memories", + "batch_delete_memories", + "batch_update_memories", + "get_llm_status", + "llm_health_check" ], - "name": "operations.rs", - "source_summary": "use crate::errors::{MemoryToolsError, MemoryToolsResult};\nuse crate::types::{\n MemoryOperationPayload, MemoryOperationResponse, QueryParams,\n StoreParams, FilterParams\n};\nuse cortex_mem_core::{\n memory::MemoryManager, Memory, MemoryType, MemoryMetadata\n};\nuse serde_json::{json, Value};\nuse tracing::{error, info};\n\n/// Core operations handler for memory tools\npub struct MemoryOperations {\n memory_manager: std::sync::Arc,\n default_user_id: Option,\n default_agent_id: Option,\n default_limit: usize,\n}\n\nimpl MemoryOperations {\n /// Create a new MemoryOperations instance\n pub fn new(\n memory_manager: std::sync::Arc,\n default_user_id: Option,\n default_agent_id: Option,\n default_limit: usize,\n ) -> Self {\n Self {\n memory_manager,\n default_user_id,\n default_agent_id,\n default_limit,\n }\n }\n\n /// Store a new memory\n pub async fn store_memory(&self, payload: MemoryOperationPayload) -> MemoryToolsResult {\n let params = StoreParams::from_payload(\n &payload,\n self.default_user_id.clone(),\n self.default_agent_id.clone(),\n )?;\n\n info!(\"Storing memory for user: {}\", params.user_id);\n\n let memory_type = MemoryType::parse_with_result(¶ms.memory_type)\n .map_err(|e| MemoryToolsError::InvalidInput(format!(\"Invalid memory_type: {}\", e)))?;\n\n let mut metadata = MemoryMetadata::new(memory_type);\n metadata.user_id = Some(params.user_id.clone());\n metadata.agent_id = params.agent_id.clone();\n\n if let Some(topics) = params.topics {\n metadata.topics = topics;\n }\n\n match self.memory_manager.store(params.content, metadata).await {\n Ok(memory_id) => {\n info!(\"Memory stored successfully with ID: {}\", memory_id);\n let data = json!({\n \"memory_id\": memory_id,\n \"user_id\": params.user_id,\n \"agent_id\": params.agent_id\n });\n\n Ok(MemoryOperationResponse::success_with_data(\n \"Memory stored successfully\",\n data,\n ))\n }\n Err(e) => {\n error!(\"Failed to store memory: {}\", e);\n Err(MemoryToolsError::Runtime(format!(\"Failed to store memory: {}\", e)))\n }\n }\n }\n\n /// Query memories based on semantic similarity\n pub async fn query_memory(&self, payload: MemoryOperationPayload) -> MemoryToolsResult {\n let params = QueryParams::from_payload(&payload, self.default_limit)?;\n\n info!(\"Querying memories with query: {}\", params.query);\n\n let memory_type = params.memory_type\n .map(|t| MemoryType::parse_with_result(&t))\n .transpose()\n .map_err(|e| MemoryToolsError::InvalidInput(format!(\"Invalid memory_type: {}\", e)))?;\n\n // Convert parameters to Filters\n let mut filters = cortex_mem_core::types::Filters::default();\n\n if let Some(user_id) = params.user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = params.agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(memory_type) = memory_type {\n filters.memory_type = Some(memory_type);\n }\n\n if let Some(topics) = params.topics {\n filters.topics = Some(topics);\n }\n\n match self.memory_manager.search(\n ¶ms.query,\n &filters,\n params.limit,\n ).await {\n Ok(memories) => {\n let count = memories.len();\n info!(\"Found {} memories\", count);\n\n let memories_json: Vec = memories\n .into_iter()\n .map(|scored_memory| memory_to_json(&scored_memory.memory))\n .collect();\n\n let data = json!({\n \"count\": count,\n \"memories\": memories_json\n });\n\n Ok(MemoryOperationResponse::success_with_data(\n \"Query completed successfully\",\n data,\n ))\n }\n Err(e) => {\n error!(\"Failed to query memories: {}\", e);\n Err(MemoryToolsError::Runtime(format!(\"Failed to query memories: {}\", e)))\n }\n }\n }\n\n /// List memories with filtering\n pub async fn list_memories(&self, payload: MemoryOperationPayload) -> MemoryToolsResult {\n let params = FilterParams::from_payload(&payload, self.default_limit)?;\n\n info!(\"Listing memories with filters\");\n\n // Convert parameters to Filters\n let mut filters = cortex_mem_core::types::Filters::default();\n\n if let Some(user_id) = params.user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = params.agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(memory_type) = params.memory_type {\n if let Ok(mt) = cortex_mem_core::types::MemoryType::parse_with_result(&memory_type) {\n filters.memory_type = Some(mt);\n }\n }\n\n match self.memory_manager.list(&filters, Some(params.limit)).await {\n Ok(memories) => {\n let count = memories.len();\n info!(\"Listed {} memories\", count);\n\n let memories_json: Vec = memories\n .into_iter()\n .map(|memory| memory_to_json(&memory))\n .collect();\n\n let data = json!({\n \"count\": count,\n \"memories\": memories_json\n });\n\n Ok(MemoryOperationResponse::success_with_data(\n \"List completed successfully\",\n data,\n ))\n }\n Err(e) => {\n error!(\"Failed to list memories: {}\", e);\n Err(MemoryToolsError::Runtime(format!(\"Failed to list memories: {}\", e)))\n }\n }\n }\n\n /// Get a specific memory by ID\n pub async fn get_memory(&self, payload: MemoryOperationPayload) -> MemoryToolsResult {\n let memory_id = payload.memory_id\n .ok_or_else(|| MemoryToolsError::InvalidInput(\"Memory ID is required\".to_string()))?;\n\n info!(\"Getting memory with ID: {}\", memory_id);\n\n match self.memory_manager.get(&memory_id).await {\n Ok(Some(memory)) => {\n let memory_json = memory_to_json(&memory);\n let data = json!({\n \"memory\": memory_json\n });\n\n Ok(MemoryOperationResponse::success_with_data(\n \"Memory retrieved successfully\",\n data,\n ))\n }\n Ok(None) => {\n error!(\"Memory not found: {}\", memory_id);\n Err(MemoryToolsError::MemoryNotFound(memory_id))\n }\n Err(e) => {\n error!(\"Failed to get memory: {}\", e);\n Err(MemoryToolsError::Runtime(format!(\"Failed to get memory: {}\", e)))\n }\n }\n }\n}\n\n/// Convert a Memory object to JSON\nfn memory_to_json(memory: &Memory) -> Value {\n let mut metadata_obj = json!({});\n\n if let Some(user_id) = &memory.metadata.user_id {\n metadata_obj[\"user_id\"] = Value::String(user_id.clone());\n }\n\n if let Some(agent_id) = &memory.metadata.agent_id {\n metadata_obj[\"agent_id\"] = Value::String(agent_id.clone());\n }\n\n if let Some(run_id) = &memory.metadata.run_id {\n metadata_obj[\"run_id\"] = Value::String(run_id.clone());\n }\n\n if let Some(actor_id) = &memory.metadata.actor_id {\n metadata_obj[\"actor_id\"] = Value::String(actor_id.clone());\n }\n\n if let Some(role) = &memory.metadata.role {\n metadata_obj[\"role\"] = Value::String(role.clone());\n }\n\n metadata_obj[\"memory_type\"] = Value::String(format!(\"{:?}\", memory.metadata.memory_type));\n\n metadata_obj[\"hash\"] = Value::String(memory.metadata.hash.clone());\n\n metadata_obj[\"importance_score\"] = Value::Number(serde_json::Number::from_f64(memory.metadata.importance_score as f64).unwrap());\n\n if !memory.metadata.entities.is_empty() {\n metadata_obj[\"entities\"] = Value::Array(\n memory.metadata.entities.iter()\n .map(|e| Value::String(e.clone()))\n .collect()\n );\n }\n\n if !memory.metadata.topics.is_empty() {\n metadata_obj[\"topics\"] = Value::Array(\n memory.metadata.topics.iter()\n .map(|t| Value::String(t.clone()))\n .collect()\n );\n }\n\n if !memory.metadata.custom.is_empty() {\n metadata_obj[\"custom\"] = Value::Object(\n memory.metadata.custom.iter()\n .map(|(k, v)| (k.clone(), v.clone()))\n .collect()\n );\n }\n\n json!({\n \"id\": memory.id,\n \"content\": memory.content,\n \"created_at\": memory.created_at.to_rfc3339(),\n \"updated_at\": memory.updated_at.to_rfc3339(),\n \"metadata\": metadata_obj\n })\n}\n" + "name": "handlers.rs", + "source_summary": "use axum::{\n extract::{Path, Query, State},\n http::StatusCode,\n response::Json,\n};\nuse chrono::Utc;\nuse cortex_mem_core::types::{Filters, MemoryMetadata, MemoryType, Message};\nuse std::time::Instant;\n\nuse tracing::{error, info};\n\nuse crate::{\n AppState,\n models::{\n BatchDeleteRequest, BatchOperationResponse, BatchUpdateRequest, CreateMemoryRequest,\n ErrorResponse, HealthResponse, LLMHealthResponse, LLMStatusResponse, ListMemoryQuery,\n ListResponse, MemoryMetadataResponse, MemoryResponse, ModelStatus, ScoredMemoryResponse,\n SearchMemoryRequest, SearchResponse, SuccessResponse, UpdateMemoryRequest,\n },\n};\n\n/// Health check endpoint\npub async fn health_check(\n State(state): State,\n) -> Result, (StatusCode, Json)> {\n match state.memory_manager.health_check().await {\n Ok(health_status) => {\n let response = HealthResponse {\n status: if health_status.overall {\n \"healthy\".to_string()\n } else {\n \"unhealthy\".to_string()\n },\n vector_store: health_status.vector_store,\n llm_service: health_status.llm_service,\n timestamp: Utc::now().to_rfc3339(),\n };\n Ok(Json(response))\n }\n Err(e) => {\n error!(\"Health check failed: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: \"Health check failed\".to_string(),\n code: \"HEALTH_CHECK_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Create a new memory with enhanced support for procedural memory and conversations\npub async fn create_memory(\n State(state): State,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n let memory_type = MemoryType::parse(request.memory_type.as_deref().unwrap_or(\"conversational\"));\n\n let mut metadata = MemoryMetadata::new(memory_type.clone());\n\n if let Some(user_id) = &request.user_id {\n metadata = metadata.with_user_id(user_id.clone());\n }\n\n if let Some(agent_id) = &request.agent_id {\n metadata = metadata.with_agent_id(agent_id.clone());\n }\n\n if let Some(run_id) = &request.run_id {\n metadata = metadata.with_run_id(run_id.clone());\n }\n\n if let Some(actor_id) = &request.actor_id {\n metadata = metadata.with_actor_id(actor_id.clone());\n }\n\n if let Some(role) = &request.role {\n metadata = metadata.with_role(role.clone());\n }\n\n if let Some(custom) = &request.custom {\n metadata.custom = custom.clone();\n }\n\n // Check if this should be handled as a conversation (for procedural memory or advanced processing)\n let is_conversation = memory_type == MemoryType::Procedural\n || request.content.contains('\\n')\n || request.content.contains(\"Assistant:\")\n || request.content.contains(\"User:\");\n\n if is_conversation {\n // Handle as conversation for advanced processing\n let messages = if request.content.contains('\\n') {\n // Parse conversation format\n parse_conversation_content(&request.content, &request.user_id, &request.agent_id)\n } else {\n // Single user message\n vec![Message {\n role: \"user\".to_string(),\n content: request.content.clone(),\n name: request.user_id.clone(),\n }]\n };\n\n match state.memory_manager.add_memory(&messages, metadata).await {\n Ok(results) => {\n info!(\"Memory created successfully with {} actions\", results.len());\n\n let ids: Vec = results.iter().map(|r| r.id.clone()).collect();\n let primary_id = ids.first().cloned().unwrap_or_default();\n\n Ok(Json(SuccessResponse {\n message: format!(\"Memory created successfully with {} actions\", results.len()),\n id: Some(primary_id),\n }))\n }\n Err(e) => {\n error!(\"Failed to create memory: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to create memory: {}\", e),\n code: \"MEMORY_CREATION_FAILED\".to_string(),\n }),\n ))\n }\n }\n } else {\n // Handle as simple content storage\n match state.memory_manager.store(request.content, metadata).await {\n Ok(memory_id) => {\n info!(\"Memory created with ID: {}\", memory_id);\n Ok(Json(SuccessResponse {\n message: \"Memory created successfully\".to_string(),\n id: Some(memory_id),\n }))\n }\n Err(e) => {\n error!(\"Failed to create memory: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to create memory: {}\", e),\n code: \"MEMORY_CREATION_FAILED\".to_string(),\n }),\n ))\n }\n }\n }\n}\n\n/// Parse conversation content from HTTP request\nfn parse_conversation_content(\n content: &str,\n user_id: &Option,\n agent_id: &Option,\n) -> Vec {\n let mut messages = Vec::new();\n let lines: Vec<&str> = content.lines().collect();\n\n for line in lines {\n let trimmed = line.trim();\n if trimmed.is_empty() {\n continue;\n }\n\n if trimmed.starts_with(\"User:\") || trimmed.starts_with(\"user:\") {\n let user_content = trimmed[5..].trim();\n messages.push(Message {\n role: \"user\".to_string(),\n content: user_content.to_string(),\n name: user_id.clone(),\n });\n } else if trimmed.starts_with(\"Assistant:\")\n || trimmed.starts_with(\"assistant:\")\n || trimmed.starts_with(\"AI:\")\n {\n let assistant_content = trimmed[10..].trim();\n messages.push(Message {\n role: \"assistant\".to_string(),\n content: assistant_content.to_string(),\n name: agent_id.clone(),\n });\n } else {\n // If no role prefix, treat as user message\n messages.push(Message {\n role: \"user\".to_string(),\n content: trimmed.to_string(),\n name: user_id.clone(),\n });\n }\n }\n\n // If no messages were parsed, treat entire content as user message\n if messages.is_empty() {\n messages.push(Message {\n role: \"user\".to_string(),\n content: content.to_string(),\n name: user_id.clone(),\n });\n }\n\n messages\n}\n\n/// Get a memory by ID\npub async fn get_memory(\n State(state): State,\n Path(id): Path,\n) -> Result, (StatusCode, Json)> {\n match state.memory_manager.get(&id).await {\n Ok(Some(memory)) => {\n let response = MemoryResponse {\n id: memory.id,\n content: memory.content,\n metadata: MemoryMetadataResponse {\n user_id: memory.metadata.user_id,\n agent_id: memory.metadata.agent_id,\n run_id: memory.metadata.run_id,\n actor_id: memory.metadata.actor_id,\n role: memory.metadata.role,\n memory_type: format!(\"{:?}\", memory.metadata.memory_type),\n hash: memory.metadata.hash,\n importance_score: Some(memory.metadata.importance_score),\n custom: memory.metadata.custom,\n },\n created_at: memory.created_at.to_rfc3339(),\n updated_at: memory.updated_at.to_rfc3339(),\n };\n Ok(Json(response))\n }\n Ok(None) => Err((\n StatusCode::NOT_FOUND,\n Json(ErrorResponse {\n error: \"Memory not found\".to_string(),\n code: \"MEMORY_NOT_FOUND\".to_string(),\n }),\n )),\n Err(e) => {\n error!(\"Failed to get memory: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to get memory: {}\", e),\n code: \"MEMORY_RETRIEVAL_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Update a memory\npub async fn update_memory(\n State(state): State,\n Path(id): Path,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n match state.memory_manager.update(&id, request.content).await {\n Ok(()) => {\n info!(\"Memory updated: {}\", id);\n Ok(Json(SuccessResponse {\n message: \"Memory updated successfully\".to_string(),\n id: Some(id),\n }))\n }\n Err(e) => {\n error!(\"Failed to update memory: {}\", e);\n let status_code = if e.to_string().contains(\"not found\") {\n StatusCode::NOT_FOUND\n } else {\n StatusCode::INTERNAL_SERVER_ERROR\n };\n\n Err((\n status_code,\n Json(ErrorResponse {\n error: format!(\"Failed to update memory: {}\", e),\n code: \"MEMORY_UPDATE_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Delete a memory\npub async fn delete_memory(\n State(state): State,\n Path(id): Path,\n) -> Result, (StatusCode, Json)> {\n match state.memory_manager.delete(&id).await {\n Ok(()) => {\n info!(\"Memory deleted: {}\", id);\n Ok(Json(SuccessResponse {\n message: \"Memory deleted successfully\".to_string(),\n id: Some(id),\n }))\n }\n Err(e) => {\n error!(\"Failed to delete memory: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to delete memory: {}\", e),\n code: \"MEMORY_DELETION_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Search memories\npub async fn search_memories(\n State(state): State,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n let mut filters = Filters::new();\n\n if let Some(user_id) = request.user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = request.agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(run_id) = request.run_id {\n filters.run_id = Some(run_id);\n }\n\n if let Some(actor_id) = request.actor_id {\n filters.actor_id = Some(actor_id);\n }\n\n if let Some(memory_type_str) = request.memory_type {\n filters.memory_type = Some(MemoryType::parse(&memory_type_str));\n }\n\n let limit = request.limit.unwrap_or(10);\n\n match state\n .memory_manager\n .search_with_threshold(\n &request.query,\n &filters,\n limit,\n request.similarity_threshold,\n )\n .await\n {\n Ok(results) => {\n let scored_memories: Vec = results\n .into_iter()\n .map(|scored_memory| ScoredMemoryResponse {\n memory: MemoryResponse {\n id: scored_memory.memory.id,\n content: scored_memory.memory.content,\n metadata: MemoryMetadataResponse {\n user_id: scored_memory.memory.metadata.user_id,\n agent_id: scored_memory.memory.metadata.agent_id,\n run_id: scored_memory.memory.metadata.run_id,\n actor_id: scored_memory.memory.metadata.actor_id,\n role: scored_memory.memory.metadata.role,\n memory_type: format!(\"{:?}\", scored_memory.memory.metadata.memory_type),\n hash: scored_memory.memory.metadata.hash,\n importance_score: Some(scored_memory.memory.metadata.importance_score),\n custom: scored_memory.memory.metadata.custom,\n },\n created_at: scored_memory.memory.created_at.to_rfc3339(),\n updated_at: scored_memory.memory.updated_at.to_rfc3339(),\n },\n score: scored_memory.score,\n })\n .collect();\n\n let response = SearchResponse {\n total: scored_memories.len(),\n results: scored_memories,\n };\n\n info!(\"Search completed: {} results found\", response.total);\n Ok(Json(response))\n }\n Err(e) => {\n error!(\"Failed to search memories: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to search memories: {}\", e),\n code: \"MEMORY_SEARCH_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// List memories\npub async fn list_memories(\n State(state): State,\n Query(query): Query,\n) -> Result, (StatusCode, Json)> {\n let mut filters = Filters::new();\n\n if let Some(user_id) = query.user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = query.agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(run_id) = query.run_id {\n filters.run_id = Some(run_id);\n }\n\n if let Some(actor_id) = query.actor_id {\n filters.actor_id = Some(actor_id);\n }\n\n if let Some(memory_type_str) = query.memory_type {\n filters.memory_type = Some(MemoryType::parse(&memory_type_str));\n }\n\n let limit = query.limit;\n\n match state.memory_manager.list(&filters, limit).await {\n Ok(memories) => {\n let memory_responses: Vec = memories\n .into_iter()\n .map(|memory| MemoryResponse {\n id: memory.id,\n content: memory.content,\n metadata: MemoryMetadataResponse {\n user_id: memory.metadata.user_id,\n agent_id: memory.metadata.agent_id,\n run_id: memory.metadata.run_id,\n actor_id: memory.metadata.actor_id,\n role: memory.metadata.role,\n memory_type: format!(\"{:?}\", memory.metadata.memory_type),\n hash: memory.metadata.hash,\n importance_score: Some(memory.metadata.importance_score),\n custom: memory.metadata.custom,\n },\n created_at: memory.created_at.to_rfc3339(),\n updated_at: memory.updated_at.to_rfc3339(),\n })\n .collect();\n\n let response = ListResponse {\n total: memory_responses.len(),\n memories: memory_responses,\n };\n\n info!(\"List completed: {} memories found\", response.total);\n Ok(Json(response))\n }\n Err(e) => {\n error!(\"Failed to list memories: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to list memories: {}\", e),\n code: \"MEMORY_LIST_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Batch delete memories\npub async fn batch_delete_memories(\n State(state): State,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n let mut success_count = 0;\n let mut failure_count = 0;\n let mut errors = Vec::new();\n\n for memory_id in &request.ids {\n match state.memory_manager.delete(memory_id).await {\n Ok(()) => {\n success_count += 1;\n info!(\"Memory deleted in batch: {}\", memory_id);\n }\n Err(e) => {\n failure_count += 1;\n let error_msg = format!(\"Failed to delete memory {}: {}\", memory_id, e);\n error!(\"{}\", error_msg);\n errors.push(error_msg);\n }\n }\n }\n\n let response = BatchOperationResponse {\n success_count,\n failure_count,\n errors,\n message: format!(\n \"Batch delete completed: {} succeeded, {} failed\",\n success_count, failure_count\n ),\n };\n\n if failure_count > 0 {\n Err((\n StatusCode::PARTIAL_CONTENT,\n Json(ErrorResponse {\n error: format!(\"Batch delete partially failed: {} errors\", failure_count),\n code: \"BATCH_DELETE_PARTIAL_FAILURE\".to_string(),\n }),\n ))\n } else {\n Ok(Json(response))\n }\n}\n\n/// Batch update memories\npub async fn batch_update_memories(\n State(state): State,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n let mut success_count = 0;\n let mut failure_count = 0;\n let mut errors = Vec::new();\n\n for update in &request.updates {\n match state\n .memory_manager\n .update(&update.id, update.content.clone())\n .await\n {\n Ok(()) => {\n success_count += 1;\n info!(\"Memory updated in batch: {}\", update.id);\n }\n Err(e) => {\n failure_count += 1;\n let error_msg = format!(\"Failed to update memory {}: {}\", update.id, e);\n error!(\"{}\", error_msg);\n errors.push(error_msg);\n }\n }\n }\n\n let response = BatchOperationResponse {\n success_count,\n failure_count,\n errors,\n message: format!(\n \"Batch update completed: {} succeeded, {} failed\",\n success_count, failure_count\n ),\n };\n\n if failure_count > 0 {\n Err((\n StatusCode::PARTIAL_CONTENT,\n Json(ErrorResponse {\n error: format!(\"Batch update partially failed: {} errors\", failure_count),\n code: \"BATCH_UPDATE_PARTIAL_FAILURE\".to_string(),\n }),\n ))\n } else {\n Ok(Json(response))\n }\n}\n\n/// Get detailed LLM service status including both completion and embedding models\npub async fn get_llm_status(\n State(state): State,\n) -> Result, (StatusCode, Json)> {\n let timestamp = Utc::now().to_rfc3339();\n\n // Check completion model (text generation)\n let completion_start = Instant::now();\n let (completion_available, completion_error) = match state\n .memory_manager\n .llm_client()\n .complete(\"只给我返回“health”单词,不要输出其他内容\")\n .await\n {\n Ok(_) => (true, None),\n Err(e) => {\n error!(\"Completion model health check failed: {}\", e);\n (false, Some(e.to_string()))\n }\n };\n let completion_latency = completion_start.elapsed().as_millis() as u64;\n\n // Check embedding model\n let embedding_start = Instant::now();\n let (embedding_available, embedding_error) = match state\n .memory_manager\n .llm_client()\n .embed(\"health check\")\n .await\n {\n Ok(_) => (true, None),\n Err(e) => {\n error!(\"Embedding model health check failed: {}\", e);\n (false, Some(e.to_string()))\n }\n };\n let embedding_latency = embedding_start.elapsed().as_millis() as u64;\n\n let overall_healthy = completion_available && embedding_available;\n let overall_status = if overall_healthy {\n \"healthy\".to_string()\n } else {\n \"unhealthy\".to_string()\n };\n\n let response = LLMStatusResponse {\n overall_status,\n completion_model: ModelStatus {\n available: completion_available,\n provider: \"openai\".to_string(),\n model_name: \"cortex-memory-llm\".to_string(), // TODO: Get actual model name from config\n latency_ms: if completion_available {\n Some(completion_latency)\n } else {\n None\n },\n error_message: completion_error,\n last_check: timestamp.clone(),\n },\n embedding_model: ModelStatus {\n available: embedding_available,\n provider: \"openai\".to_string(),\n model_name: \"cortex-memory-embed\".to_string(), // TODO: Get actual model name from config\n latency_ms: if embedding_available {\n Some(embedding_latency)\n } else {\n None\n },\n error_message: embedding_error,\n last_check: timestamp.clone(),\n },\n timestamp,\n };\n\n Ok(Json(response))\n}\n\n/// Simple LLM health check endpoint\npub async fn llm_health_check(\n State(state): State,\n) -> Result, (StatusCode, Json)> {\n // Quick health check for both models\n let (completion_available, embedding_available) = tokio::join!(\n async {\n match state\n .memory_manager\n .llm_client()\n .complete(\"只给我返回“health”单词,不要输出其他内容\")\n .await\n {\n Ok(_) => true,\n Err(_) => false,\n }\n },\n async {\n match state.memory_manager.llm_client().embed(\"Hi\").await {\n Ok(_) => true,\n Err(_) => false,\n }\n }\n );\n\n let response = LLMHealthResponse {\n completion_model_available: completion_available,\n embedding_model_available: embedding_available,\n timestamp: Utc::now().to_rfc3339(),\n };\n\n Ok(Json(response))\n}\n" }, "complexity_metrics": { - "cyclomatic_complexity": 24.0, - "lines_of_code": 281, - "number_of_classes": 1, - "number_of_functions": 6 + "cyclomatic_complexity": 52.0, + "lines_of_code": 677, + "number_of_classes": 0, + "number_of_functions": 12 }, "dependencies": [ { - "dependency_type": "error", - "is_external": false, + "dependency_type": "framework", + "is_external": true, "line_number": null, - "name": "MemoryToolsError", - "path": "crate::errors", + "name": "axum", + "path": null, "version": null }, { - "dependency_type": "type", + "dependency_type": "library", + "is_external": true, + "line_number": null, + "name": "chrono", + "path": null, + "version": null + }, + { + "dependency_type": "internal", "is_external": false, "line_number": null, - "name": "MemoryOperationPayload", - "path": "crate::types", + "name": "cortex_mem_core", + "path": null, "version": null }, { - "dependency_type": "core", + "dependency_type": "library", "is_external": true, "line_number": null, - "name": "MemoryManager", - "path": "cortex_mem_core::memory", + "name": "tracing", + "path": null, "version": null } ], - "detailed_description": "This component implements the core business logic for a memory management system, providing operations to store, query, list, and retrieve memory entries. It acts as an intermediary layer between external requests (via MemoryOperationPayload) and the underlying MemoryManager from cortex-mem-core. Each operation validates input, constructs appropriate parameters, interacts with the memory manager, and returns structured responses. The component handles metadata enrichment, logging, error mapping, and JSON serialization for API responses. It supports semantic search through query operations with filters for user_id, agent_id, memory_type, and topics.", + "detailed_description": "This controller component handles all HTTP endpoints for the memory service. It processes CRUD operations for memories, supports both simple content storage and conversation-based procedural memory, and provides search capabilities with filtering and similarity thresholds. The component also implements health check endpoints to monitor system status and LLM service availability. All operations are implemented with proper error handling and logging, returning appropriate HTTP status codes and structured responses.", "interfaces": [ { - "description": "Main struct that encapsulates memory operations with dependency injection of MemoryManager", - "interface_type": "struct", - "name": "MemoryOperations", - "parameters": [], - "return_type": null, - "visibility": "public" + "description": "Health check endpoint that verifies the overall system health including vector store and LLM service", + "interface_type": "function", + "name": "health_check", + "parameters": [ + { + "description": "Application state containing shared resources", + "is_optional": false, + "name": "state", + "param_type": "State" + } + ], + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "description": "Constructor for MemoryOperations with dependency injection", - "interface_type": "method", - "name": "new", + "description": "Creates a new memory with support for both simple content and conversation-based procedural memory", + "interface_type": "function", + "name": "create_memory", + "parameters": [ + { + "description": "Application state containing shared resources", + "is_optional": false, + "name": "state", + "param_type": "State" + }, + { + "description": "Request containing memory content and metadata", + "is_optional": false, + "name": "request", + "param_type": "Json" + } + ], + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" + }, + { + "description": "Retrieves a specific memory by its ID", + "interface_type": "function", + "name": "get_memory", "parameters": [ { - "description": null, + "description": "Application state containing shared resources", "is_optional": false, - "name": "memory_manager", - "param_type": "std::sync::Arc" + "name": "state", + "param_type": "State" }, { - "description": null, - "is_optional": true, - "name": "default_user_id", - "param_type": "Option" + "description": "Memory identifier", + "is_optional": false, + "name": "id", + "param_type": "Path" + } + ], + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" + }, + { + "description": "Updates an existing memory with new content", + "interface_type": "function", + "name": "update_memory", + "parameters": [ + { + "description": "Application state containing shared resources", + "is_optional": false, + "name": "state", + "param_type": "State" }, { - "description": null, - "is_optional": true, - "name": "default_agent_id", - "param_type": "Option" + "description": "Memory identifier", + "is_optional": false, + "name": "id", + "param_type": "Path" }, { - "description": null, + "description": "Request containing updated content", "is_optional": false, - "name": "default_limit", - "param_type": "usize" + "name": "request", + "param_type": "Json" } ], - "return_type": "Self", - "visibility": "public" + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "description": "Stores a new memory entry with metadata", - "interface_type": "method", - "name": "store_memory", + "description": "Deletes a memory by its ID", + "interface_type": "function", + "name": "delete_memory", "parameters": [ { - "description": null, + "description": "Application state containing shared resources", "is_optional": false, - "name": "payload", - "param_type": "MemoryOperationPayload" + "name": "state", + "param_type": "State" + }, + { + "description": "Memory identifier", + "is_optional": false, + "name": "id", + "param_type": "Path" } ], - "return_type": "MemoryToolsResult", - "visibility": "public" + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "description": "Queries memories based on semantic similarity with filters", - "interface_type": "method", - "name": "query_memory", + "description": "Searches memories using similarity matching with optional filters", + "interface_type": "function", + "name": "search_memories", "parameters": [ { - "description": null, + "description": "Application state containing shared resources", "is_optional": false, - "name": "payload", - "param_type": "MemoryOperationPayload" + "name": "state", + "param_type": "State" + }, + { + "description": "Search query and filtering parameters", + "is_optional": false, + "name": "request", + "param_type": "Json" } ], - "return_type": "MemoryToolsResult", - "visibility": "public" + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "description": "Lists memories with filtering options", - "interface_type": "method", + "description": "Lists memories with optional filtering and pagination", + "interface_type": "function", "name": "list_memories", "parameters": [ { - "description": null, + "description": "Application state containing shared resources", "is_optional": false, - "name": "payload", - "param_type": "MemoryOperationPayload" + "name": "state", + "param_type": "State" + }, + { + "description": "Filtering parameters for listing memories", + "is_optional": false, + "name": "query", + "param_type": "Query" } ], - "return_type": "MemoryToolsResult", - "visibility": "public" + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "description": "Retrieves a specific memory by ID", - "interface_type": "method", - "name": "get_memory", + "description": "Performs batch deletion of multiple memories", + "interface_type": "function", + "name": "batch_delete_memories", "parameters": [ { - "description": null, + "description": "Application state containing shared resources", "is_optional": false, - "name": "payload", - "param_type": "MemoryOperationPayload" + "name": "state", + "param_type": "State" + }, + { + "description": "List of memory IDs to delete", + "is_optional": false, + "name": "request", + "param_type": "Json" } ], - "return_type": "MemoryToolsResult", - "visibility": "public" + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "description": "Converts a Memory object to JSON representation", + "description": "Performs batch update of multiple memories", "interface_type": "function", - "name": "memory_to_json", + "name": "batch_update_memories", "parameters": [ { - "description": null, + "description": "Application state containing shared resources", "is_optional": false, - "name": "memory", - "param_type": "&Memory" + "name": "state", + "param_type": "State" + }, + { + "description": "List of memory updates with IDs and new content", + "is_optional": false, + "name": "request", + "param_type": "Json" } ], - "return_type": "Value", - "visibility": "private" - } - ], - "responsibilities": [ - "Handle storage of new memory entries with proper metadata handling", - "Execute semantic queries on memories with filtering capabilities", - "List memories based on various filter criteria and limits", - "Retrieve specific memory entries by ID with proper error handling", - "Convert internal memory structures to JSON representation for API responses" - ] - }, - { - "code_dossier": { - "code_purpose": "specificfeature", - "description": "A processor responsible for passively learning from conversations. This component should be used by the application/framework layer after each conversation turn to automatically update memories in the background.", - "file_path": "cortex-mem-rig/src/processor.rs", - "functions": [ - "new", - "process_turn" - ], - "importance_score": 0.8, - "interfaces": [ - "ConversationProcessor", - "process_turn", - "new" - ], - "name": "processor.rs", - "source_summary": "use std::sync::Arc;\nuse tracing::error;\n\nuse cortex_mem_core::{\n memory::MemoryManager,\n types::{MemoryMetadata, MemoryResult, Message},\n Result,\n};\n\n/// A processor responsible for passively learning from conversations.\n/// This component should be used by the application/framework layer after each\n/// conversation turn to automatically update memories in the background.\npub struct ConversationProcessor {\n memory_manager: Arc,\n}\n\nimpl ConversationProcessor {\n /// Creates a new `ConversationProcessor`.\n ///\n /// # Arguments\n ///\n /// * `memory_manager` - An `Arc` wrapped `MemoryManager` from `cortex-mem-core`.\n pub fn new(memory_manager: Arc) -> Self {\n Self { memory_manager }\n }\n\n /// Processes a conversation turn, allowing the memory system to learn from it.\n ///\n /// This method invokes the core `add_memory` function, which triggers the\n /// \"extract-retrieve-reason-act\" pipeline to intelligently update the knowledge base.\n ///\n /// # Arguments\n ///\n /// * `messages` - A slice of `cortex_mem_core::types::Message` representing the conversation turn.\n /// * `metadata` - Metadata associated with the memory, such as `user_id` or `agent_id`.\n ///\n /// # Returns\n ///\n /// A `Result` containing a `Vec` which details the actions\n /// (`Create`, `Update`, `Delete`, etc.) performed by the memory system.\n pub async fn process_turn(\n &self,\n messages: &[Message],\n metadata: MemoryMetadata,\n ) -> Result> {\n match self.memory_manager.add_memory(messages, metadata).await {\n Ok(results) => Ok(results),\n Err(e) => {\n error!(\"Failed to process conversation turn for memory: {}\", e);\n Err(e)\n }\n }\n }\n}\n" - }, - "complexity_metrics": { - "cyclomatic_complexity": 4.0, - "lines_of_code": 54, - "number_of_classes": 1, - "number_of_functions": 2 - }, - "dependencies": [ - { - "dependency_type": "logging", - "is_external": true, - "line_number": 2, - "name": "tracing", - "path": null, - "version": null + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "dependency_type": "library", - "is_external": false, - "line_number": 4, - "name": "cortex_mem_core", - "path": null, - "version": null - } - ], - "detailed_description": "The ConversationProcessor struct is designed to facilitate passive learning from conversation turns by interfacing with a MemoryManager to update the system's knowledge base. It wraps the core memory management logic and exposes a clean API for processing conversation slices and associated metadata. The primary method, `process_turn`, asynchronously invokes the `add_memory` function on the MemoryManager, which triggers an 'extract-retrieve-reason-act' pipeline to determine necessary memory operations (create, update, delete). Error handling is implemented via structured logging using the `tracing` crate, ensuring failures are logged at the error level while preserving the original error for upstream handling. This component acts as a bridge between the application logic and the core memory engine, abstracting complex memory update workflows into a simple, reusable interface.", - "interfaces": [ - { - "description": "Main processor type that encapsulates memory learning logic", - "interface_type": "struct", - "name": "ConversationProcessor", - "parameters": [], - "return_type": null, - "visibility": "public" + "description": "Gets detailed status of LLM services including completion and embedding models", + "interface_type": "function", + "name": "get_llm_status", + "parameters": [ + { + "description": "Application state containing shared resources", + "is_optional": false, + "name": "state", + "param_type": "State" + } + ], + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "description": "Constructs a new ConversationProcessor with a shared MemoryManager", - "interface_type": "method", - "name": "new", + "description": "Simple health check for LLM services", + "interface_type": "function", + "name": "llm_health_check", "parameters": [ { - "description": "Shared reference to the core memory management system", + "description": "Application state containing shared resources", "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" + "name": "state", + "param_type": "State" } ], - "return_type": "ConversationProcessor", - "visibility": "public" + "return_type": "Result, (StatusCode, Json)>", + "visibility": "pub" }, { - "description": "Processes a conversation turn to update memories using the core pipeline", - "interface_type": "method", - "name": "process_turn", + "description": "Parses conversation content into structured messages with roles", + "interface_type": "function", + "name": "parse_conversation_content", "parameters": [ { - "description": "Slice of messages representing the conversation turn", + "description": "Raw conversation content to parse", "is_optional": false, - "name": "messages", - "param_type": "&[Message]" + "name": "content", + "param_type": "&str" }, { - "description": "Metadata associated with the memory (e.g., user_id, agent_id)", + "description": "Optional user identifier", "is_optional": false, - "name": "metadata", - "param_type": "MemoryMetadata" + "name": "user_id", + "param_type": "&Option" + }, + { + "description": "Optional agent identifier", + "is_optional": false, + "name": "agent_id", + "param_type": "&Option" } ], - "return_type": "Result>", - "visibility": "public" + "return_type": "Vec", + "visibility": "private" } ], "responsibilities": [ - "Orchestrate background memory updates after each conversation turn", - "Interface with MemoryManager to trigger the 'extract-retrieve-reason-act' learning pipeline", - "Handle error logging and propagation during memory processing", - "Provide a thread-safe, reusable component via Arc-wrapped dependencies" + "Handle HTTP requests for creating, retrieving, updating, and deleting individual memories", + "Process batch operations for multiple memory updates and deletions", + "Implement search functionality with filtering and similarity-based retrieval", + "Provide health check endpoints for monitoring system and LLM service status", + "Parse and transform conversation content from HTTP requests into structured message format" ] }, { "code_dossier": { - "code_purpose": "tool", - "description": "Functional tool code for interacting with memory operations via a set of standardized tools (Store, Query, List, Get Memory). Acts as a bridge between external tool systems (like MCP/RIG) and the core memory manager.", - "file_path": "cortex-mem-rig/src/tool.rs", - "functions": [ - "new", - "definition", - "call", - "store_memory", - "query_memory", - "list_memories", - "get_memory", - "create_memory_tools" - ], + "code_purpose": "model", + "description": "Data transfer objects for memory operations, including CRUD, batch processing, search, and health monitoring.", + "file_path": "cortex-mem-service/src/models.rs", + "functions": [], "importance_score": 0.8, "interfaces": [ - "StoreMemoryTool", - "QueryMemoryTool", - "ListMemoriesTool", - "GetMemoryTool", - "MemoryTools", - "MemoryToolsBase" + "CreateMemoryRequest", + "UpdateMemoryRequest", + "BatchDeleteRequest", + "BatchUpdateRequest", + "MemoryUpdate", + "BatchOperationResponse", + "SearchMemoryRequest", + "ListMemoryQuery", + "MemoryResponse", + "MemoryMetadataResponse", + "SearchResponse", + "ScoredMemoryResponse", + "ListResponse", + "SuccessResponse", + "ErrorResponse", + "HealthResponse", + "LLMStatusResponse", + "ModelStatus", + "LLMHealthResponse" ], - "name": "tool.rs", - "source_summary": "use cortex_mem_config::Config;\nuse cortex_mem_core::MemoryManager;\nuse cortex_mem_tools::{MemoryOperations, get_mcp_tool_definitions, map_mcp_arguments_to_payload};\nuse rig::{completion::ToolDefinition, tool::Tool};\nuse serde::{Deserialize, Serialize};\nuse serde_json::{Map, Value, json};\nuse std::sync::Arc;\nuse tracing::{error, info};\n\n// Re-export the error type from cortex-mem-tools for backward compatibility\npub use cortex_mem_tools::MemoryToolsError as MemoryToolError;\n\n/// Memory tool configuration\npub struct MemoryToolConfig {\n pub default_user_id: Option,\n pub default_agent_id: Option,\n pub max_search_results: Option,\n pub auto_enhance: Option,\n pub search_similarity_threshold: Option,\n}\n\n/// Store Memory tool arguments\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct StoreMemoryArgs {\n pub content: String,\n pub user_id: Option,\n pub agent_id: Option,\n pub memory_type: Option,\n pub topics: Option>,\n}\n\n/// Query Memory tool arguments\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct QueryMemoryArgs {\n pub query: String,\n pub k: Option,\n pub memory_type: Option,\n pub min_salience: Option,\n pub topics: Option>,\n pub user_id: Option,\n pub agent_id: Option,\n}\n\n/// List Memories tool arguments\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ListMemoriesArgs {\n pub limit: Option,\n pub memory_type: Option,\n pub user_id: Option,\n pub agent_id: Option,\n}\n\n/// Get Memory tool arguments\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct GetMemoryArgs {\n pub memory_id: String,\n}\n\n/// Common tool output\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct MemoryToolOutput {\n pub success: bool,\n pub message: String,\n pub data: Option,\n}\n\n/// Base struct for memory tools that shares common functionality\npub struct MemoryToolsBase {\n operations: MemoryOperations,\n config: MemoryToolConfig,\n}\n\nimpl MemoryToolsBase {\n /// Create a new memory tools base with the provided memory manager and configuration\n pub fn new(\n memory_manager: Arc,\n global_config: &Config,\n custom_config: Option,\n ) -> Self {\n let mut config = MemoryToolConfig::default();\n\n // Apply custom config overrides if provided\n if let Some(custom) = custom_config {\n config.default_user_id = custom.default_user_id.or(config.default_user_id);\n config.default_agent_id = custom.default_agent_id.or(config.default_agent_id);\n config.max_search_results = custom.max_search_results.or(config.max_search_results);\n config.auto_enhance = custom.auto_enhance.or(config.auto_enhance);\n config.search_similarity_threshold = custom\n .search_similarity_threshold\n .or(config.search_similarity_threshold);\n }\n\n // Fallback to values from global config if not set in custom\n if config.max_search_results.is_none() {\n config.max_search_results = Some(global_config.memory.max_search_results);\n }\n if config.auto_enhance.is_none() {\n config.auto_enhance = Some(global_config.memory.auto_enhance);\n }\n if config.search_similarity_threshold.is_none() {\n config.search_similarity_threshold = global_config.memory.search_similarity_threshold;\n }\n\n // Create operations handler\n let operations = MemoryOperations::new(\n memory_manager.clone(),\n config.default_user_id.clone(),\n config.default_agent_id.clone(),\n config.max_search_results.unwrap_or(10),\n );\n\n Self { operations, config }\n }\n\n /// Convert JSON values to a Map for the map_mcp_arguments_to_payload function\n fn args_to_map(&self, args: &serde_json::Value) -> Map {\n if let Value::Object(map) = args {\n map.clone()\n } else {\n Map::new()\n }\n }\n}\n\n/// Store Memory Tool\npub struct StoreMemoryTool {\n base: Arc,\n}\n\nimpl StoreMemoryTool {\n pub fn new(base: Arc) -> Self {\n Self { base }\n }\n}\n\nimpl Tool for StoreMemoryTool {\n const NAME: &'static str = \"store_memory\";\n\n type Error = MemoryToolError;\n type Args = StoreMemoryArgs;\n type Output = MemoryToolOutput;\n\n fn definition(\n &self,\n _prompt: String,\n ) -> impl std::future::Future + Send + Sync {\n async move {\n // Get tool definition from MCP definitions\n let tool_definitions = get_mcp_tool_definitions();\n let def = tool_definitions\n .iter()\n .find(|d| d.name == \"store_memory\")\n .expect(\" store_memory tool definition should exist\");\n\n ToolDefinition {\n name: Self::NAME.to_string(),\n description: def.description.clone().unwrap_or_default(),\n parameters: def.input_schema.clone(),\n }\n }\n }\n\n fn call(\n &self,\n args: Self::Args,\n ) -> impl std::future::Future> + Send {\n async move {\n // Convert args to JSON Value\n let args_json = json!(args);\n let arguments = self.base.args_to_map(&args_json);\n\n // Map to payload using shared function\n let payload =\n map_mcp_arguments_to_payload(&arguments, &self.base.config.default_agent_id);\n\n match self.base.operations.store_memory(payload).await {\n Ok(response) => {\n info!(\"Memory stored via rig tool\");\n Ok(MemoryToolOutput {\n success: response.success,\n message: response.message,\n data: response.data,\n })\n }\n Err(e) => {\n error!(\"Failed to store memory via rig tool: {}\", e);\n Err(e)\n }\n }\n }\n }\n}\n\n/// Query Memory Tool\npub struct QueryMemoryTool {\n base: Arc,\n}\n\nimpl QueryMemoryTool {\n pub fn new(base: Arc) -> Self {\n Self { base }\n }\n}\n\nimpl Tool for QueryMemoryTool {\n const NAME: &'static str = \"query_memory\";\n\n type Error = MemoryToolError;\n type Args = QueryMemoryArgs;\n type Output = MemoryToolOutput;\n\n fn definition(\n &self,\n _prompt: String,\n ) -> impl std::future::Future + Send + Sync {\n async move {\n // Get tool definition from MCP definitions\n let tool_definitions = get_mcp_tool_definitions();\n let def = tool_definitions\n .iter()\n .find(|d| d.name == \"query_memory\")\n .expect(\"query_memory tool definition should exist\");\n\n ToolDefinition {\n name: Self::NAME.to_string(),\n description: def.description.clone().unwrap_or_default(),\n parameters: def.input_schema.clone(),\n }\n }\n }\n\n fn call(\n &self,\n args: Self::Args,\n ) -> impl std::future::Future> + Send {\n async move {\n // Convert args to JSON Value\n let args_json = json!(args);\n let arguments = self.base.args_to_map(&args_json);\n\n // Map to payload using shared function\n let payload =\n map_mcp_arguments_to_payload(&arguments, &self.base.config.default_agent_id);\n\n match self.base.operations.query_memory(payload).await {\n Ok(response) => Ok(MemoryToolOutput {\n success: response.success,\n message: response.message,\n data: response.data,\n }),\n Err(e) => {\n error!(\"Failed to query memories via rig tool: {}\", e);\n Err(e)\n }\n }\n }\n }\n}\n\n/// List Memories Tool\npub struct ListMemoriesTool {\n base: Arc,\n}\n\nimpl ListMemoriesTool {\n pub fn new(base: Arc) -> Self {\n Self { base }\n }\n}\n\nimpl Tool for ListMemoriesTool {\n const NAME: &'static str = \"list_memories\";\n\n type Error = MemoryToolError;\n type Args = ListMemoriesArgs;\n type Output = MemoryToolOutput;\n\n fn definition(\n &self,\n _prompt: String,\n ) -> impl std::future::Future + Send + Sync {\n async move {\n // Get tool definition from MCP definitions\n let tool_definitions = get_mcp_tool_definitions();\n let def = tool_definitions\n .iter()\n .find(|d| d.name == \"list_memories\")\n .expect(\"list_memories tool definition should exist\");\n\n ToolDefinition {\n name: Self::NAME.to_string(),\n description: def.description.clone().unwrap_or_default(),\n parameters: def.input_schema.clone(),\n }\n }\n }\n\n fn call(\n &self,\n args: Self::Args,\n ) -> impl std::future::Future> + Send {\n async move {\n // Convert args to JSON Value\n let args_json = json!(args);\n let arguments = self.base.args_to_map(&args_json);\n\n // Map to payload using shared function\n let payload =\n map_mcp_arguments_to_payload(&arguments, &self.base.config.default_agent_id);\n\n match self.base.operations.list_memories(payload).await {\n Ok(response) => Ok(MemoryToolOutput {\n success: response.success,\n message: response.message,\n data: response.data,\n }),\n Err(e) => {\n error!(\"Failed to list memories via rig tool: {}\", e);\n Err(e)\n }\n }\n }\n }\n}\n\n/// Get Memory Tool\npub struct GetMemoryTool {\n base: Arc,\n}\n\nimpl GetMemoryTool {\n pub fn new(base: Arc) -> Self {\n Self { base }\n }\n}\n\nimpl Tool for GetMemoryTool {\n const NAME: &'static str = \"get_memory\";\n\n type Error = MemoryToolError;\n type Args = GetMemoryArgs;\n type Output = MemoryToolOutput;\n\n fn definition(\n &self,\n _prompt: String,\n ) -> impl std::future::Future + Send + Sync {\n async move {\n // Get tool definition from MCP definitions\n let tool_definitions = get_mcp_tool_definitions();\n let def = tool_definitions\n .iter()\n .find(|d| d.name == \"get_memory\")\n .expect(\"get_memory tool definition should exist\");\n\n ToolDefinition {\n name: Self::NAME.to_string(),\n description: def.description.clone().unwrap_or_default(),\n parameters: def.input_schema.clone(),\n }\n }\n }\n\n fn call(\n &self,\n args: Self::Args,\n ) -> impl std::future::Future> + Send {\n async move {\n // Convert args to JSON Value\n let args_json = json!(args);\n let arguments = self.base.args_to_map(&args_json);\n\n // Map to payload using shared function\n let payload =\n map_mcp_arguments_to_payload(&arguments, &self.base.config.default_agent_id);\n\n match self.base.operations.get_memory(payload).await {\n Ok(response) => Ok(MemoryToolOutput {\n success: response.success,\n message: response.message,\n data: response.data,\n }),\n Err(e) => {\n error!(\"Failed to get memory via rig tool: {}\", e);\n Err(e)\n }\n }\n }\n }\n}\n\n/// MemoryTools struct that provides all memory tools\npub struct MemoryTools {\n base: Arc,\n}\n\nimpl MemoryTools {\n /// Create new memory tools with the provided memory manager and configuration\n pub fn new(\n memory_manager: Arc,\n global_config: &Config,\n custom_config: Option,\n ) -> Self {\n let base = Arc::new(MemoryToolsBase::new(\n memory_manager,\n global_config,\n custom_config,\n ));\n Self { base }\n }\n\n /// Get the store memory tool\n pub fn store_memory(&self) -> StoreMemoryTool {\n StoreMemoryTool::new(self.base.clone())\n }\n\n /// Get the query memory tool\n pub fn query_memory(&self) -> QueryMemoryTool {\n QueryMemoryTool::new(self.base.clone())\n }\n\n /// Get the list memories tool\n pub fn list_memories(&self) -> ListMemoriesTool {\n ListMemoriesTool::new(self.base.clone())\n }\n\n /// Get the get memory tool\n pub fn get_memory(&self) -> GetMemoryTool {\n GetMemoryTool::new(self.base.clone())\n }\n}\n\nimpl Default for MemoryToolConfig {\n fn default() -> Self {\n Self {\n default_user_id: None,\n default_agent_id: None,\n max_search_results: None, // Will be taken from global config\n auto_enhance: None, // Will be taken from global config\n search_similarity_threshold: None, // Will be taken from global config\n }\n }\n}\n\n/// Create memory tools with default configuration\npub fn create_memory_tools(\n memory_manager: Arc,\n global_config: &Config,\n custom_config: Option,\n) -> MemoryTools {\n MemoryTools::new(memory_manager, global_config, custom_config)\n}" + "name": "models.rs", + "source_summary": "use serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\n\n/// Request to create a new memory\n#[derive(Debug, Deserialize)]\npub struct CreateMemoryRequest {\n pub content: String,\n pub user_id: Option,\n pub agent_id: Option,\n pub run_id: Option,\n pub actor_id: Option,\n pub role: Option,\n pub memory_type: Option,\n pub custom: Option>,\n}\n\n/// Request to update an existing memory\n#[derive(Debug, Deserialize)]\npub struct UpdateMemoryRequest {\n pub content: String,\n}\n\n/// Request to batch delete memories\n#[derive(Debug, Deserialize)]\npub struct BatchDeleteRequest {\n pub ids: Vec,\n}\n\n/// Request to batch update memories\n#[derive(Debug, Deserialize)]\npub struct BatchUpdateRequest {\n pub updates: Vec,\n}\n\n/// Single memory update for batch operation\n#[derive(Debug, Deserialize)]\npub struct MemoryUpdate {\n pub id: String,\n pub content: String,\n}\n\n/// Response for batch operations\n#[derive(Debug, Serialize)]\npub struct BatchOperationResponse {\n pub success_count: usize,\n pub failure_count: usize,\n pub errors: Vec,\n pub message: String,\n}\n\n/// Request to search memories\n#[derive(Debug, Deserialize)]\npub struct SearchMemoryRequest {\n pub query: String,\n pub user_id: Option,\n pub agent_id: Option,\n pub run_id: Option,\n pub actor_id: Option,\n pub memory_type: Option,\n pub limit: Option,\n pub similarity_threshold: Option,\n}\n\n/// Query parameters for listing memories\n#[derive(Debug, Deserialize)]\npub struct ListMemoryQuery {\n pub user_id: Option,\n pub agent_id: Option,\n pub run_id: Option,\n pub actor_id: Option,\n pub memory_type: Option,\n pub limit: Option,\n}\n\n/// Response for memory operations\n#[derive(Debug, Serialize)]\npub struct MemoryResponse {\n pub id: String,\n pub content: String,\n pub metadata: MemoryMetadataResponse,\n pub created_at: String,\n pub updated_at: String,\n}\n\n/// Response for memory metadata\n#[derive(Debug, Serialize)]\npub struct MemoryMetadataResponse {\n pub user_id: Option,\n pub agent_id: Option,\n pub run_id: Option,\n pub actor_id: Option,\n pub role: Option,\n pub memory_type: String,\n pub hash: String,\n pub importance_score: Option,\n pub custom: HashMap,\n}\n\n/// Response for search results\n#[derive(Debug, Serialize)]\npub struct SearchResponse {\n pub results: Vec,\n pub total: usize,\n}\n\n/// Response for scored memory\n#[derive(Debug, Serialize)]\npub struct ScoredMemoryResponse {\n pub memory: MemoryResponse,\n pub score: f32,\n}\n\n/// Response for list results\n#[derive(Debug, Serialize)]\npub struct ListResponse {\n pub memories: Vec,\n pub total: usize,\n}\n\n/// Response for successful operations\n#[derive(Debug, Serialize)]\npub struct SuccessResponse {\n pub message: String,\n pub id: Option,\n}\n\n/// Error response\n#[derive(Debug, Serialize)]\npub struct ErrorResponse {\n pub error: String,\n pub code: String,\n}\n\n/// Health check response\n#[derive(Debug, Serialize)]\npub struct HealthResponse {\n pub status: String,\n pub vector_store: bool,\n pub llm_service: bool,\n pub timestamp: String,\n}\n\n/// LLM service status response\n#[derive(Debug, Serialize)]\npub struct LLMStatusResponse {\n pub overall_status: String,\n pub completion_model: ModelStatus,\n pub embedding_model: ModelStatus,\n pub timestamp: String,\n}\n\n/// Individual model status\n#[derive(Debug, Serialize)]\npub struct ModelStatus {\n pub available: bool,\n pub provider: String,\n pub model_name: String,\n pub latency_ms: Option,\n pub error_message: Option,\n pub last_check: String,\n}\n\n/// Simple health check response for LLM services\n#[derive(Debug, Serialize)]\npub struct LLMHealthResponse {\n pub completion_model_available: bool,\n pub embedding_model_available: bool,\n pub timestamp: String,\n}\n\n\n" }, "complexity_metrics": { - "cyclomatic_complexity": 20.0, - "lines_of_code": 452, - "number_of_classes": 11, - "number_of_functions": 22 + "cyclomatic_complexity": 11.0, + "lines_of_code": 171, + "number_of_classes": 19, + "number_of_functions": 0 }, "dependencies": [ { - "dependency_type": "use", - "is_external": false, - "line_number": null, - "name": "cortex_mem_config::Config", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": false, - "line_number": null, - "name": "cortex_mem_core::MemoryManager", - "path": null, - "version": null - }, - { - "dependency_type": "use", - "is_external": false, - "line_number": null, - "name": "cortex_mem_tools", - "path": "cortex_mem_tools", - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": null, - "name": "rig", - "path": "rig", - "version": null - }, - { - "dependency_type": "use", + "dependency_type": "library", "is_external": true, - "line_number": null, + "line_number": 1, "name": "serde", - "path": "serde", - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": null, - "name": "serde_json", - "path": "serde_json", - "version": null - }, - { - "dependency_type": "use", - "is_external": true, - "line_number": null, - "name": "std::sync::Arc", "path": null, "version": null }, { - "dependency_type": "use", - "is_external": true, - "line_number": null, - "name": "tracing", - "path": "tracing", + "dependency_type": "std", + "is_external": false, + "line_number": 2, + "name": "std::collections::HashMap", + "path": null, "version": null } ], - "detailed_description": "This component implements a suite of memory interaction tools designed for integration with external agent/tool frameworks (e.g., MCP via RIG). It provides four primary tools: StoreMemoryTool, QueryMemoryTool, ListMemoriesTool, and GetMemoryTool. Each tool wraps a corresponding operation from the MemoryOperations service. The core of the implementation is the MemoryToolsBase struct, which holds shared state including a MemoryOperations instance and a configuration object (MemoryToolConfig). This base is shared among all tool instances via Arc, promoting code reuse and consistent configuration. The tools follow a uniform pattern: they retrieve standardized tool definitions (name, description, parameters) from an MCP source and implement the 'call' method to execute the underlying memory operation. Arguments are converted from JSON to a payload format using shared utility functions from cortex_mem_tools. The top-level MemoryTools struct acts as a factory, providing access to all individual tool instances. Error handling is consistent, logging errors via tracing and propagating MemoryToolError. The code is highly structured, leveraging Rust's type system with serde for serialization and async/await for non-blocking operations.", + "detailed_description": "This component defines a comprehensive set of data models used for request and response handling in a memory service system. It supports core operations such as creating, updating, deleting, searching, and listing memory entries, along with batch operations and health monitoring. All types are serializable using Serde, enabling seamless integration with external systems via JSON. The models encapsulate metadata like user_id, agent_id, run_id, and custom fields, supporting flexible and contextual memory storage. Health-related responses indicate this service integrates with LLM (Large Language Model) components and includes observability features. The design follows a clear separation between request and response types, enhancing type safety and API clarity.", "interfaces": [ { - "description": "Arguments for storing a new memory.", + "description": "Request payload for creating a new memory entry", "interface_type": "struct", - "name": "StoreMemoryArgs", + "name": "CreateMemoryRequest", "parameters": [ { - "description": "The content of the memory to store.", + "description": "The actual memory content to be stored", "is_optional": false, "name": "content", "param_type": "String" }, { - "description": "Optional user ID associated with the memory.", + "description": "Optional ID of the user associated with the memory", "is_optional": true, "name": "user_id", "param_type": "Option" }, { - "description": "Optional agent ID associated with the memory.", + "description": "Optional ID of the agent that generated or accessed the memory", "is_optional": true, "name": "agent_id", "param_type": "Option" }, { - "description": "Optional type/category of the memory.", + "description": "Optional ID of the execution run context", "is_optional": true, - "name": "memory_type", + "name": "run_id", "param_type": "Option" }, { - "description": "Optional list of topics associated with the memory.", - "is_optional": true, - "name": "topics", - "param_type": "Option>" - } - ], - "return_type": null, - "visibility": "public" - }, - { - "description": "Arguments for querying memories based on a text query.", - "interface_type": "struct", - "name": "QueryMemoryArgs", - "parameters": [ - { - "description": "The search query string.", - "is_optional": false, - "name": "query", - "param_type": "String" - }, - { - "description": "Optional number of results to return.", - "is_optional": true, - "name": "k", - "param_type": "Option" - }, - { - "description": "Optional filter by memory type.", + "description": "Optional ID of the actor involved", "is_optional": true, - "name": "memory_type", + "name": "actor_id", "param_type": "Option" }, { - "description": "Optional minimum salience score.", - "is_optional": true, - "name": "min_salience", - "param_type": "Option" - }, - { - "description": "Optional list of topics to filter by.", + "description": "Optional role of the memory (e.g., system, assistant)", "is_optional": true, - "name": "topics", - "param_type": "Option>" + "name": "role", + "param_type": "Option" }, { - "description": "Optional filter by user ID.", + "description": "Optional classification of the memory type", "is_optional": true, - "name": "user_id", + "name": "memory_type", "param_type": "Option" }, { - "description": "Optional filter by agent ID.", + "description": "Optional custom metadata fields", "is_optional": true, - "name": "agent_id", - "param_type": "Option" + "name": "custom", + "param_type": "Option>" } ], "return_type": null, - "visibility": "public" + "visibility": "pub" }, { - "description": "Arguments for listing memories.", + "description": "Request payload for updating an existing memory", "interface_type": "struct", - "name": "ListMemoriesArgs", + "name": "UpdateMemoryRequest", "parameters": [ { - "description": "Optional maximum number of memories to return.", - "is_optional": true, - "name": "limit", - "param_type": "Option" - }, - { - "description": "Optional filter by memory type.", - "is_optional": true, - "name": "memory_type", - "param_type": "Option" - }, + "description": "New content to update the memory with", + "is_optional": false, + "name": "content", + "param_type": "String" + } + ], + "return_type": null, + "visibility": "pub" + }, + { + "description": "Request payload for deleting multiple memories by ID", + "interface_type": "struct", + "name": "BatchDeleteRequest", + "parameters": [ { - "description": "Optional filter by user ID.", - "is_optional": true, - "name": "user_id", - "param_type": "Option" - }, + "description": "List of memory IDs to delete", + "is_optional": false, + "name": "ids", + "param_type": "Vec" + } + ], + "return_type": null, + "visibility": "pub" + }, + { + "description": "Request payload for batch updating multiple memories", + "interface_type": "struct", + "name": "BatchUpdateRequest", + "parameters": [ { - "description": "Optional filter by agent ID.", - "is_optional": true, - "name": "agent_id", - "param_type": "Option" + "description": "List of individual memory updates", + "is_optional": false, + "name": "updates", + "param_type": "Vec" } ], "return_type": null, - "visibility": "public" + "visibility": "pub" }, { - "description": "Arguments for retrieving a single memory by ID.", + "description": "Represents a single update in a batch operation", "interface_type": "struct", - "name": "GetMemoryArgs", + "name": "MemoryUpdate", "parameters": [ { - "description": "The unique identifier of the memory to retrieve.", + "description": "ID of the memory to update", "is_optional": false, - "name": "memory_id", + "name": "id", + "param_type": "String" + }, + { + "description": "New content for the memory", + "is_optional": false, + "name": "content", "param_type": "String" } ], "return_type": null, - "visibility": "public" + "visibility": "pub" }, { - "description": "Standardized output structure for all memory tool operations.", + "description": "Response for batch operations indicating success/failure counts", "interface_type": "struct", - "name": "MemoryToolOutput", + "name": "BatchOperationResponse", "parameters": [ { - "description": "Indicates if the operation was successful.", + "description": "Number of successful operations", + "is_optional": false, + "name": "success_count", + "param_type": "usize" + }, + { + "description": "Number of failed operations", + "is_optional": false, + "name": "failure_count", + "param_type": "usize" + }, + { + "description": "List of error messages", "is_optional": false, - "name": "success", - "param_type": "bool" + "name": "errors", + "param_type": "Vec" }, { - "description": "A human-readable message describing the result.", + "description": "Summary message of the operation", "is_optional": false, "name": "message", "param_type": "String" - }, - { - "description": "Optional payload data returned by the operation.", - "is_optional": true, - "name": "data", - "param_type": "Option" } ], "return_type": null, - "visibility": "public" + "visibility": "pub" }, { - "description": "Configuration parameters for the memory tools.", + "description": "Request payload for semantic or keyword-based memory search", "interface_type": "struct", - "name": "MemoryToolConfig", + "name": "SearchMemoryRequest", "parameters": [ { - "description": "Default user ID to use if not provided in a call.", + "description": "Search query string", + "is_optional": false, + "name": "query", + "param_type": "String" + }, + { + "description": "Optional filter by user ID", "is_optional": true, - "name": "default_user_id", + "name": "user_id", "param_type": "Option" }, { - "description": "Default agent ID to use if not provided in a call.", + "description": "Optional filter by agent ID", "is_optional": true, - "name": "default_agent_id", + "name": "agent_id", "param_type": "Option" }, { - "description": "Maximum number of results for search queries.", + "description": "Optional filter by run ID", "is_optional": true, - "name": "max_search_results", - "param_type": "Option" + "name": "run_id", + "param_type": "Option" }, { - "description": "Whether to automatically enhance stored content.", + "description": "Optional filter by actor ID", "is_optional": true, - "name": "auto_enhance", - "param_type": "Option" + "name": "actor_id", + "param_type": "Option" }, { - "description": "Minimum similarity score for search results.", + "description": "Optional filter by memory type", "is_optional": true, - "name": "search_similarity_threshold", + "name": "memory_type", + "param_type": "Option" + }, + { + "description": "Maximum number of results to return", + "is_optional": true, + "name": "limit", + "param_type": "Option" + }, + { + "description": "Minimum similarity score for search results", + "is_optional": true, + "name": "similarity_threshold", "param_type": "Option" } ], "return_type": null, - "visibility": "public" - }, - { - "description": "Base struct holding shared state and configuration for all memory tools.", - "interface_type": "struct", - "name": "MemoryToolsBase", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Tool for storing new memories. Implements the RIG Tool trait.", - "interface_type": "struct", - "name": "StoreMemoryTool", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Tool for querying memories by text. Implements the RIG Tool trait.", - "interface_type": "struct", - "name": "QueryMemoryTool", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Tool for listing memories. Implements the RIG Tool trait.", - "interface_type": "struct", - "name": "ListMemoriesTool", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Tool for retrieving a single memory by ID. Implements the RIG Tool trait.", - "interface_type": "struct", - "name": "GetMemoryTool", - "parameters": [], - "return_type": null, - "visibility": "public" + "visibility": "pub" }, { - "description": "Main factory struct for creating and accessing all memory tools.", + "description": "Query parameters for listing memories with optional filters", "interface_type": "struct", - "name": "MemoryTools", - "parameters": [], - "return_type": null, - "visibility": "public" - }, - { - "description": "Convenience function to create a new MemoryTools instance.", - "interface_type": "function", - "name": "create_memory_tools", + "name": "ListMemoryQuery", "parameters": [ { - "description": "The memory manager instance to use.", - "is_optional": false, - "name": "memory_manager", - "param_type": "Arc" + "description": "Optional filter by user ID", + "is_optional": true, + "name": "user_id", + "param_type": "Option" }, { - "description": "The global application configuration.", - "is_optional": false, - "name": "global_config", - "param_type": "&Config" + "description": "Optional filter by agent ID", + "is_optional": true, + "name": "agent_id", + "param_type": "Option" }, { - "description": "Optional custom configuration to override defaults.", + "description": "Optional filter by run ID", "is_optional": true, - "name": "custom_config", - "param_type": "Option" - } - ], - "return_type": "MemoryTools", - "visibility": "public" - } - ], - "responsibilities": [ - "Provide a standardized, tool-oriented interface (Store, Query, List, Get) for external systems to interact with the memory subsystem.", - "Manage tool lifecycle and configuration by encapsulating shared state (MemoryOperations, config) in MemoryToolsBase and providing factory methods via MemoryTools.", - "Translate between external tool call arguments (JSON) and internal service payloads using the map_mcp_arguments_to_payload utility, ensuring protocol compatibility.", - "Integrate with the MCP specification by dynamically retrieving tool definitions (name, description, schema) to ensure consistency with external tool registries.", - "Handle errors consistently across all tools by logging failures with tracing and propagating domain-specific MemoryToolError." - ] - }, - { - "code_dossier": { - "code_purpose": "controller", - "description": "Controller component handling HTTP requests for memory management operations including CRUD, search, batch operations, and health checks.", - "file_path": "cortex-mem-service/src/handlers.rs", - "functions": [ - "health_check", - "create_memory", - "get_memory", - "update_memory", - "delete_memory", - "search_memories", - "list_memories", - "batch_delete_memories", - "batch_update_memories", - "get_llm_status", - "llm_health_check", - "parse_conversation_content" - ], - "importance_score": 0.8, - "interfaces": [ - "health_check", - "create_memory", - "get_memory", - "update_memory", - "delete_memory", - "search_memories", - "list_memories", - "batch_delete_memories", - "batch_update_memories", - "get_llm_status", - "llm_health_check" - ], - "name": "handlers.rs", - "source_summary": "use axum::{\n extract::{Path, Query, State},\n http::StatusCode,\n response::Json,\n};\nuse chrono::Utc;\nuse cortex_mem_core::types::{Filters, MemoryMetadata, MemoryType, Message};\nuse std::time::Instant;\n\nuse tracing::{error, info};\n\nuse crate::{\n AppState,\n models::{\n BatchDeleteRequest, BatchOperationResponse, BatchUpdateRequest, CreateMemoryRequest,\n ErrorResponse, HealthResponse, LLMHealthResponse, LLMStatusResponse, ListMemoryQuery,\n ListResponse, MemoryMetadataResponse, MemoryResponse, ModelStatus, ScoredMemoryResponse,\n SearchMemoryRequest, SearchResponse, SuccessResponse, UpdateMemoryRequest,\n },\n};\n\n/// Health check endpoint\npub async fn health_check(\n State(state): State,\n) -> Result, (StatusCode, Json)> {\n match state.memory_manager.health_check().await {\n Ok(health_status) => {\n let response = HealthResponse {\n status: if health_status.overall {\n \"healthy\".to_string()\n } else {\n \"unhealthy\".to_string()\n },\n vector_store: health_status.vector_store,\n llm_service: health_status.llm_service,\n timestamp: Utc::now().to_rfc3339(),\n };\n Ok(Json(response))\n }\n Err(e) => {\n error!(\"Health check failed: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: \"Health check failed\".to_string(),\n code: \"HEALTH_CHECK_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Create a new memory with enhanced support for procedural memory and conversations\npub async fn create_memory(\n State(state): State,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n let memory_type = MemoryType::parse(request.memory_type.as_deref().unwrap_or(\"conversational\"));\n\n let mut metadata = MemoryMetadata::new(memory_type.clone());\n\n if let Some(user_id) = &request.user_id {\n metadata = metadata.with_user_id(user_id.clone());\n }\n\n if let Some(agent_id) = &request.agent_id {\n metadata = metadata.with_agent_id(agent_id.clone());\n }\n\n if let Some(run_id) = &request.run_id {\n metadata = metadata.with_run_id(run_id.clone());\n }\n\n if let Some(actor_id) = &request.actor_id {\n metadata = metadata.with_actor_id(actor_id.clone());\n }\n\n if let Some(role) = &request.role {\n metadata = metadata.with_role(role.clone());\n }\n\n if let Some(custom) = &request.custom {\n metadata.custom = custom.clone();\n }\n\n // Check if this should be handled as a conversation (for procedural memory or advanced processing)\n let is_conversation = memory_type == MemoryType::Procedural\n || request.content.contains('\\n')\n || request.content.contains(\"Assistant:\")\n || request.content.contains(\"User:\");\n\n if is_conversation {\n // Handle as conversation for advanced processing\n let messages = if request.content.contains('\\n') {\n // Parse conversation format\n parse_conversation_content(&request.content, &request.user_id, &request.agent_id)\n } else {\n // Single user message\n vec![Message {\n role: \"user\".to_string(),\n content: request.content.clone(),\n name: request.user_id.clone(),\n }]\n };\n\n match state.memory_manager.add_memory(&messages, metadata).await {\n Ok(results) => {\n info!(\"Memory created successfully with {} actions\", results.len());\n\n let ids: Vec = results.iter().map(|r| r.id.clone()).collect();\n let primary_id = ids.first().cloned().unwrap_or_default();\n\n Ok(Json(SuccessResponse {\n message: format!(\"Memory created successfully with {} actions\", results.len()),\n id: Some(primary_id),\n }))\n }\n Err(e) => {\n error!(\"Failed to create memory: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to create memory: {}\", e),\n code: \"MEMORY_CREATION_FAILED\".to_string(),\n }),\n ))\n }\n }\n } else {\n // Handle as simple content storage\n match state.memory_manager.store(request.content, metadata).await {\n Ok(memory_id) => {\n info!(\"Memory created with ID: {}\", memory_id);\n Ok(Json(SuccessResponse {\n message: \"Memory created successfully\".to_string(),\n id: Some(memory_id),\n }))\n }\n Err(e) => {\n error!(\"Failed to create memory: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to create memory: {}\", e),\n code: \"MEMORY_CREATION_FAILED\".to_string(),\n }),\n ))\n }\n }\n }\n}\n\n/// Parse conversation content from HTTP request\nfn parse_conversation_content(\n content: &str,\n user_id: &Option,\n agent_id: &Option,\n) -> Vec {\n let mut messages = Vec::new();\n let lines: Vec<&str> = content.lines().collect();\n\n for line in lines {\n let trimmed = line.trim();\n if trimmed.is_empty() {\n continue;\n }\n\n if trimmed.starts_with(\"User:\") || trimmed.starts_with(\"user:\") {\n let user_content = trimmed[5..].trim();\n messages.push(Message {\n role: \"user\".to_string(),\n content: user_content.to_string(),\n name: user_id.clone(),\n });\n } else if trimmed.starts_with(\"Assistant:\")\n || trimmed.starts_with(\"assistant:\")\n || trimmed.starts_with(\"AI:\")\n {\n let assistant_content = trimmed[10..].trim();\n messages.push(Message {\n role: \"assistant\".to_string(),\n content: assistant_content.to_string(),\n name: agent_id.clone(),\n });\n } else {\n // If no role prefix, treat as user message\n messages.push(Message {\n role: \"user\".to_string(),\n content: trimmed.to_string(),\n name: user_id.clone(),\n });\n }\n }\n\n // If no messages were parsed, treat entire content as user message\n if messages.is_empty() {\n messages.push(Message {\n role: \"user\".to_string(),\n content: content.to_string(),\n name: user_id.clone(),\n });\n }\n\n messages\n}\n\n/// Get a memory by ID\npub async fn get_memory(\n State(state): State,\n Path(id): Path,\n) -> Result, (StatusCode, Json)> {\n match state.memory_manager.get(&id).await {\n Ok(Some(memory)) => {\n let response = MemoryResponse {\n id: memory.id,\n content: memory.content,\n metadata: MemoryMetadataResponse {\n user_id: memory.metadata.user_id,\n agent_id: memory.metadata.agent_id,\n run_id: memory.metadata.run_id,\n actor_id: memory.metadata.actor_id,\n role: memory.metadata.role,\n memory_type: format!(\"{:?}\", memory.metadata.memory_type),\n hash: memory.metadata.hash,\n custom: memory.metadata.custom,\n },\n created_at: memory.created_at.to_rfc3339(),\n updated_at: memory.updated_at.to_rfc3339(),\n };\n Ok(Json(response))\n }\n Ok(None) => Err((\n StatusCode::NOT_FOUND,\n Json(ErrorResponse {\n error: \"Memory not found\".to_string(),\n code: \"MEMORY_NOT_FOUND\".to_string(),\n }),\n )),\n Err(e) => {\n error!(\"Failed to get memory: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to get memory: {}\", e),\n code: \"MEMORY_RETRIEVAL_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Update a memory\npub async fn update_memory(\n State(state): State,\n Path(id): Path,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n match state.memory_manager.update(&id, request.content).await {\n Ok(()) => {\n info!(\"Memory updated: {}\", id);\n Ok(Json(SuccessResponse {\n message: \"Memory updated successfully\".to_string(),\n id: Some(id),\n }))\n }\n Err(e) => {\n error!(\"Failed to update memory: {}\", e);\n let status_code = if e.to_string().contains(\"not found\") {\n StatusCode::NOT_FOUND\n } else {\n StatusCode::INTERNAL_SERVER_ERROR\n };\n\n Err((\n status_code,\n Json(ErrorResponse {\n error: format!(\"Failed to update memory: {}\", e),\n code: \"MEMORY_UPDATE_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Delete a memory\npub async fn delete_memory(\n State(state): State,\n Path(id): Path,\n) -> Result, (StatusCode, Json)> {\n match state.memory_manager.delete(&id).await {\n Ok(()) => {\n info!(\"Memory deleted: {}\", id);\n Ok(Json(SuccessResponse {\n message: \"Memory deleted successfully\".to_string(),\n id: Some(id),\n }))\n }\n Err(e) => {\n error!(\"Failed to delete memory: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to delete memory: {}\", e),\n code: \"MEMORY_DELETION_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Search memories\npub async fn search_memories(\n State(state): State,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n let mut filters = Filters::new();\n\n if let Some(user_id) = request.user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = request.agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(run_id) = request.run_id {\n filters.run_id = Some(run_id);\n }\n\n if let Some(actor_id) = request.actor_id {\n filters.actor_id = Some(actor_id);\n }\n\n if let Some(memory_type_str) = request.memory_type {\n filters.memory_type = Some(MemoryType::parse(&memory_type_str));\n }\n\n let limit = request.limit.unwrap_or(10);\n\n match state\n .memory_manager\n .search_with_threshold(\n &request.query,\n &filters,\n limit,\n request.similarity_threshold,\n )\n .await\n {\n Ok(results) => {\n let scored_memories: Vec = results\n .into_iter()\n .map(|scored_memory| ScoredMemoryResponse {\n memory: MemoryResponse {\n id: scored_memory.memory.id,\n content: scored_memory.memory.content,\n metadata: MemoryMetadataResponse {\n user_id: scored_memory.memory.metadata.user_id,\n agent_id: scored_memory.memory.metadata.agent_id,\n run_id: scored_memory.memory.metadata.run_id,\n actor_id: scored_memory.memory.metadata.actor_id,\n role: scored_memory.memory.metadata.role,\n memory_type: format!(\"{:?}\", scored_memory.memory.metadata.memory_type),\n hash: scored_memory.memory.metadata.hash,\n custom: scored_memory.memory.metadata.custom,\n },\n created_at: scored_memory.memory.created_at.to_rfc3339(),\n updated_at: scored_memory.memory.updated_at.to_rfc3339(),\n },\n score: scored_memory.score,\n })\n .collect();\n\n let response = SearchResponse {\n total: scored_memories.len(),\n results: scored_memories,\n };\n\n info!(\"Search completed: {} results found\", response.total);\n Ok(Json(response))\n }\n Err(e) => {\n error!(\"Failed to search memories: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to search memories: {}\", e),\n code: \"MEMORY_SEARCH_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// List memories\npub async fn list_memories(\n State(state): State,\n Query(query): Query,\n) -> Result, (StatusCode, Json)> {\n let mut filters = Filters::new();\n\n if let Some(user_id) = query.user_id {\n filters.user_id = Some(user_id);\n }\n\n if let Some(agent_id) = query.agent_id {\n filters.agent_id = Some(agent_id);\n }\n\n if let Some(run_id) = query.run_id {\n filters.run_id = Some(run_id);\n }\n\n if let Some(actor_id) = query.actor_id {\n filters.actor_id = Some(actor_id);\n }\n\n if let Some(memory_type_str) = query.memory_type {\n filters.memory_type = Some(MemoryType::parse(&memory_type_str));\n }\n\n let limit = query.limit;\n\n match state.memory_manager.list(&filters, limit).await {\n Ok(memories) => {\n let memory_responses: Vec = memories\n .into_iter()\n .map(|memory| MemoryResponse {\n id: memory.id,\n content: memory.content,\n metadata: MemoryMetadataResponse {\n user_id: memory.metadata.user_id,\n agent_id: memory.metadata.agent_id,\n run_id: memory.metadata.run_id,\n actor_id: memory.metadata.actor_id,\n role: memory.metadata.role,\n memory_type: format!(\"{:?}\", memory.metadata.memory_type),\n hash: memory.metadata.hash,\n custom: memory.metadata.custom,\n },\n created_at: memory.created_at.to_rfc3339(),\n updated_at: memory.updated_at.to_rfc3339(),\n })\n .collect();\n\n let response = ListResponse {\n total: memory_responses.len(),\n memories: memory_responses,\n };\n\n info!(\"List completed: {} memories found\", response.total);\n Ok(Json(response))\n }\n Err(e) => {\n error!(\"Failed to list memories: {}\", e);\n Err((\n StatusCode::INTERNAL_SERVER_ERROR,\n Json(ErrorResponse {\n error: format!(\"Failed to list memories: {}\", e),\n code: \"MEMORY_LIST_FAILED\".to_string(),\n }),\n ))\n }\n }\n}\n\n/// Batch delete memories\npub async fn batch_delete_memories(\n State(state): State,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n let mut success_count = 0;\n let mut failure_count = 0;\n let mut errors = Vec::new();\n\n for memory_id in &request.ids {\n match state.memory_manager.delete(memory_id).await {\n Ok(()) => {\n success_count += 1;\n info!(\"Memory deleted in batch: {}\", memory_id);\n }\n Err(e) => {\n failure_count += 1;\n let error_msg = format!(\"Failed to delete memory {}: {}\", memory_id, e);\n error!(\"{}\", error_msg);\n errors.push(error_msg);\n }\n }\n }\n\n let response = BatchOperationResponse {\n success_count,\n failure_count,\n errors,\n message: format!(\n \"Batch delete completed: {} succeeded, {} failed\",\n success_count, failure_count\n ),\n };\n\n if failure_count > 0 {\n Err((\n StatusCode::PARTIAL_CONTENT,\n Json(ErrorResponse {\n error: format!(\"Batch delete partially failed: {} errors\", failure_count),\n code: \"BATCH_DELETE_PARTIAL_FAILURE\".to_string(),\n }),\n ))\n } else {\n Ok(Json(response))\n }\n}\n\n/// Batch update memories\npub async fn batch_update_memories(\n State(state): State,\n Json(request): Json,\n) -> Result, (StatusCode, Json)> {\n let mut success_count = 0;\n let mut failure_count = 0;\n let mut errors = Vec::new();\n\n for update in &request.updates {\n match state\n .memory_manager\n .update(&update.id, update.content.clone())\n .await\n {\n Ok(()) => {\n success_count += 1;\n info!(\"Memory updated in batch: {}\", update.id);\n }\n Err(e) => {\n failure_count += 1;\n let error_msg = format!(\"Failed to update memory {}: {}\", update.id, e);\n error!(\"{}\", error_msg);\n errors.push(error_msg);\n }\n }\n }\n\n let response = BatchOperationResponse {\n success_count,\n failure_count,\n errors,\n message: format!(\n \"Batch update completed: {} succeeded, {} failed\",\n success_count, failure_count\n ),\n };\n\n if failure_count > 0 {\n Err((\n StatusCode::PARTIAL_CONTENT,\n Json(ErrorResponse {\n error: format!(\"Batch update partially failed: {} errors\", failure_count),\n code: \"BATCH_UPDATE_PARTIAL_FAILURE\".to_string(),\n }),\n ))\n } else {\n Ok(Json(response))\n }\n}\n\n/// Get detailed LLM service status including both completion and embedding models\npub async fn get_llm_status(\n State(state): State,\n) -> Result, (StatusCode, Json)> {\n let timestamp = Utc::now().to_rfc3339();\n\n // Check completion model (text generation)\n let completion_start = Instant::now();\n let (completion_available, completion_error) = match state\n .memory_manager\n .llm_client()\n .complete(\"只给我返回“health”单词,不要输出其他内容\")\n .await\n {\n Ok(_) => (true, None),\n Err(e) => {\n error!(\"Completion model health check failed: {}\", e);\n (false, Some(e.to_string()))\n }\n };\n let completion_latency = completion_start.elapsed().as_millis() as u64;\n\n // Check embedding model\n let embedding_start = Instant::now();\n let (embedding_available, embedding_error) = match state\n .memory_manager\n .llm_client()\n .embed(\"health check\")\n .await\n {\n Ok(_) => (true, None),\n Err(e) => {\n error!(\"Embedding model health check failed: {}\", e);\n (false, Some(e.to_string()))\n }\n };\n let embedding_latency = embedding_start.elapsed().as_millis() as u64;\n\n let overall_healthy = completion_available && embedding_available;\n let overall_status = if overall_healthy {\n \"healthy\".to_string()\n } else {\n \"unhealthy\".to_string()\n };\n\n let response = LLMStatusResponse {\n overall_status,\n completion_model: ModelStatus {\n available: completion_available,\n provider: \"openai\".to_string(),\n model_name: \"cortex-memory-llm\".to_string(), // TODO: Get actual model name from config\n latency_ms: if completion_available {\n Some(completion_latency)\n } else {\n None\n },\n error_message: completion_error,\n last_check: timestamp.clone(),\n },\n embedding_model: ModelStatus {\n available: embedding_available,\n provider: \"openai\".to_string(),\n model_name: \"cortex-memory-embed\".to_string(), // TODO: Get actual model name from config\n latency_ms: if embedding_available {\n Some(embedding_latency)\n } else {\n None\n },\n error_message: embedding_error,\n last_check: timestamp.clone(),\n },\n timestamp,\n };\n\n Ok(Json(response))\n}\n\n/// Simple LLM health check endpoint\npub async fn llm_health_check(\n State(state): State,\n) -> Result, (StatusCode, Json)> {\n // Quick health check for both models\n let (completion_available, embedding_available) = tokio::join!(\n async {\n match state\n .memory_manager\n .llm_client()\n .complete(\"只给我返回“health”单词,不要输出其他内容\")\n .await\n {\n Ok(_) => true,\n Err(_) => false,\n }\n },\n async {\n match state.memory_manager.llm_client().embed(\"Hi\").await {\n Ok(_) => true,\n Err(_) => false,\n }\n }\n );\n\n let response = LLMHealthResponse {\n completion_model_available: completion_available,\n embedding_model_available: embedding_available,\n timestamp: Utc::now().to_rfc3339(),\n };\n\n Ok(Json(response))\n}\n" - }, - "complexity_metrics": { - "cyclomatic_complexity": 52.0, - "lines_of_code": 674, - "number_of_classes": 0, - "number_of_functions": 12 - }, - "dependencies": [ - { - "dependency_type": "framework", - "is_external": true, - "line_number": null, - "name": "axum", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "chrono", - "path": null, - "version": null - }, - { - "dependency_type": "internal", - "is_external": false, - "line_number": null, - "name": "cortex_mem_core", - "path": null, - "version": null - }, - { - "dependency_type": "library", - "is_external": true, - "line_number": null, - "name": "tracing", - "path": null, - "version": null - } - ], - "detailed_description": "This controller component implements RESTful endpoints for a memory management service. It handles all HTTP requests related to memory operations including creation (with special handling for conversational/procedural memory), retrieval, update, deletion, searching with similarity scoring, listing with filters, and batch operations. The component also provides health check endpoints to monitor both the overall system health and LLM service status (completion and embedding models). Each handler follows a consistent pattern of extracting state and parameters, delegating to the memory manager service, and returning appropriate JSON responses or error codes. The create_memory function includes sophisticated logic to detect and parse conversation content format.", - "interfaces": [ - { - "description": "Returns system health status including vector store and LLM service health", - "interface_type": "function", - "name": "health_check", - "parameters": [ - { - "description": "Application state containing memory manager", - "is_optional": false, - "name": "state", - "param_type": "State" - } - ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" - }, - { - "description": "Creates new memory with support for both simple content and conversation formats", - "interface_type": "function", - "name": "create_memory", - "parameters": [ - { - "description": "Application state containing memory manager", - "is_optional": false, - "name": "state", - "param_type": "State" + "name": "run_id", + "param_type": "Option" }, { - "description": "Memory creation request with content and metadata", - "is_optional": false, - "name": "request", - "param_type": "Json" - } - ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" - }, - { - "description": "Retrieves a specific memory by ID", - "interface_type": "function", - "name": "get_memory", - "parameters": [ + "description": "Optional filter by actor ID", + "is_optional": true, + "name": "actor_id", + "param_type": "Option" + }, { - "description": "Application state containing memory manager", - "is_optional": false, - "name": "state", - "param_type": "State" + "description": "Optional filter by memory type", + "is_optional": true, + "name": "memory_type", + "param_type": "Option" }, { - "description": "ID of memory to retrieve", - "is_optional": false, - "name": "id", - "param_type": "Path" + "description": "Maximum number of results to return", + "is_optional": true, + "name": "limit", + "param_type": "Option" } ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" + "return_type": null, + "visibility": "pub" }, { - "description": "Updates the content of an existing memory", - "interface_type": "function", - "name": "update_memory", + "description": "Full response for a single memory entry", + "interface_type": "struct", + "name": "MemoryResponse", "parameters": [ { - "description": "Application state containing memory manager", + "description": "Unique identifier of the memory", "is_optional": false, - "name": "state", - "param_type": "State" + "name": "id", + "param_type": "String" }, { - "description": "ID of memory to update", + "description": "Content of the memory", "is_optional": false, - "name": "id", - "param_type": "Path" + "name": "content", + "param_type": "String" }, { - "description": "Update request with new content", + "description": "Associated metadata", "is_optional": false, - "name": "request", - "param_type": "Json" - } - ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" - }, - { - "description": "Deletes a memory by ID", - "interface_type": "function", - "name": "delete_memory", - "parameters": [ + "name": "metadata", + "param_type": "MemoryMetadataResponse" + }, { - "description": "Application state containing memory manager", + "description": "Creation timestamp in string format", "is_optional": false, - "name": "state", - "param_type": "State" + "name": "created_at", + "param_type": "String" }, { - "description": "ID of memory to delete", + "description": "Last update timestamp in string format", "is_optional": false, - "name": "id", - "param_type": "Path" + "name": "updated_at", + "param_type": "String" } ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" + "return_type": null, + "visibility": "pub" }, { - "description": "Searches memories by semantic similarity with filtering", - "interface_type": "function", - "name": "search_memories", + "description": "Metadata portion of a memory response", + "interface_type": "struct", + "name": "MemoryMetadataResponse", "parameters": [ { - "description": "Application state containing memory manager", - "is_optional": false, - "name": "state", - "param_type": "State" + "description": "Optional user ID", + "is_optional": true, + "name": "user_id", + "param_type": "Option" + }, + { + "description": "Optional agent ID", + "is_optional": true, + "name": "agent_id", + "param_type": "Option" + }, + { + "description": "Optional run ID", + "is_optional": true, + "name": "run_id", + "param_type": "Option" + }, + { + "description": "Optional actor ID", + "is_optional": true, + "name": "actor_id", + "param_type": "Option" }, { - "description": "Search request with query and filters", + "description": "Optional role of the memory", + "is_optional": true, + "name": "role", + "param_type": "Option" + }, + { + "description": "Type classification of the memory", "is_optional": false, - "name": "request", - "param_type": "Json" - } - ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" - }, - { - "description": "Lists memories with optional filtering and pagination", - "interface_type": "function", - "name": "list_memories", - "parameters": [ + "name": "memory_type", + "param_type": "String" + }, { - "description": "Application state containing memory manager", + "description": "Content hash for deduplication or integrity", "is_optional": false, - "name": "state", - "param_type": "State" + "name": "hash", + "param_type": "String" }, { - "description": "Query parameters for listing with filters", + "description": "Optional importance score for prioritization", + "is_optional": true, + "name": "importance_score", + "param_type": "Option" + }, + { + "description": "Custom metadata key-value pairs", "is_optional": false, - "name": "query", - "param_type": "Query" + "name": "custom", + "param_type": "HashMap" } ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" + "return_type": null, + "visibility": "pub" }, { - "description": "Performs batch deletion of multiple memories", - "interface_type": "function", - "name": "batch_delete_memories", + "description": "Response for memory search operations", + "interface_type": "struct", + "name": "SearchResponse", "parameters": [ { - "description": "Application state containing memory manager", + "description": "List of scored search results", "is_optional": false, - "name": "state", - "param_type": "State" + "name": "results", + "param_type": "Vec" }, { - "description": "Batch delete request with list of memory IDs", + "description": "Total number of matching results", "is_optional": false, - "name": "request", - "param_type": "Json" + "name": "total", + "param_type": "usize" } ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" + "return_type": null, + "visibility": "pub" }, { - "description": "Performs batch update of multiple memories", - "interface_type": "function", - "name": "batch_update_memories", + "description": "Search result with an associated score", + "interface_type": "struct", + "name": "ScoredMemoryResponse", "parameters": [ { - "description": "Application state containing memory manager", + "description": "The memory entry", "is_optional": false, - "name": "state", - "param_type": "State" + "name": "memory", + "param_type": "MemoryResponse" }, { - "description": "Batch update request with list of update operations", + "description": "Relevance or similarity score", "is_optional": false, - "name": "request", - "param_type": "Json" + "name": "score", + "param_type": "f32" } ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" + "return_type": null, + "visibility": "pub" }, { - "description": "Gets detailed status of LLM service including both completion and embedding models", - "interface_type": "function", - "name": "get_llm_status", + "description": "Response for listing memories", + "interface_type": "struct", + "name": "ListResponse", "parameters": [ { - "description": "Application state containing memory manager", + "description": "List of memory entries", "is_optional": false, - "name": "state", - "param_type": "State" - } - ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" - }, - { - "description": "Simple health check for LLM service availability", - "interface_type": "function", - "name": "llm_health_check", - "parameters": [ + "name": "memories", + "param_type": "Vec" + }, { - "description": "Application state containing memory manager", + "description": "Total number of memories", "is_optional": false, - "name": "state", - "param_type": "State" + "name": "total", + "param_type": "usize" } ], - "return_type": "Result, (StatusCode, Json)>", - "visibility": "public" + "return_type": null, + "visibility": "pub" }, { - "description": "Parses conversation content into structured messages with roles", - "interface_type": "function", - "name": "parse_conversation_content", + "description": "Generic success response", + "interface_type": "struct", + "name": "SuccessResponse", "parameters": [ { - "description": "Raw conversation content to parse", - "is_optional": false, - "name": "content", - "param_type": "&str" - }, - { - "description": "Optional user ID for message attribution", + "description": "Success message", "is_optional": false, - "name": "user_id", - "param_type": "&Option" + "name": "message", + "param_type": "String" }, { - "description": "Optional agent ID for message attribution", - "is_optional": false, - "name": "agent_id", - "param_type": "&Option" + "description": "Optional ID of the created or affected resource", + "is_optional": true, + "name": "id", + "param_type": "Option" } ], - "return_type": "Vec", - "visibility": "private" - } - ], - "responsibilities": [ - "Handling HTTP requests for memory CRUD operations", - "Implementing search and list functionality with filtering capabilities", - "Managing batch operations for memory deletion and updates", - "Providing health check endpoints for system and LLM service monitoring", - "Parsing and validating request data and converting between internal and external representations" - ] - }, - { - "code_dossier": { - "code_purpose": "model", - "description": "Data transfer objects and response structures for memory service operations including CRUD, batch processing, search, and health monitoring.", - "file_path": "cortex-mem-service/src/models.rs", - "functions": [], - "importance_score": 0.8, - "interfaces": [ - "CreateMemoryRequest", - "UpdateMemoryRequest", - "BatchDeleteRequest", - "BatchUpdateRequest", - "MemoryUpdate", - "BatchOperationResponse", - "SearchMemoryRequest", - "ListMemoryQuery", - "MemoryResponse", - "MemoryMetadataResponse", - "SearchResponse", - "ScoredMemoryResponse", - "ListResponse", - "SuccessResponse", - "ErrorResponse", - "HealthResponse", - "LLMStatusResponse", - "ModelStatus", - "LLMHealthResponse" - ], - "name": "models.rs", - "source_summary": "use serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\n\n/// Request to create a new memory\n#[derive(Debug, Deserialize)]\npub struct CreateMemoryRequest {\n pub content: String,\n pub user_id: Option,\n pub agent_id: Option,\n pub run_id: Option,\n pub actor_id: Option,\n pub role: Option,\n pub memory_type: Option,\n pub custom: Option>,\n}\n\n/// Request to update an existing memory\n#[derive(Debug, Deserialize)]\npub struct UpdateMemoryRequest {\n pub content: String,\n}\n\n/// Request to batch delete memories\n#[derive(Debug, Deserialize)]\npub struct BatchDeleteRequest {\n pub ids: Vec,\n}\n\n/// Request to batch update memories\n#[derive(Debug, Deserialize)]\npub struct BatchUpdateRequest {\n pub updates: Vec,\n}\n\n/// Single memory update for batch operation\n#[derive(Debug, Deserialize)]\npub struct MemoryUpdate {\n pub id: String,\n pub content: String,\n}\n\n/// Response for batch operations\n#[derive(Debug, Serialize)]\npub struct BatchOperationResponse {\n pub success_count: usize,\n pub failure_count: usize,\n pub errors: Vec,\n pub message: String,\n}\n\n/// Request to search memories\n#[derive(Debug, Deserialize)]\npub struct SearchMemoryRequest {\n pub query: String,\n pub user_id: Option,\n pub agent_id: Option,\n pub run_id: Option,\n pub actor_id: Option,\n pub memory_type: Option,\n pub limit: Option,\n pub similarity_threshold: Option,\n}\n\n/// Query parameters for listing memories\n#[derive(Debug, Deserialize)]\npub struct ListMemoryQuery {\n pub user_id: Option,\n pub agent_id: Option,\n pub run_id: Option,\n pub actor_id: Option,\n pub memory_type: Option,\n pub limit: Option,\n}\n\n/// Response for memory operations\n#[derive(Debug, Serialize)]\npub struct MemoryResponse {\n pub id: String,\n pub content: String,\n pub metadata: MemoryMetadataResponse,\n pub created_at: String,\n pub updated_at: String,\n}\n\n/// Response for memory metadata\n#[derive(Debug, Serialize)]\npub struct MemoryMetadataResponse {\n pub user_id: Option,\n pub agent_id: Option,\n pub run_id: Option,\n pub actor_id: Option,\n pub role: Option,\n pub memory_type: String,\n pub hash: String,\n pub custom: HashMap,\n}\n\n/// Response for search results\n#[derive(Debug, Serialize)]\npub struct SearchResponse {\n pub results: Vec,\n pub total: usize,\n}\n\n/// Response for scored memory\n#[derive(Debug, Serialize)]\npub struct ScoredMemoryResponse {\n pub memory: MemoryResponse,\n pub score: f32,\n}\n\n/// Response for list results\n#[derive(Debug, Serialize)]\npub struct ListResponse {\n pub memories: Vec,\n pub total: usize,\n}\n\n/// Response for successful operations\n#[derive(Debug, Serialize)]\npub struct SuccessResponse {\n pub message: String,\n pub id: Option,\n}\n\n/// Error response\n#[derive(Debug, Serialize)]\npub struct ErrorResponse {\n pub error: String,\n pub code: String,\n}\n\n/// Health check response\n#[derive(Debug, Serialize)]\npub struct HealthResponse {\n pub status: String,\n pub vector_store: bool,\n pub llm_service: bool,\n pub timestamp: String,\n}\n\n/// LLM service status response\n#[derive(Debug, Serialize)]\npub struct LLMStatusResponse {\n pub overall_status: String,\n pub completion_model: ModelStatus,\n pub embedding_model: ModelStatus,\n pub timestamp: String,\n}\n\n/// Individual model status\n#[derive(Debug, Serialize)]\npub struct ModelStatus {\n pub available: bool,\n pub provider: String,\n pub model_name: String,\n pub latency_ms: Option,\n pub error_message: Option,\n pub last_check: String,\n}\n\n/// Simple health check response for LLM services\n#[derive(Debug, Serialize)]\npub struct LLMHealthResponse {\n pub completion_model_available: bool,\n pub embedding_model_available: bool,\n pub timestamp: String,\n}\n\n\n" - }, - "complexity_metrics": { - "cyclomatic_complexity": 11.0, - "lines_of_code": 170, - "number_of_classes": 0, - "number_of_functions": 0 - }, - "dependencies": [ - { - "dependency_type": "library", - "is_external": true, - "line_number": 1, - "name": "serde", - "path": null, - "version": null - }, - { - "dependency_type": "standard_library", - "is_external": false, - "line_number": 2, - "name": "std::collections::HashMap", - "path": null, - "version": null - } - ], - "detailed_description": "This component defines all the data models used for request and response payloads in the memory service. It supports core operations including creating, updating, deleting, searching, and listing memory records with rich metadata. The models facilitate interaction between the API layer and external clients, ensuring consistent data structure across the system. Specialized types are provided for batch operations, error handling, and health monitoring, including detailed status reporting for LLM services. All request types use Deserialize for incoming data parsing, while response types use Serialize for output formatting, following REST API best practices.", - "interfaces": [ + "return_type": null, + "visibility": "pub" + }, { - "description": "Request payload for creating a new memory record with content and optional metadata", + "description": "Generic error response structure", "interface_type": "struct", - "name": "CreateMemoryRequest", + "name": "ErrorResponse", "parameters": [ { - "description": "The actual memory content", + "description": "Error message", "is_optional": false, - "name": "content", + "name": "error", "param_type": "String" }, { - "description": "Optional user identifier", - "is_optional": true, - "name": "user_id", - "param_type": "Option" - }, - { - "description": "Optional agent identifier", - "is_optional": true, - "name": "agent_id", - "param_type": "Option" - }, - { - "description": "Optional run/session identifier", - "is_optional": true, - "name": "run_id", - "param_type": "Option" - }, + "description": "Error code", + "is_optional": false, + "name": "code", + "param_type": "String" + } + ], + "return_type": null, + "visibility": "pub" + }, + { + "description": "Comprehensive health check response", + "interface_type": "struct", + "name": "HealthResponse", + "parameters": [ { - "description": "Optional actor identifier", - "is_optional": true, - "name": "actor_id", - "param_type": "Option" + "description": "Overall service status", + "is_optional": false, + "name": "status", + "param_type": "String" }, { - "description": "Optional role information", - "is_optional": true, - "name": "role", - "param_type": "Option" + "description": "Whether vector store is operational", + "is_optional": false, + "name": "vector_store", + "param_type": "bool" }, { - "description": "Optional memory type categorization", - "is_optional": true, - "name": "memory_type", - "param_type": "Option" + "description": "Whether LLM service is available", + "is_optional": false, + "name": "llm_service", + "param_type": "bool" }, { - "description": "Optional custom metadata fields", - "is_optional": true, - "name": "custom", - "param_type": "Option>" + "description": "Timestamp of the check", + "is_optional": false, + "name": "timestamp", + "param_type": "String" } ], "return_type": null, "visibility": "pub" }, { - "description": "Standard response structure for memory operations containing full memory details", + "description": "Detailed status of LLM models", "interface_type": "struct", - "name": "MemoryResponse", + "name": "LLMStatusResponse", "parameters": [ { - "description": "Unique identifier for the memory", - "is_optional": false, - "name": "id", - "param_type": "String" - }, - { - "description": "The memory content", + "description": "Overall status of LLM service", "is_optional": false, - "name": "content", + "name": "overall_status", "param_type": "String" }, { - "description": "Associated metadata for the memory", + "description": "Status of completion model", "is_optional": false, - "name": "metadata", - "param_type": "MemoryMetadataResponse" + "name": "completion_model", + "param_type": "ModelStatus" }, { - "description": "Creation timestamp in string format", + "description": "Status of embedding model", "is_optional": false, - "name": "created_at", - "param_type": "String" + "name": "embedding_model", + "param_type": "ModelStatus" }, { - "description": "Last update timestamp in string format", + "description": "Timestamp of the status", "is_optional": false, - "name": "updated_at", + "name": "timestamp", "param_type": "String" } ], @@ -17209,51 +14683,69 @@ Code analysis results from preprocessing phase, including definitions of functio "visibility": "pub" }, { - "description": "Response structure for memory search operations containing scored results", + "description": "Status information for a single model", "interface_type": "struct", - "name": "SearchResponse", + "name": "ModelStatus", "parameters": [ { - "description": "List of scored memory results", + "description": "Whether the model is available", "is_optional": false, - "name": "results", - "param_type": "Vec" + "name": "available", + "param_type": "bool" }, { - "description": "Total number of results available", + "description": "Model provider name", "is_optional": false, - "name": "total", - "param_type": "usize" + "name": "provider", + "param_type": "String" + }, + { + "description": "Name of the model", + "is_optional": false, + "name": "model_name", + "param_type": "String" + }, + { + "description": "Optional latency in milliseconds", + "is_optional": true, + "name": "latency_ms", + "param_type": "Option" + }, + { + "description": "Optional error message if unavailable", + "is_optional": true, + "name": "error_message", + "param_type": "Option" + }, + { + "description": "Timestamp of last status check", + "is_optional": false, + "name": "last_check", + "param_type": "String" } ], "return_type": null, "visibility": "pub" }, { - "description": "Comprehensive status reporting for LLM services including individual model statuses", + "description": "Simplified health response for LLM services", "interface_type": "struct", - "name": "LLMStatusResponse", + "name": "LLMHealthResponse", "parameters": [ { - "description": "Aggregated status of all LLM services", - "is_optional": false, - "name": "overall_status", - "param_type": "String" - }, - { - "description": "Status of the completion model", + "description": "Whether completion model is available", "is_optional": false, - "name": "completion_model", - "param_type": "ModelStatus" + "name": "completion_model_available", + "param_type": "bool" }, { - "description": "Status of the embedding model", + "description": "Whether embedding model is available", "is_optional": false, - "name": "embedding_model", - "param_type": "ModelStatus" + "name": "embedding_model_available", + "param_type": "bool" }, { - "description": "Timestamp of the status check", + "description": "Timestamp of the health check", "is_optional": false, "name": "timestamp", "param_type": "String" @@ -17264,11 +14756,11 @@ Code analysis results from preprocessing phase, including definitions of functio } ], "responsibilities": [ - "Define structured data schemas for API request and response payloads", - "Support CRUD operations on memory records with appropriate metadata", - "Enable batch operations with comprehensive success/failure tracking", - "Facilitate memory search and filtering with configurable parameters", - "Provide health check and service status monitoring data structures" + "Define data structures for memory creation, update, and deletion requests", + "Provide response models for CRUD and batch operations on memory entries", + "Support search and list operations with filtering and pagination via query models", + "Enable health and status monitoring for the memory and LLM services", + "Facilitate data serialization and deserialization through Serde integration" ] }, { @@ -17621,6 +15113,89 @@ Code analysis results from preprocessing phase, including definitions of functio "Integrate and extend Tailwind CSS framework with project-specific styles" ] }, + { + "code_dossier": { + "code_purpose": "config", + "description": "中文语言包配置文件,包含系统各模块的多语言文本内容", + "file_path": "cortex-mem-insights/src/lib/i18n/locales/zh.json", + "functions": [], + "importance_score": 0.6, + "interfaces": [], + "name": "zh.json", + "source_summary": "{\n\t\"common\": {\n\t\t\"appName\": \"Cortex Memory Insights\",\n\t\t\"loading\": \"加载中...\",\n\t\t\"error\": \"错误\",\n\t\t\"success\": \"成功\",\n\t\t\"save\": \"保存\",\n\t\t\"cancel\": \"取消\",\n\t\t\"delete\": \"删除\",\n\t\t\"edit\": \"编辑\",\n\t\t\"view\": \"查看\",\n\t\t\"search\": \"搜索\",\n\t\t\"filter\": \"筛选\",\n\t\t\"sort\": \"排序\",\n\t\t\"refresh\": \"刷新\",\n\t\t\"back\": \"返回\",\n\t\t\"next\": \"下一步\",\n\t\t\"previous\": \"上一步\",\n\t\t\"confirm\": \"确认\",\n\t\t\"close\": \"关闭\",\n\t\t\"language\": \"语言\",\n\t\t\"language_this\": \"中文\",\n\t\t\"english\": \"英文\",\n\t\t\"chinese\": \"中文\",\n\t\t\"japanese\": \"日文\",\n\t\t\"unknown\": \"未知\",\n\t\t\"unit\": \"条\"\n\t},\n\t\"navigation\": {\n\t\t\"dashboard\": \"仪表板\",\n\t\t\"memories\": \"记忆\",\n\t\t\"analytics\": \"统计分析\",\n\t\t\"monitor\": \"监控\",\n\t\t\"optimization\": \"优化\",\n\t\t\"settings\": \"设置\"\n\t},\n\t\"dashboard\": {\n\t\t\"title\": \"仪表板\",\n\t\t\"welcome\": \"欢迎使用 Cortex Memory 洞察\",\n\t\t\"totalMemories\": \"总记忆数\",\n\t\t\"optimizationCount\": \"优化次数\",\n\t\t\"averageQuality\": \"平均质量\",\n\t\t\"qualityDistribution\": \"质量分布\",\n\t\t\"highMediumLow\": \"高/中/低\",\n\t\t\"systemStatus\": \"系统状态\",\n\t\t\"recentMemories\": \"最近记忆\",\n\t\t\"viewAll\": \"查看全部\",\n\t\t\"noMemories\": \"暂无记忆\",\n\t\t\"detecting\": \"检测中\",\n\t\t\"healthy\": \"健康\",\n\t\t\"unhealthy\": \"不健康\",\n\t\t\"cortexMemService\": \"Cortex Memory 服务\",\n\t\t\"llmService\": \"LLM 服务\",\n\t\t\"vectorStore\": \"向量存储\",\n\t\t\"latency\": \"延迟\",\n\t\t\"version\": \"版本\",\n\t\t\"lastCheck\": \"最后检查\"\n\t},\n\t\"memories\": {\n\t\t\"title\": \"记忆管理\",\n\t\t\"description\": \"浏览、搜索和管理所有记忆记录\",\n\t\t\"searchPlaceholder\": \"搜索记忆内容、ID、用户或Agent...\",\n\t\t\"typeFilter\": \"类型筛选\",\n\t\t\"allTypes\": \"所有类型\",\n\t\t\"conversational\": \"对话\",\n\t\t\"factual\": \"事实\",\n\t\t\"personal\": \"个人\",\n\t\t\"procedural\": \"流程\",\n\t\t\"unknown\": \"未知\",\n\t\t\"sortBy\": \"排序方式\",\n\t\t\"createdAt\": \"创建时间\",\n\t\t\"importance\": \"重要性\",\n\t\t\"ascending\": \"升序\",\n\t\t\"descending\": \"降序\",\n\t\t\"selectAll\": \"全选\",\n\t\t\"batchOperations\": \"批量操作\",\n\t\t\"deleteSelected\": \"删除选中\",\n\t\t\"exportSelected\": \"导出选中\",\n\t\t\"optimizeSelected\": \"优化选中\",\n\t\t\"noMemoriesFound\": \"未找到记忆\",\n\t\t\"loadingMemories\": \"加载记忆中...\",\n\t\t\"memoryDetails\": \"记忆详情\",\n\t\t\"content\": \"内容\",\n\t\t\"type\": \"类型\",\n\t\t\"userId\": \"用户ID\",\n\t\t\"agentId\": \"代理ID\",\n\t\t\"userAgent\": \"用户/Agent\",\n\t\t\"created\": \"创建时间\",\n\t\t\"updated\": \"更新时间\",\n\t\t\"actions\": \"操作\",\n\t\t\"confirmDelete\": \"确认删除\",\n\t\t\"deleteMemoryConfirm\": \"确定要删除此记忆吗?\",\n\t\t\"deleteMemoriesConfirm\": \"确定要删除 {count} 条记忆吗?\",\n\t\t\"memoryDeleted\": \"记忆删除成功\",\n\t\t\"memoriesDeleted\": \"{count} 条记忆删除成功\",\n\t\t\"exportFormat\": \"导出格式\",\n\t\t\"json\": \"JSON\",\n\t\t\"csv\": \"CSV\",\n\t\t\"txt\": \"文本\",\n\t\t\"search\": \"搜索\",\n\t\t\"reset\": \"重置\",\n\t\t\"sort\": \"排序\",\n\t\t\"totalMemories\": \"总记忆数\",\n\t\t\"showing\": \"显示第\",\n\t\t\"to\": \"到\",\n\t\t\"of\": \"条,共\",\n\t\t\"page\": \"页\",\n\t\t\"previousPage\": \"上一页\",\n\t\t\"nextPage\": \"下一页\",\n\t\t\"goToFirstPage\": \"返回第一页\",\n\t\t\"clearFilters\": \"清除筛选条件\",\n\t\t\"adjustSearch\": \"尝试调整搜索条件\",\n\t\t\"noMemoriesInSystem\": \"系统暂无记忆记录\",\n\t\t\"noDataOnCurrentPage\": \"当前页无数据\",\n\t\t\"checkPageOrFilters\": \"请检查页码或调整筛选条件\",\n\t\t\"fullContent\": \"记忆内容详情\",\n\t\t\"clickToViewFullContent\": \"点击查看完整内容\",\n\t\t\"characters\": \"字符\",\n\t\t\"close\": \"关闭\",\n\t\t\"retry\": \"重试\",\n\t\t\"loadFailed\": \"加载失败\"\n\t},\n\t\"analytics\": {\n\t\t\"title\": \"统计分析\",\n\t\t\"description\": \"深入分析记忆数据的分布、质量和趋势\",\n\t\t\"summary\": \"概览\",\n\t\t\"totalMemories\": \"总记忆数\",\n\t\t\"activeUsers\": \"活跃用户\",\n\t\t\"averageQuality\": \"平均质量\",\n\t\t\"basedOnImportance\": \"基于重要性评分\",\n\t\t\"qualityDistribution\": \"质量评分分布\",\n\t\t\"userActivity\": \"用户活跃度\",\n\t\t\"memoryCount\": \"记忆数量\",\n\t\t\"avgImportance\": \"平均重要性\",\n\t\t\"percentage\": \"百分比\",\n\t\t\"timeTrend\": \"时间趋势\",\n\t\t\"last7Days\": \"最近7天\",\n\t\t\"last30Days\": \"最近30天\",\n\t\t\"newMemoriesTrend\": \"新增记忆趋势\",\n\t\t\"noData\": \"暂无数据\",\n\t\t\"loadingAnalytics\": \"加载分析数据...\",\n\t\t\"currentTotal\": \"当前总数\",\n\t\t\"usersWithMemories\": \"有记忆的用户\",\n\t\t\"historicalOptimization\": \"历史优化记录\",\n\t\t\"memoryTypeDistribution\": \"记忆类型分布\",\n\t\t\"qualityScoreDistribution\": \"质量评分分布\",\n\t\t\"newMemoriesAdded\": \"新增记忆\",\n\t\t\"averageDaily\": \"日均新增\",\n\t\t\"peak\": \"峰值\",\n\t\t\"userDimensionStatistics\": \"用户维度统计\",\n\t\t\"proportion\": \"占比\",\n\t\t\"trend\": \"趋势\",\n\t\t\"analysisTools\": \"分析工具\",\n\t\t\"qualityAnalysisReport\": \"质量分析报告\",\n\t\t\"detailedQualityAnalysis\": \"详细的质量分析\",\n\t\t\"trendPrediction\": \"趋势预测\",\n\t\t\"futureGrowthTrends\": \"预测未来增长趋势\",\n\t\t\"comparativeAnalysis\": \"对比分析\",\n\t\t\"differentTimePeriods\": \"不同时间段对比\",\n\t\t\"top\": \"前\",\n\t\t\"usersAccountFor\": \"用户占总记忆的\",\n\t\t\"ofTotalMemories\": \"\",\n\t\t\"insufficientData\": \"数据不足\"\n\t},\n\t\"monitor\": {\n\t\t\"title\": \"系统监控\",\n\t\t\"description\": \"实时监控系统状态、性能指标和运行日志\",\n\t\t\"systemMetrics\": \"系统指标\",\n\t\t\"memoryUsage\": \"内存使用率\",\n\t\t\"cpuUsage\": \"CPU 使用率\",\n\t\t\"diskUsage\": \"磁盘使用率\",\n\t\t\"activeConnections\": \"活跃连接数\",\n\t\t\"requestCount\": \"请求数量\",\n\t\t\"errorRate\": \"错误率\",\n\t\t\"responseTime\": \"响应时间\",\n\t\t\"alerts\": \"告警\",\n\t\t\"noAlerts\": \"无告警\",\n\t\t\"critical\": \"严重\",\n\t\t\"warning\": \"警告\",\n\t\t\"info\": \"信息\",\n\t\t\"healthy\": \"健康\",\n\t\t\"threshold\": \"阈值\",\n\t\t\"current\": \"当前值\",\n\t\t\"status\": \"状态\",\n\t\t\"lastUpdated\": \"最后更新\",\n\t\t\"autoRefresh\": \"自动刷新\",\n\t\t\"refreshNow\": \"立即刷新\",\n\t\t\"resourceUsage\": \"资源使用\",\n\t\t\"networkStatus\": \"网络状态\",\n\t\t\"throughput\": \"吞吐量\",\n\t\t\"performanceMetrics\": \"性能指标\",\n\t\t\"apiResponseTime\": \"API响应时间\",\n\t\t\"searchLatency\": \"搜索延迟\",\n\t\t\"healthCheck\": \"健康检查\",\n\t\t\"vectorQuery\": \"向量查询\",\n\t\t\"usageRate\": \"使用率\",\n\t\t\"systemAlerts\": \"系统告警\",\n\t\t\"unprocessed\": \"未处理\",\n\t\t\"error\": \"错误\",\n\t\t\"realtimeLogs\": \"实时日志\",\n\t\t\"clear\": \"清空\",\n\t\t\"noLogs\": \"暂无日志\",\n\t\t\"monitoringTools\": \"监控工具\",\n\t\t\"healthCheckTool\": \"健康检查\",\n\t\t\"performanceTest\": \"性能测试\",\n\t\t\"diagnosticTools\": \"诊断工具\",\n\t\t\"comprehensiveHealthCheck\": \"全面检查系统健康状态\",\n\t\t\"runPerformanceBenchmark\": \"运行性能基准测试\",\n\t\t\"systemDiagnosisAndRepair\": \"系统问题诊断和修复\"\n\t},\n\t\"optimization\": {\n\t\t\"title\": \"记忆优化\",\n\t\t\"description\": \"检测和优化记忆数据,提升系统性能和信息密度\",\n\t\t\"runOptimization\": \"运行优化\",\n\t\t\"optimizationHistory\": \"优化历史\",\n\t\t\"status\": \"状态\",\n\t\t\"pending\": \"等待中\",\n\t\t\"running\": \"运行中\",\n\t\t\"completed\": \"已完成\",\n\t\t\"failed\": \"失败\",\n\t\t\"totalMemories\": \"总记忆数\",\n\t\t\"processed\": \"已处理\",\n\t\t\"deduplicated\": \"去重\",\n\t\t\"merged\": \"合并\",\n\t\t\"enhanced\": \"增强\",\n\t\t\"errors\": \"错误\",\n\t\t\"startTime\": \"开始时间\",\n\t\t\"endTime\": \"结束时间\",\n\t\t\"duration\": \"持续时间\",\n\t\t\"actions\": \"操作\",\n\t\t\"viewDetails\": \"查看详情\",\n\t\t\"cancel\": \"取消\",\n\t\t\"dryRun\": \"试运行\",\n\t\t\"verbose\": \"详细模式\",\n\t\t\"startOptimization\": \"开始优化\",\n\t\t\"optimizationStarted\": \"优化已开始\",\n\t\t\"noHistory\": \"暂无优化历史\",\n\t\t\"optimizationPanel\": \"优化面板\",\n\t\t\"optimizationControl\": \"优化控制\",\n\t\t\"optimizationStrategy\": \"优化策略\",\n\t\t\"fullOptimization\": \"全面优化\",\n\t\t\"deduplicationOptimization\": \"去重优化\",\n\t\t\"qualityOptimization\": \"质量优化\",\n\t\t\"relevanceOptimization\": \"相关性优化\",\n\t\t\"detectAllIssues\": \"检测并处理所有类型的问题\",\n\t\t\"handleDuplicatesOnly\": \"仅处理重复记忆\",\n\t\t\"handleLowQuality\": \"处理低质量记忆\",\n\t\t\"optimizeRelevance\": \"优化记忆相关性\",\n\t\t\"estimatedTime\": \"预计时间\",\n\t\t\"optimizationOptions\": \"优化选项\",\n\t\t\"previewMode\": \"预览模式\",\n\t\t\"analyzeOnly\": \"仅分析问题,不执行优化\",\n\t\t\"aggressiveMode\": \"激进模式\",\n\t\t\"stricterStandards\": \"更严格的优化标准\",\n\t\t\"timeout\": \"超时时间\",\n\t\t\"minutes\": \"分钟\",\n\t\t\"estimatedImpact\": \"预估影响\",\n\t\t\"estimatedAffectedMemories\": \"预计影响记忆\",\n\t\t\"estimatedSpaceSaved\": \"预计节省空间\",\n\t\t\"estimatedQualityImprovement\": \"预计提升质量\",\n\t\t\"previewModeWarning\": \"预览模式不会实际修改数据\",\n\t\t\"optimizationWarning\": \"优化将永久修改记忆数据\",\n\t\t\"cancelOptimization\": \"取消优化\",\n\t\t\"analyzeIssues\": \"分析问题\",\n\t\t\"exportReport\": \"导出优化报告\",\n\t\t\"optimizationProgress\": \"优化进度\",\n\t\t\"analyzingIssues\": \"分析问题中...\",\n\t\t\"executingOptimization\": \"执行优化中...\",\n\t\t\"optimizationComplete\": \"优化完成\",\n\t\t\"optimizationFailed\": \"优化失败\",\n\t\t\"currentPhase\": \"当前阶段\",\n\t\t\"memoriesProcessed\": \"已处理记忆\",\n\t\t\"estimatedRemainingTime\": \"预计剩余时间\",\n\t\t\"issueAnalysis\": \"问题分析\",\n\t\t\"execution\": \"执行优化\",\n\t\t\"realtimeLogs\": \"实时日志\",\n\t\t\"detectedIssues\": \"检测到的问题\",\n\t\t\"rescan\": \"重新检测\",\n\t\t\"high\": \"高\",\n\t\t\"medium\": \"中\",\n\t\t\"low\": \"低\",\n\t\t\"duplicateMemories\": \"重复记忆\",\n\t\t\"lowQualityMemories\": \"低质量记忆\",\n\t\t\"outdatedMemories\": \"过时记忆\",\n\t\t\"misclassifiedMemories\": \"分类不当\",\n\t\t\"semanticSimilarity\": \"语义相似度超过85%的记忆\",\n\t\t\"importanceBelowThreshold\": \"重要性评分低于50%的记忆\",\n\t\t\"notUpdated30Days\": \"超过30天未更新的记忆\",\n\t\t\"typeContentMismatch\": \"类型与内容不匹配的记忆\",\n\t\t\"optimizationId\": \"优化ID\",\n\t\t\"strategy\": \"策略\",\n\t\t\"timeConsumed\": \"耗时\",\n\t\t\"affectedMemories\": \"影响记忆\",\n\t\t\"spaceSaved\": \"节省空间\",\n\t\t\"report\": \"报告\",\n\t\t\"totalOptimizations\": \"共 {count} 次优化记录\",\n\t\t\"clearHistory\": \"清空历史记录\"\n\t},\n\t\"settings\": {\n\t\t\"title\": \"设置\",\n\t\t\"languageSettings\": \"语言设置\",\n\t\t\"selectLanguage\": \"选择语言\",\n\t\t\"saveSettings\": \"保存设置\",\n\t\t\"settingsSaved\": \"设置保存成功\"\n\t}\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 1.0, + "lines_of_code": 306, + "number_of_classes": 0, + "number_of_functions": 0 + }, + "dependencies": [], + "detailed_description": "该组件是一个JSON格式的多语言配置文件,专用于存储中文(简体)用户界面文本。文件按功能模块组织为common、navigation、dashboard、memories、analytics、monitor、optimization和settings八个主要命名空间,覆盖了应用的通用术语、导航菜单、仪表板、记忆管理、统计分析、系统监控、记忆优化和设置等全量UI文本。每个键值对提供英文原文到中文翻译的映射,支持国际化(i18n)功能,使前端界面能够根据用户语言偏好动态加载对应语言资源。", + "interfaces": [], + "responsibilities": [ + "提供系统用户界面的中文文本翻译", + "按功能模块组织多语言资源,支持按需加载", + "维护统一的中文术语标准,确保UI文本一致性", + "支持国际化框架的本地化资源加载" + ] + }, + { + "code_dossier": { + "code_purpose": "config", + "description": "日本語のローカライゼーションファイル。UIのすべてのテキストラベル、メッセージ、エラーテキスト、ナビゲーション項目、分析ラベルなどを含む。", + "file_path": "cortex-mem-insights/src/lib/i18n/locales/ja.json", + "functions": [], + "importance_score": 0.6, + "interfaces": [], + "name": "ja.json", + "source_summary": "{\n\t\"common\": {\n\t\t\"appName\": \"Cortex Memory Insights\",\n\t\t\"loading\": \"読み込み中...\",\n\t\t\"error\": \"エラー\",\n\t\t\"success\": \"成功\",\n\t\t\"save\": \"保存\",\n\t\t\"cancel\": \"キャンセル\",\n\t\t\"delete\": \"削除\",\n\t\t\"edit\": \"編集\",\n\t\t\"view\": \"表示\",\n\t\t\"search\": \"検索\",\n\t\t\"filter\": \"フィルター\",\n\t\t\"sort\": \"並び替え\",\n\t\t\"refresh\": \"更新\",\n\t\t\"back\": \"戻る\",\n\t\t\"next\": \"次へ\",\n\t\t\"previous\": \"前へ\",\n\t\t\"confirm\": \"確認\",\n\t\t\"close\": \"閉じる\",\n\t\t\"language\": \"言語\",\n\t\t\"language_this\": \"日本語\",\n\t\t\"english\": \"英語\",\n\t\t\"chinese\": \"中国語\",\n\t\t\"japanese\": \"日本語\",\n\t\t\"unknown\": \"不明\",\n\t\t\"unit\": \"件\"\n\t},\n\t\"navigation\": {\n\t\t\"dashboard\": \"ダッシュボード\",\n\t\t\"memories\": \"メモリー\",\n\t\t\"analytics\": \"分析\",\n\t\t\"monitor\": \"モニター\",\n\t\t\"optimization\": \"最適化\",\n\t\t\"settings\": \"設定\"\n\t},\n\t\"dashboard\": {\n\t\t\"title\": \"ダッシュボード\",\n\t\t\"welcome\": \"Cortex Memory インサイトへようこそ\",\n\t\t\"totalMemories\": \"総メモリー数\",\n\t\t\"optimizationCount\": \"最適化回数\",\n\t\t\"averageQuality\": \"平均品質\",\n\t\t\"qualityDistribution\": \"品質分布\",\n\t\t\"highMediumLow\": \"高/中/低\",\n\t\t\"systemStatus\": \"システム状態\",\n\t\t\"recentMemories\": \"最近のメモリー\",\n\t\t\"viewAll\": \"すべて表示\",\n\t\t\"noMemories\": \"メモリーがありません\",\n\t\t\"detecting\": \"検出中\",\n\t\t\"healthy\": \"正常\",\n\t\t\"unhealthy\": \"異常\",\n\t\t\"cortexMemService\": \"Cortex Memory サービス\",\n\t\t\"llmService\": \"LLM サービス\",\n\t\t\"vectorStore\": \"ベクトルストア\",\n\t\t\"latency\": \"レイテンシ\",\n\t\t\"version\": \"バージョン\",\n\t\t\"lastCheck\": \"最終チェック\"\n\t},\n\t\"memories\": {\n\t\t\"title\": \"メモリー管理\",\n\t\t\"description\": \"すべてのメモリーレコードを閲覧、検索、管理\",\n\t\t\"searchPlaceholder\": \"メモリー内容、ID、ユーザーまたはエージェントを検索...\",\n\t\t\"typeFilter\": \"タイプフィルター\",\n\t\t\"allTypes\": \"すべてのタイプ\",\n\t\t\"conversational\": \"会話\",\n\t\t\"factual\": \"事実\",\n\t\t\"personal\": \"個人\",\n\t\t\"procedural\": \"手順\",\n\t\t\"unknown\": \"不明\",\n\t\t\"sortBy\": \"並び替え\",\n\t\t\"createdAt\": \"作成日時\",\n\t\t\"importance\": \"重要度\",\n\t\t\"ascending\": \"昇順\",\n\t\t\"descending\": \"降順\",\n\t\t\"selectAll\": \"すべて選択\",\n\t\t\"batchOperations\": \"一括操作\",\n\t\t\"deleteSelected\": \"選択を削除\",\n\t\t\"exportSelected\": \"選択をエクスポート\",\n\t\t\"optimizeSelected\": \"選択を最適化\",\n\t\t\"noMemoriesFound\": \"メモリーが見つかりません\",\n\t\t\"loadingMemories\": \"メモリーを読み込み中...\",\n\t\t\"memoryDetails\": \"メモリー詳細\",\n\t\t\"content\": \"内容\",\n\t\t\"type\": \"タイプ\",\n\t\t\"userId\": \"ユーザーID\",\n\t\t\"agentId\": \"エージェントID\",\n\t\t\"userAgent\": \"ユーザー/エージェント\",\n\t\t\"created\": \"作成日時\",\n\t\t\"updated\": \"更新日時\",\n\t\t\"actions\": \"操作\",\n\t\t\"confirmDelete\": \"削除確認\",\n\t\t\"deleteMemoryConfirm\": \"このメモリーを削除してもよろしいですか?\",\n\t\t\"deleteMemoriesConfirm\": \"{count} 件のメモリーを削除してもよろしいですか?\",\n\t\t\"memoryDeleted\": \"メモリーを削除しました\",\n\t\t\"memoriesDeleted\": \"{count} 件のメモリーを削除しました\",\n\t\t\"exportFormat\": \"エクスポート形式\",\n\t\t\"json\": \"JSON\",\n\t\t\"csv\": \"CSV\",\n\t\t\"txt\": \"テキスト\",\n\t\t\"search\": \"検索\",\n\t\t\"reset\": \"リセット\",\n\t\t\"sort\": \"並び替え\",\n\t\t\"totalMemories\": \"総メモリー数\",\n\t\t\"showing\": \"表示中\",\n\t\t\"to\": \"から\",\n\t\t\"of\": \"件、合計\",\n\t\t\"page\": \"ページ\",\n\t\t\"previousPage\": \"前へ\",\n\t\t\"nextPage\": \"次へ\",\n\t\t\"goToFirstPage\": \"最初のページへ\",\n\t\t\"clearFilters\": \"フィルターをクリア\",\n\t\t\"adjustSearch\": \"検索条件を調整してみてください\",\n\t\t\"noMemoriesInSystem\": \"システムにメモリーレコードがありません\",\n\t\t\"noDataOnCurrentPage\": \"現在のページにデータがありません\",\n\t\t\"checkPageOrFilters\": \"ページ番号を確認するか、フィルターを調整してください\",\n\t\t\"fullContent\": \"メモリー内容詳細\",\n\t\t\"clickToViewFullContent\": \"クリックして完全な内容を表示\",\n\t\t\"characters\": \"文字\",\n\t\t\"close\": \"閉じる\",\n\t\t\"retry\": \"再試行\",\n\t\t\"loadFailed\": \"読み込みに失敗しました\"\n\t},\n\t\"analytics\": {\n\t\t\"title\": \"分析\",\n\t\t\"description\": \"メモリーデータの分布、品質、トレンドの詳細な分析\",\n\t\t\"summary\": \"概要\",\n\t\t\"totalMemories\": \"総メモリー数\",\n\t\t\"activeUsers\": \"アクティブユーザー\",\n\t\t\"averageQuality\": \"平均品質\",\n\t\t\"basedOnImportance\": \"重要度スコアに基づく\",\n\t\t\"qualityDistribution\": \"品質スコア分布\",\n\t\t\"userActivity\": \"ユーザー活動\",\n\t\t\"memoryCount\": \"メモリー数\",\n\t\t\"avgImportance\": \"平均重要度\",\n\t\t\"percentage\": \"パーセンテージ\",\n\t\t\"timeTrend\": \"時間トレンド\",\n\t\t\"last7Days\": \"過去7日間\",\n\t\t\"last30Days\": \"過去30日間\",\n\t\t\"newMemoriesTrend\": \"新規メモリートレンド\",\n\t\t\"noData\": \"データがありません\",\n\t\t\"loadingAnalytics\": \"分析データを読み込み中...\",\n\t\t\"currentTotal\": \"現在の合計\",\n\t\t\"usersWithMemories\": \"メモリーを持つユーザー\",\n\t\t\"historicalOptimization\": \"過去の最適化記録\",\n\t\t\"memoryTypeDistribution\": \"メモリータイプ分布\",\n\t\t\"qualityScoreDistribution\": \"品質スコア分布\",\n\t\t\"newMemoriesAdded\": \"新規メモリー追加\",\n\t\t\"averageDaily\": \"日平均\",\n\t\t\"peak\": \"ピーク\",\n\t\t\"userDimensionStatistics\": \"ユーザー次元統計\",\n\t\t\"proportion\": \"割合\",\n\t\t\"trend\": \"トレンド\",\n\t\t\"analysisTools\": \"分析ツール\",\n\t\t\"qualityAnalysisReport\": \"品質分析レポート\",\n\t\t\"detailedQualityAnalysis\": \"詳細な品質分析\",\n\t\t\"trendPrediction\": \"トレンド予測\",\n\t\t\"futureGrowthTrends\": \"将来の成長トレンドを予測\",\n\t\t\"comparativeAnalysis\": \"比較分析\",\n\t\t\"differentTimePeriods\": \"異なる期間の比較\",\n\t\t\"top\": \"上位\",\n\t\t\"usersAccountFor\": \"ユーザーが総メモリーの\",\n\t\t\"ofTotalMemories\": \"を占めています\",\n\t\t\"insufficientData\": \"データ不足\"\n\t},\n\t\"monitor\": {\n\t\t\"title\": \"モニター\",\n\t\t\"description\": \"システム状態、パフォーマンス指標、実行ログのリアルタイム監視\",\n\t\t\"systemMetrics\": \"システムメトリクス\",\n\t\t\"memoryUsage\": \"メモリ使用率\",\n\t\t\"cpuUsage\": \"CPU 使用率\",\n\t\t\"diskUsage\": \"ディスク使用率\",\n\t\t\"activeConnections\": \"アクティブ接続数\",\n\t\t\"requestCount\": \"リクエスト数\",\n\t\t\"errorRate\": \"エラー率\",\n\t\t\"responseTime\": \"応答時間\",\n\t\t\"alerts\": \"アラート\",\n\t\t\"noAlerts\": \"アラートなし\",\n\t\t\"critical\": \"重大\",\n\t\t\"warning\": \"警告\",\n\t\t\"info\": \"情報\",\n\t\t\"healthy\": \"正常\",\n\t\t\"threshold\": \"閾値\",\n\t\t\"current\": \"現在値\",\n\t\t\"status\": \"状態\",\n\t\t\"lastUpdated\": \"最終更新\",\n\t\t\"autoRefresh\": \"自動更新\",\n\t\t\"refreshNow\": \"今すぐ更新\",\n\t\t\"resourceUsage\": \"リソース使用率\",\n\t\t\"networkStatus\": \"ネットワーク状態\",\n\t\t\"throughput\": \"スループット\",\n\t\t\"performanceMetrics\": \"パフォーマンス指標\",\n\t\t\"apiResponseTime\": \"API応答時間\",\n\t\t\"searchLatency\": \"検索遅延\",\n\t\t\"healthCheck\": \"ヘルスチェック\",\n\t\t\"vectorQuery\": \"ベクタークエリ\",\n\t\t\"usageRate\": \"使用率\",\n\t\t\"systemAlerts\": \"システムアラート\",\n\t\t\"unprocessed\": \"未処理\",\n\t\t\"error\": \"エラー\",\n\t\t\"realtimeLogs\": \"リアルタイムログ\",\n\t\t\"clear\": \"クリア\",\n\t\t\"noLogs\": \"ログなし\",\n\t\t\"monitoringTools\": \"監視ツール\",\n\t\t\"healthCheckTool\": \"ヘルスチェック\",\n\t\t\"performanceTest\": \"パフォーマンステスト\",\n\t\t\"diagnosticTools\": \"診断ツール\",\n\t\t\"comprehensiveHealthCheck\": \"システムの包括的なヘルスチェック\",\n\t\t\"runPerformanceBenchmark\": \"パフォーマンスベンチマークテストの実行\",\n\t\t\"systemDiagnosisAndRepair\": \"システム問題の診断と修復\"\n\t},\n\t\"optimization\": {\n\t\t\"title\": \"メモリー最適化\",\n\t\t\"description\": \"メモリーデータの検出と最適化、システムパフォーマンスと情報密度の向上\",\n\t\t\"runOptimization\": \"最適化を実行\",\n\t\t\"optimizationHistory\": \"最適化履歴\",\n\t\t\"status\": \"状態\",\n\t\t\"pending\": \"待機中\",\n\t\t\"running\": \"実行中\",\n\t\t\"completed\": \"完了\",\n\t\t\"failed\": \"失敗\",\n\t\t\"totalMemories\": \"総メモリー数\",\n\t\t\"processed\": \"処理済み\",\n\t\t\"deduplicated\": \"重複排除\",\n\t\t\"merged\": \"統合\",\n\t\t\"enhanced\": \"強化\",\n\t\t\"errors\": \"エラー\",\n\t\t\"startTime\": \"開始時間\",\n\t\t\"endTime\": \"終了時間\",\n\t\t\"duration\": \"期間\",\n\t\t\"actions\": \"操作\",\n\t\t\"viewDetails\": \"詳細を表示\",\n\t\t\"cancel\": \"キャンセル\",\n\t\t\"dryRun\": \"ドライラン\",\n\t\t\"verbose\": \"詳細モード\",\n\t\t\"startOptimization\": \"最適化を開始\",\n\t\t\"optimizationStarted\": \"最適化を開始しました\",\n\t\t\"noHistory\": \"最適化履歴がありません\",\n\t\t\"optimizationPanel\": \"最適化パネル\",\n\t\t\"optimizationControl\": \"最適化制御\",\n\t\t\"optimizationStrategy\": \"最適化戦略\",\n\t\t\"fullOptimization\": \"全面最適化\",\n\t\t\"deduplicationOptimization\": \"重複排除最適化\",\n\t\t\"qualityOptimization\": \"品質最適化\",\n\t\t\"relevanceOptimization\": \"関連性最適化\",\n\t\t\"detectAllIssues\": \"すべてのタイプの問題を検出・処理\",\n\t\t\"handleDuplicatesOnly\": \"重複メモリーのみ処理\",\n\t\t\"handleLowQuality\": \"低品質メモリーを処理\",\n\t\t\"optimizeRelevance\": \"メモリー関連性を最適化\",\n\t\t\"estimatedTime\": \"予想時間\",\n\t\t\"optimizationOptions\": \"最適化オプション\",\n\t\t\"previewMode\": \"プレビューモード\",\n\t\t\"analyzeOnly\": \"問題分析のみ、最適化は実行しない\",\n\t\t\"aggressiveMode\": \"アグレッシブモード\",\n\t\t\"stricterStandards\": \"より厳格な最適化基準\",\n\t\t\"timeout\": \"タイムアウト\",\n\t\t\"minutes\": \"分\",\n\t\t\"estimatedImpact\": \"予想影響\",\n\t\t\"estimatedAffectedMemories\": \"予想影響メモリー数\",\n\t\t\"estimatedSpaceSaved\": \"予想節約容量\",\n\t\t\"estimatedQualityImprovement\": \"予想品質向上\",\n\t\t\"previewModeWarning\": \"プレビューモードでは実際にデータを変更しません\",\n\t\t\"optimizationWarning\": \"最適化はメモリーデータを永続的に変更します\",\n\t\t\"cancelOptimization\": \"最適化をキャンセル\",\n\t\t\"analyzeIssues\": \"問題を分析\",\n\t\t\"exportReport\": \"レポートをエクスポート\",\n\t\t\"optimizationProgress\": \"最適化進捗\",\n\t\t\"analyzingIssues\": \"問題分析中...\",\n\t\t\"executingOptimization\": \"最適化実行中...\",\n\t\t\"optimizationComplete\": \"最適化完了\",\n\t\t\"optimizationFailed\": \"最適化失敗\",\n\t\t\"currentPhase\": \"現在のフェーズ\",\n\t\t\"memoriesProcessed\": \"処理済みメモリー\",\n\t\t\"estimatedRemainingTime\": \"予想残り時間\",\n\t\t\"issueAnalysis\": \"問題分析\",\n\t\t\"execution\": \"実行\",\n\t\t\"realtimeLogs\": \"リアルタイムログ\",\n\t\t\"detectedIssues\": \"検出された問題\",\n\t\t\"rescan\": \"再スキャン\",\n\t\t\"high\": \"高\",\n\t\t\"medium\": \"中\",\n\t\t\"low\": \"低\",\n\t\t\"duplicateMemories\": \"重複メモリー\",\n\t\t\"lowQualityMemories\": \"低品質メモリー\",\n\t\t\"outdatedMemories\": \"古いメモリー\",\n\t\t\"misclassifiedMemories\": \"分類不適切\",\n\t\t\"semanticSimilarity\": \"意味的類似度85%以上のメモリー\",\n\t\t\"importanceBelowThreshold\": \"重要度スコア50%未満のメモリー\",\n\t\t\"notUpdated30Days\": \"30日以上更新されていないメモリー\",\n\t\t\"typeContentMismatch\": \"タイプと内容が一致しないメモリー\",\n\t\t\"optimizationId\": \"最適化ID\",\n\t\t\"strategy\": \"戦略\",\n\t\t\"timeConsumed\": \"所要時間\",\n\t\t\"affectedMemories\": \"影響メモリー数\",\n\t\t\"spaceSaved\": \"節約容量\",\n\t\t\"report\": \"レポート\",\n\t\t\"totalOptimizations\": \"合計 {count} 件の最適化記録\",\n\t\t\"clearHistory\": \"履歴をクリア\"\n\t},\n\t\"settings\": {\n\t\t\"title\": \"設定\",\n\t\t\"languageSettings\": \"言語設定\",\n\t\t\"selectLanguage\": \"言語を選択\",\n\t\t\"saveSettings\": \"設定を保存\",\n\t\t\"settingsSaved\": \"設定を保存しました\"\n\t}\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 1.0, + "lines_of_code": 306, + "number_of_classes": 0, + "number_of_functions": 0 + }, + "dependencies": [], + "detailed_description": "このファイルは、Cortex Memory Insightsアプリケーションの日本語ローカライゼーション(i18n)を担当する設定ファイルです。アプリケーションのUIに表示されるすべての静的テキストを日本語に翻訳して提供します。共通ボタンラベル(例: 保存、削除、編集)、ナビゲーションメニュー(ダッシュボード、メモリー、分析など)、ダッシュボードの統計ラベル、メモリー管理画面のフィルター・検索オプション、分析モジュールのグラフラベル、モニタリング画面のメトリクス名、最適化ツールのステータスメッセージ、設定画面の言語選択オプションなどを含む幅広いUI要素に対応しています。動的値の挿入(例: {count})もサポートしており、国際化対応のベストプラクティスに準拠しています。", + "interfaces": [], + "responsibilities": [ + "アプリケーションUIの全テキスト要素を日本語に翻訳して提供する", + "多言語対応を実現し、ユーザーの言語設定に応じて適切な翻訳をロードする", + "動的コンテンツ(例: 削除件数)を含むメッセージのテンプレートを管理する", + "一貫した日本語表記とUI用語体系を維持する", + "エラーメッセージ、成功メッセージ、プロンプトなどのユーザー通知文言を定義する" + ] + }, + { + "code_dossier": { + "code_purpose": "config", + "description": "English language translation file for the Cortex Memory Insights application, containing all user-facing text labels and messages organized by modules.", + "file_path": "cortex-mem-insights/src/lib/i18n/locales/en.json", + "functions": [], + "importance_score": 0.6, + "interfaces": [], + "name": "en.json", + "source_summary": "{\n\t\"common\": {\n\t\t\"appName\": \"Cortex Memory Insights\",\n\t\t\"loading\": \"Loading...\",\n\t\t\"error\": \"Error\",\n\t\t\"success\": \"Success\",\n\t\t\"save\": \"Save\",\n\t\t\"cancel\": \"Cancel\",\n\t\t\"delete\": \"Delete\",\n\t\t\"edit\": \"Edit\",\n\t\t\"view\": \"View\",\n\t\t\"search\": \"Search\",\n\t\t\"filter\": \"Filter\",\n\t\t\"sort\": \"Sort\",\n\t\t\"refresh\": \"Refresh\",\n\t\t\"back\": \"Back\",\n\t\t\"next\": \"Next\",\n\t\t\"previous\": \"Previous\",\n\t\t\"confirm\": \"Confirm\",\n\t\t\"close\": \"Close\",\n\t\t\"language\": \"Language\",\n\t\t\"language_this\": \"English\",\n\t\t\"english\": \"English\",\n\t\t\"chinese\": \"Chinese\",\n\t\t\"japanese\": \"Japanese\",\n\t\t\"unknown\": \"Unknown\",\n\t\t\"unit\": \"units\"\n\t},\n\t\"navigation\": {\n\t\t\"dashboard\": \"Dashboard\",\n\t\t\"memories\": \"Memories\",\n\t\t\"analytics\": \"Analytics\",\n\t\t\"monitor\": \"Monitor\",\n\t\t\"optimization\": \"Optimization\",\n\t\t\"settings\": \"Settings\"\n\t},\n\t\"dashboard\": {\n\t\t\"title\": \"Dashboard\",\n\t\t\"welcome\": \"Welcome to Cortex Memory Insights\",\n\t\t\"totalMemories\": \"Total Memories\",\n\t\t\"optimizationCount\": \"Optimizations\",\n\t\t\"averageQuality\": \"Average Quality\",\n\t\t\"qualityDistribution\": \"Quality Distribution\",\n\t\t\"highMediumLow\": \"High/Medium/Low\",\n\t\t\"systemStatus\": \"System Status\",\n\t\t\"recentMemories\": \"Recent Memories\",\n\t\t\"viewAll\": \"View All\",\n\t\t\"noMemories\": \"No memories available\",\n\t\t\"detecting\": \"Detecting\",\n\t\t\"healthy\": \"Healthy\",\n\t\t\"unhealthy\": \"Unhealthy\",\n\t\t\"cortexMemService\": \"Cortex Memory Service\",\n\t\t\"llmService\": \"LLM Service\",\n\t\t\"vectorStore\": \"Vector Store\",\n\t\t\"latency\": \"Latency\",\n\t\t\"version\": \"Version\",\n\t\t\"lastCheck\": \"Last Check\"\n\t},\n\t\"memories\": {\n\t\t\"title\": \"Memories\",\n\t\t\"description\": \"Browse, search and manage all memory records\",\n\t\t\"searchPlaceholder\": \"Search memory content, ID, user or Agent...\",\n\t\t\"typeFilter\": \"Type Filter\",\n\t\t\"allTypes\": \"All Types\",\n\t\t\"conversational\": \"Conversational\",\n\t\t\"factual\": \"Factual\",\n\t\t\"personal\": \"Personal\",\n\t\t\"procedural\": \"Procedural\",\n\t\t\"unknown\": \"Unknown\",\n\t\t\"sortBy\": \"Sort By\",\n\t\t\"createdAt\": \"Created At\",\n\t\t\"importance\": \"Importance\",\n\t\t\"ascending\": \"Ascending\",\n\t\t\"descending\": \"Descending\",\n\t\t\"selectAll\": \"Select All\",\n\t\t\"batchOperations\": \"Batch Operations\",\n\t\t\"deleteSelected\": \"Delete Selected\",\n\t\t\"exportSelected\": \"Export Selected\",\n\t\t\"optimizeSelected\": \"Optimize Selected\",\n\t\t\"noMemoriesFound\": \"No memories found\",\n\t\t\"loadingMemories\": \"Loading memories...\",\n\t\t\"memoryDetails\": \"Memory Details\",\n\t\t\"content\": \"Content\",\n\t\t\"type\": \"Type\",\n\t\t\"userId\": \"User ID\",\n\t\t\"agentId\": \"Agent ID\",\n\t\t\"userAgent\": \"User/Agent\",\n\t\t\"created\": \"Created\",\n\t\t\"updated\": \"Updated\",\n\t\t\"actions\": \"Actions\",\n\t\t\"confirmDelete\": \"Confirm Delete\",\n\t\t\"deleteMemoryConfirm\": \"Are you sure you want to delete this memory?\",\n\t\t\"deleteMemoriesConfirm\": \"Are you sure you want to delete {count} memories?\",\n\t\t\"memoryDeleted\": \"Memory deleted successfully\",\n\t\t\"memoriesDeleted\": \"{count} memories deleted successfully\",\n\t\t\"exportFormat\": \"Export Format\",\n\t\t\"json\": \"JSON\",\n\t\t\"csv\": \"CSV\",\n\t\t\"txt\": \"Text\",\n\t\t\"search\": \"Search\",\n\t\t\"reset\": \"Reset\",\n\t\t\"sort\": \"Sort\",\n\t\t\"totalMemories\": \"Total memories\",\n\t\t\"showing\": \"Showing\",\n\t\t\"to\": \"to\",\n\t\t\"of\": \"of\",\n\t\t\"page\": \"Page\",\n\t\t\"previousPage\": \"Previous\",\n\t\t\"nextPage\": \"Next\",\n\t\t\"goToFirstPage\": \"Go to first page\",\n\t\t\"clearFilters\": \"Clear filters\",\n\t\t\"adjustSearch\": \"Try adjusting search criteria\",\n\t\t\"noMemoriesInSystem\": \"No memory records in system\",\n\t\t\"noDataOnCurrentPage\": \"No data on current page\",\n\t\t\"checkPageOrFilters\": \"Check page number or adjust filters\",\n\t\t\"fullContent\": \"Full Content\",\n\t\t\"clickToViewFullContent\": \"Click to view full content\",\n\t\t\"characters\": \"characters\",\n\t\t\"close\": \"Close\",\n\t\t\"retry\": \"Retry\",\n\t\t\"loadFailed\": \"Load failed\"\n\t},\n\t\"analytics\": {\n\t\t\"title\": \"Analytics\",\n\t\t\"description\": \"In-depth analysis of memory data distribution, quality and trends\",\n\t\t\"summary\": \"Summary\",\n\t\t\"totalMemories\": \"Total Memories\",\n\t\t\"activeUsers\": \"Active Users\",\n\t\t\"averageQuality\": \"Average Quality\",\n\t\t\"basedOnImportance\": \"Based on importance score\",\n\t\t\"qualityDistribution\": \"Quality Distribution\",\n\t\t\"userActivity\": \"User Activity\",\n\t\t\"memoryCount\": \"Memory Count\",\n\t\t\"avgImportance\": \"Avg. Importance\",\n\t\t\"percentage\": \"Percentage\",\n\t\t\"timeTrend\": \"Time Trend\",\n\t\t\"last7Days\": \"Last 7 Days\",\n\t\t\"last30Days\": \"Last 30 Days\",\n\t\t\"newMemoriesTrend\": \"New Memories Trend\",\n\t\t\"noData\": \"No data available\",\n\t\t\"loadingAnalytics\": \"Loading analytics...\",\n\t\t\"currentTotal\": \"Current total\",\n\t\t\"usersWithMemories\": \"Users with memories\",\n\t\t\"historicalOptimization\": \"Historical optimization records\",\n\t\t\"memoryTypeDistribution\": \"Memory Type Distribution\",\n\t\t\"qualityScoreDistribution\": \"Quality Score Distribution\",\n\t\t\"newMemoriesAdded\": \"New Memories Added\",\n\t\t\"averageDaily\": \"Average daily\",\n\t\t\"peak\": \"Peak\",\n\t\t\"userDimensionStatistics\": \"User Dimension Statistics\",\n\t\t\"proportion\": \"Proportion\",\n\t\t\"trend\": \"Trend\",\n\t\t\"analysisTools\": \"Analysis Tools\",\n\t\t\"qualityAnalysisReport\": \"Quality Analysis Report\",\n\t\t\"detailedQualityAnalysis\": \"Detailed quality analysis\",\n\t\t\"trendPrediction\": \"Trend Prediction\",\n\t\t\"futureGrowthTrends\": \"Future growth trends\",\n\t\t\"comparativeAnalysis\": \"Comparative Analysis\",\n\t\t\"differentTimePeriods\": \"Different time periods\",\n\t\t\"top\": \"Top\",\n\t\t\"usersAccountFor\": \"users account for\",\n\t\t\"ofTotalMemories\": \"of total memories\",\n\t\t\"insufficientData\": \"Insufficient data\"\n\t},\n\t\"monitor\": {\n\t\t\"title\": \"Monitor\",\n\t\t\"description\": \"Real-time monitoring of system status, performance metrics and operation logs\",\n\t\t\"systemMetrics\": \"System Metrics\",\n\t\t\"memoryUsage\": \"Memory Usage\",\n\t\t\"cpuUsage\": \"CPU Usage\",\n\t\t\"diskUsage\": \"Disk Usage\",\n\t\t\"activeConnections\": \"Active Connections\",\n\t\t\"requestCount\": \"Request Count\",\n\t\t\"errorRate\": \"Error Rate\",\n\t\t\"responseTime\": \"Response Time\",\n\t\t\"alerts\": \"Alerts\",\n\t\t\"noAlerts\": \"No alerts\",\n\t\t\"critical\": \"Critical\",\n\t\t\"warning\": \"Warning\",\n\t\t\"info\": \"Info\",\n\t\t\"healthy\": \"Healthy\",\n\t\t\"threshold\": \"Threshold\",\n\t\t\"current\": \"Current\",\n\t\t\"status\": \"Status\",\n\t\t\"lastUpdated\": \"Last Updated\",\n\t\t\"autoRefresh\": \"Auto Refresh\",\n\t\t\"refreshNow\": \"Refresh Now\",\n\t\t\"resourceUsage\": \"Resource Usage\",\n\t\t\"networkStatus\": \"Network Status\",\n\t\t\"throughput\": \"Throughput\",\n\t\t\"performanceMetrics\": \"Performance Metrics\",\n\t\t\"apiResponseTime\": \"API Response Time\",\n\t\t\"searchLatency\": \"Search Latency\",\n\t\t\"healthCheck\": \"Health Check\",\n\t\t\"vectorQuery\": \"Vector Query\",\n\t\t\"usageRate\": \"Usage Rate\",\n\t\t\"systemAlerts\": \"System Alerts\",\n\t\t\"unprocessed\": \"Unprocessed\",\n\t\t\"error\": \"Error\",\n\t\t\"realtimeLogs\": \"Realtime Logs\",\n\t\t\"clear\": \"Clear\",\n\t\t\"noLogs\": \"No logs available\",\n\t\t\"monitoringTools\": \"Monitoring Tools\",\n\t\t\"healthCheckTool\": \"Health Check\",\n\t\t\"performanceTest\": \"Performance Test\",\n\t\t\"diagnosticTools\": \"Diagnostic Tools\",\n\t\t\"comprehensiveHealthCheck\": \"Comprehensive system health check\",\n\t\t\"runPerformanceBenchmark\": \"Run performance benchmark tests\",\n\t\t\"systemDiagnosisAndRepair\": \"System problem diagnosis and repair\"\n\t},\n\t\"optimization\": {\n\t\t\"title\": \"Optimization\",\n\t\t\"description\": \"Detect and optimize memory data to improve system performance and information density\",\n\t\t\"runOptimization\": \"Run Optimization\",\n\t\t\"optimizationHistory\": \"Optimization History\",\n\t\t\"status\": \"Status\",\n\t\t\"pending\": \"Pending\",\n\t\t\"running\": \"Running\",\n\t\t\"completed\": \"Completed\",\n\t\t\"failed\": \"Failed\",\n\t\t\"totalMemories\": \"Total Memories\",\n\t\t\"processed\": \"Processed\",\n\t\t\"deduplicated\": \"Deduplicated\",\n\t\t\"merged\": \"Merged\",\n\t\t\"enhanced\": \"Enhanced\",\n\t\t\"errors\": \"Errors\",\n\t\t\"startTime\": \"Start Time\",\n\t\t\"endTime\": \"End Time\",\n\t\t\"duration\": \"Duration\",\n\t\t\"actions\": \"Actions\",\n\t\t\"viewDetails\": \"View Details\",\n\t\t\"cancel\": \"Cancel\",\n\t\t\"dryRun\": \"Dry Run\",\n\t\t\"verbose\": \"Verbose\",\n\t\t\"startOptimization\": \"Start Optimization\",\n\t\t\"optimizationStarted\": \"Optimization started successfully\",\n\t\t\"noHistory\": \"No optimization history\",\n\t\t\"optimizationPanel\": \"Optimization Panel\",\n\t\t\"optimizationControl\": \"Optimization Control\",\n\t\t\"optimizationStrategy\": \"Optimization Strategy\",\n\t\t\"fullOptimization\": \"Full Optimization\",\n\t\t\"deduplicationOptimization\": \"Deduplication Optimization\",\n\t\t\"qualityOptimization\": \"Quality Optimization\",\n\t\t\"relevanceOptimization\": \"Relevance Optimization\",\n\t\t\"detectAllIssues\": \"Detect and handle all types of issues\",\n\t\t\"handleDuplicatesOnly\": \"Handle duplicate memories only\",\n\t\t\"handleLowQuality\": \"Handle low-quality memories\",\n\t\t\"optimizeRelevance\": \"Optimize memory relevance\",\n\t\t\"estimatedTime\": \"Estimated time\",\n\t\t\"optimizationOptions\": \"Optimization Options\",\n\t\t\"previewMode\": \"Preview Mode\",\n\t\t\"analyzeOnly\": \"Analyze issues only, do not execute optimization\",\n\t\t\"aggressiveMode\": \"Aggressive Mode\",\n\t\t\"stricterStandards\": \"Stricter optimization standards\",\n\t\t\"timeout\": \"Timeout\",\n\t\t\"minutes\": \"minutes\",\n\t\t\"estimatedImpact\": \"Estimated Impact\",\n\t\t\"estimatedAffectedMemories\": \"Estimated affected memories\",\n\t\t\"estimatedSpaceSaved\": \"Estimated space saved\",\n\t\t\"estimatedQualityImprovement\": \"Estimated quality improvement\",\n\t\t\"previewModeWarning\": \"Preview mode will not actually modify data\",\n\t\t\"optimizationWarning\": \"Optimization will permanently modify memory data\",\n\t\t\"cancelOptimization\": \"Cancel Optimization\",\n\t\t\"analyzeIssues\": \"Analyze Issues\",\n\t\t\"exportReport\": \"Export Report\",\n\t\t\"optimizationProgress\": \"Optimization Progress\",\n\t\t\"analyzingIssues\": \"Analyzing issues...\",\n\t\t\"executingOptimization\": \"Executing optimization...\",\n\t\t\"optimizationComplete\": \"Optimization complete\",\n\t\t\"optimizationFailed\": \"Optimization failed\",\n\t\t\"currentPhase\": \"Current Phase\",\n\t\t\"memoriesProcessed\": \"Memories processed\",\n\t\t\"estimatedRemainingTime\": \"Estimated remaining time\",\n\t\t\"issueAnalysis\": \"Issue Analysis\",\n\t\t\"execution\": \"Execution\",\n\t\t\"realtimeLogs\": \"Realtime Logs\",\n\t\t\"detectedIssues\": \"Detected Issues\",\n\t\t\"rescan\": \"Rescan\",\n\t\t\"high\": \"High\",\n\t\t\"medium\": \"Medium\",\n\t\t\"low\": \"Low\",\n\t\t\"duplicateMemories\": \"Duplicate Memories\",\n\t\t\"lowQualityMemories\": \"Low Quality Memories\",\n\t\t\"outdatedMemories\": \"Outdated Memories\",\n\t\t\"misclassifiedMemories\": \"Misclassified Memories\",\n\t\t\"semanticSimilarity\": \"Memories with semantic similarity over 85%\",\n\t\t\"importanceBelowThreshold\": \"Memories with importance score below 50%\",\n\t\t\"notUpdated30Days\": \"Memories not updated for over 30 days\",\n\t\t\"typeContentMismatch\": \"Memories with type-content mismatch\",\n\t\t\"optimizationId\": \"Optimization ID\",\n\t\t\"strategy\": \"Strategy\",\n\t\t\"timeConsumed\": \"Time Consumed\",\n\t\t\"affectedMemories\": \"Affected Memories\",\n\t\t\"spaceSaved\": \"Space Saved\",\n\t\t\"report\": \"Report\",\n\t\t\"totalOptimizations\": \"Total {count} optimization records\",\n\t\t\"clearHistory\": \"Clear History\"\n\t},\n\t\"settings\": {\n\t\t\"title\": \"Settings\",\n\t\t\"languageSettings\": \"Language Settings\",\n\t\t\"selectLanguage\": \"Select Language\",\n\t\t\"saveSettings\": \"Save Settings\",\n\t\t\"settingsSaved\": \"Settings saved successfully\"\n\t}\n}\n" + }, + "complexity_metrics": { + "cyclomatic_complexity": 2.0, + "lines_of_code": 306, + "number_of_classes": 0, + "number_of_functions": 0 + }, + "dependencies": [], + "detailed_description": "This component is a JSON-based localization configuration file that stores all English language strings for the Cortex Memory Insights application. It provides translations for UI elements across multiple modules including dashboard, memories, analytics, monitor, optimization, and settings. The structure is hierarchical, grouping related terms under semantic categories (e.g., 'common', 'navigation') to facilitate maintainability and scalability. Each key-value pair represents a translatable string, with keys following a consistent dot-notation path pattern (e.g., 'memories.searchPlaceholder') that reflects the component and property being labeled. The file supports dynamic content through parameterized strings (e.g., '{count}') and maintains consistency in terminology across the application. It serves as a single source of truth for English UI text, enabling centralized updates and reducing hard-coded strings in the frontend codebase.", + "interfaces": [], + "responsibilities": [ + "Store and provide English language translations for all user interface elements", + "Support internationalization by centralizing translatable strings in a structured format", + "Enable dynamic text rendering with parameterized messages for count and context variations", + "Maintain consistent terminology and labeling across different application modules", + "Facilitate easy updates and modifications to UI text without code changes" + ] + }, { "code_dossier": { "code_purpose": "router", @@ -17651,17 +15226,17 @@ Code analysis results from preprocessing phase, including definitions of functio { "code_dossier": { "code_purpose": "module", - "description": "Evaluation module that aggregates various evaluators such as recall, effectiveness, and performance evaluators. Serves as a centralized interface for exporting evaluation functionalities.", - "file_path": "examples/cortex-mem-evaluation/src/evaluator/mod.rs", + "description": "Cortex Mem evaluation module for mem0 evaluation framework. This module integrates Cortex Mem memory system into the evaluation framework.", + "file_path": "examples/lomoco-evaluation/src/cortex_mem/__init__.py", "functions": [], "importance_score": 0.6, "interfaces": [], - "name": "mod.rs", - "source_summary": "//! 评估器模块\n//! \n//! 包含召回率评估器、有效性评估器和性能评估器\n\npub mod metrics;\npub mod recall_evaluator;\npub mod effectiveness_evaluator;\npub mod performance_evaluator;\npub mod real_recall_evaluator;\npub mod real_effectiveness_evaluator;\n\npub use metrics::*;\npub use recall_evaluator::*;\npub use effectiveness_evaluator::*;\npub use performance_evaluator::*;\npub use real_recall_evaluator::*;\npub use real_effectiveness_evaluator::*;" + "name": "__init__.py", + "source_summary": "\"\"\"\nCortex Mem evaluation module for mem0 evaluation framework.\nThis module integrates Cortex Mem memory system into the evaluation framework.\n\"\"\"\n\nfrom .add import CortexMemAdd\nfrom .search import CortexMemSearch\nfrom .config_utils import (\n validate_config,\n check_openai_config,\n get_config_value\n)\n\n__all__ = [\n \"CortexMemAdd\",\n \"CortexMemSearch\",\n \"validate_config\",\n \"check_openai_config\",\n \"get_config_value\"\n]" }, "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 17, + "cyclomatic_complexity": 2.0, + "lines_of_code": 20, "number_of_classes": 0, "number_of_functions": 0 }, @@ -17670,172 +15245,80 @@ Code analysis results from preprocessing phase, including definitions of functio "dependency_type": "module", "is_external": false, "line_number": null, - "name": "metrics", - "path": "examples/cortex-mem-evaluation/src/evaluator/metrics", - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": null, - "name": "recall_evaluator", - "path": "examples/cortex-mem-evaluation/src/evaluator/recall_evaluator", - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": null, - "name": "effectiveness_evaluator", - "path": "examples/cortex-mem-evaluation/src/evaluator/effectiveness_evaluator", - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": null, - "name": "performance_evaluator", - "path": "examples/cortex-mem-evaluation/src/evaluator/performance_evaluator", + "name": ".add", + "path": "examples/lomoco-evaluation/src/cortex_mem/add.py", "version": null }, { "dependency_type": "module", "is_external": false, "line_number": null, - "name": "real_recall_evaluator", - "path": "examples/cortex-mem-evaluation/src/evaluator/real_recall_evaluator", + "name": ".search", + "path": "examples/lomoco-evaluation/src/cortex_mem/search.py", "version": null }, { "dependency_type": "module", "is_external": false, "line_number": null, - "name": "real_effectiveness_evaluator", - "path": "examples/cortex-mem-evaluation/src/evaluator/real_effectiveness_evaluator", + "name": ".config_utils", + "path": "examples/lomoco-evaluation/src/cortex_mem/config_utils.py", "version": null } ], - "detailed_description": "This module acts as a facade for the evaluator submodules, re-exporting all public items from recall_evaluator, effectiveness_evaluator, performance_evaluator, real_recall_evaluator, real_effectiveness_evaluator, and metrics. It enables higher-level modules to import evaluation functionalities through a single unified path. The module is designed to simplify dependency management and provide a clean API boundary for the evaluation logic in the Cortex memory evaluation system.", + "detailed_description": "This __init__.py file serves as the public API entry point for the cortex_mem module. It imports and exposes key classes and utility functions from internal submodules to provide a clean, organized interface for external usage. The module integrates the Cortex Mem memory system into the mem0 evaluation framework by exporting the CortexMemAdd and CortexMemSearch classes for memory operations, along with configuration utilities including validate_config, check_openai_config, and get_config_value for configuration management. Through __all__, it explicitly defines the public interface, enabling controlled exposure of internal components and supporting cleaner imports for users of the package.", "interfaces": [], "responsibilities": [ - "Aggregates and re-exports evaluation-related modules", - "Provides a unified public interface for all evaluator components", - "Organizes evaluation logic into a cohesive subsystem", - "Facilitates modular development and namespace management" + "Define and expose the public API surface of the cortex_mem module", + "Aggregate and re-export core memory operation classes (CortexMemAdd, CortexMemSearch)", + "Provide configuration utility functions for system setup and validation", + "Integrate Cortex Mem memory system into the mem0 evaluation framework", + "Control module-level imports via explicit __all__ declaration" ] }, { "code_dossier": { "code_purpose": "module", - "description": "数据集模块,包含测试数据集的生成、加载和验证功能", - "file_path": "examples/cortex-mem-evaluation/src/dataset/mod.rs", + "description": null, + "file_path": "examples/lomoco-evaluation/src/langmem_eval/__init__.py", "functions": [], "importance_score": 0.6, "interfaces": [], - "name": "mod.rs", - "source_summary": "//! 数据集模块\n//! \n//! 包含测试数据集的生成、加载和验证功能\n\npub mod generator;\npub mod loader;\npub mod types;\npub mod lab_data_integration;\n\npub use generator::*;\npub use loader::*;\npub use types::*;\npub use lab_data_integration::*;" + "name": "__init__.py", + "source_summary": "\"\"\"\nLangMem evaluation module for benchmarking memory systems.\n\"\"\"\n\nfrom .add import LangMemAdd\nfrom .search import LangMemSearch\n\n__all__ = [\"LangMemAdd\", \"LangMemSearch\"]" }, "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 13, - "number_of_classes": 0, + "cyclomatic_complexity": 2.0, + "lines_of_code": 8, + "number_of_classes": 2, "number_of_functions": 0 }, "dependencies": [ { - "dependency_type": "module", - "is_external": false, - "line_number": 6, - "name": "generator", - "path": "examples/cortex-mem-evaluation/src/dataset/generator", - "version": null - }, - { - "dependency_type": "module", - "is_external": false, - "line_number": 7, - "name": "loader", - "path": "examples/cortex-mem-evaluation/src/dataset/loader", - "version": null - }, - { - "dependency_type": "module", + "dependency_type": "internal", "is_external": false, - "line_number": 8, - "name": "types", - "path": "examples/cortex-mem-evaluation/src/dataset/types", + "line_number": null, + "name": "add", + "path": null, "version": null }, { - "dependency_type": "module", + "dependency_type": "internal", "is_external": false, - "line_number": 9, - "name": "lab_data_integration", - "path": "examples/cortex-mem-evaluation/src/dataset/lab_data_integration", + "line_number": null, + "name": "search", + "path": null, "version": null } ], - "detailed_description": "该组件是数据集功能的聚合模块,通过Rust的mod系统组织和重新导出子模块(generator、loader、types、lab_data_integration)中的所有公共项。其主要作用是为上层模块提供统一的数据集相关功能访问接口,包括数据生成、加载、类型定义和实验数据集成等能力。该模块本身不包含具体实现逻辑,而是作为功能门面(facade)简化外部调用者的使用。", - "interfaces": [], - "responsibilities": [ - "组织和聚合数据集相关的子模块", - "统一导出数据集功能接口供外部使用", - "提供清晰的模块边界和命名空间管理", - "简化上层模块对数据集功能的引用路径" - ] - }, - { - "code_dossier": { - "code_purpose": "module", - "description": "运行器模块,包含实验运行器和基准测试运行器的公共接口与实现", - "file_path": "examples/cortex-mem-evaluation/src/runner/mod.rs", - "functions": [], - "importance_score": 0.6, - "interfaces": [], - "name": "mod.rs", - "source_summary": "//! 运行器模块\n//! \n//! 包含实验运行器和基准测试运行器\n\npub mod experiment_runner;\npub mod benchmark_runner;\n\npub use experiment_runner::*;\npub use benchmark_runner::*;" - }, - "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 9, - "number_of_classes": 0, - "number_of_functions": 0 - }, - "dependencies": [], - "detailed_description": "该模块是一个 Rust 项目中的子模块聚合器,位于 cortex-mem-evaluation 示例项目的 runner 目录下。其主要功能是组织并重新导出两个子模块:experiment_runner 和 benchmark_runner。通过 pub mod 声明公共子模块,并使用 pub use 将其内容重新导出,使得外部模块可以统一通过 runner 模块访问这两个运行器的功能,而无需直接引用其内部结构。该模块本身不包含具体实现逻辑,仅承担模块组织和 API 聚合的角色。", - "interfaces": [], - "responsibilities": [ - "聚合 experiment_runner 和 benchmark_runner 两个子模块", - "统一对外暴露运行器相关的公共接口", - "提供清晰的模块边界和命名空间管理", - "简化外部模块对运行器功能的引用路径" - ] - }, - { - "code_dossier": { - "code_purpose": "module", - "description": "Report module that organizes and re-exports components for report generation and visualization.", - "file_path": "examples/cortex-mem-evaluation/src/report/mod.rs", - "functions": [], - "importance_score": 0.6, - "interfaces": [], - "name": "mod.rs", - "source_summary": "//! 报告模块\n//! \n//! 包含报告生成器和可视化工具\n\npub mod generator;\npub mod visualizer;\n\npub use generator::*;\npub use visualizer::*;" - }, - "complexity_metrics": { - "cyclomatic_complexity": 1.0, - "lines_of_code": 9, - "number_of_classes": 0, - "number_of_functions": 0 - }, - "dependencies": [], - "detailed_description": "This module serves as a container for the 'report' functionality in the cortex-mem-evaluation project. It defines two submodules: 'generator' and 'visualizer', which are responsible for generating memory evaluation reports and visualizing them, respectively. The module uses 'pub mod' to declare public submodules and re-exports all their contents into the parent scope using 'pub use', enabling convenient access to the submodule APIs without requiring deep path imports. The module itself contains no executable logic, acting purely as an organizational and accessibility layer.", + "detailed_description": "This __init__.py file serves as the entry point module for the LangMem evaluation system. It defines the public API by exporting two core classes: LangMemAdd and LangMemSearch, which are imported from internal submodules 'add' and 'search'. The module's primary purpose is to provide a clean, consolidated interface for external consumers to access these evaluation components without needing to know their internal implementation locations.", "interfaces": [], "responsibilities": [ - "Organizes report-related functionality into a coherent module structure", - "Provides centralized access to report generation capabilities via re-exporting", - "Provides centralized access to report visualization capabilities via re-exporting", - "Defines the public API surface for the report subsystem" + "Export public API classes for external consumption", + "Organize and expose internal evaluation components", + "Define module-level contract via __all__", + "Enable clean import patterns for users", + "Facilitate module-level documentation" ] }, { @@ -18226,19 +15709,24 @@ Code analysis results from preprocessing phase, including definitions of functio ## Memory Storage Statistics -**Total Storage Size**: 1693607 bytes +**Total Storage Size**: 1652867 bytes -- **preprocess**: 1533048 bytes (90.5%) -- **timing**: 36 bytes (0.0%) -- **documentation**: 99706 bytes (5.9%) -- **studies_research**: 60817 bytes (3.6%) +- **timing**: 37 bytes (0.0%) +- **preprocess**: 1391134 bytes (84.2%) +- **studies_research**: 95646 bytes (5.8%) +- **documentation**: 166050 bytes (10.0%) ## Generated Documents Statistics -Number of Generated Documents: 5 +Number of Generated Documents: 10 -- Boundary Interfaces -- Architecture Description - Core Workflows - Key Modules and Components Research Report_Memory Management Domain +- Boundary Interfaces - Project Overview +- Key Modules and Components Research Report_Access Interface Domain +- Key Modules and Components Research Report_Memory Optimization Domain +- Key Modules and Components Research Report_Configuration Management Domain +- Key Modules and Components Research Report_Storage Integration Domain +- Architecture Description +- Key Modules and Components Research Report_LLM Integration Domain From e3b3b3d452793e2bb3ace20b26b1d44c4d00009c Mon Sep 17 00:00:00 2001 From: Sopaco Date: Wed, 31 Dec 2025 12:51:22 +0800 Subject: [PATCH 2/3] ``` Update README badges and improve Qdrant vector store payload handling - Replace crates.io badge with Litho docs and benchmark badges - Change entity and topic storage to use Qdrant ListValue format - Update filtering to use Keyword matching instead of nested filters - Add backward compatibility for JSON string format in payload retrieval ``` --- README.md | 6 +- cortex-mem-core/src/vector_store/qdrant.rs | 89 +++++++++++++--------- 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index d46a59b..788b359 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,11 @@

Built with Rust, Cortex Memory gives your AI agents a high-performance, persistent, and intelligent long-term memory.

- + Litho Docs + + Benchmark GitHub Actions Workflow Status - + MIT


diff --git a/cortex-mem-core/src/vector_store/qdrant.rs b/cortex-mem-core/src/vector_store/qdrant.rs index 30e6ff6..311baa1 100644 --- a/cortex-mem-core/src/vector_store/qdrant.rs +++ b/cortex-mem-core/src/vector_store/qdrant.rs @@ -202,14 +202,29 @@ impl QdrantVectorStore { // Store entities and topics as arrays if !memory.metadata.entities.is_empty() { - let entities_json = - serde_json::to_string(&memory.metadata.entities).unwrap_or_default(); - payload.insert("entities".to_string(), entities_json.into()); + let entities_values: Vec = + memory.metadata.entities.iter() + .map(|entity| entity.to_string().into()) + .collect(); + payload.insert("entities".to_string(), qdrant_client::qdrant::Value { + kind: Some(qdrant_client::qdrant::value::Kind::ListValue( + qdrant_client::qdrant::ListValue { + values: entities_values, + })), + }); } if !memory.metadata.topics.is_empty() { - let topics_json = serde_json::to_string(&memory.metadata.topics).unwrap_or_default(); - payload.insert("topics".to_string(), topics_json.into()); + let topics_values: Vec = + memory.metadata.topics.iter() + .map(|topic| topic.to_string().into()) + .collect(); + payload.insert("topics".to_string(), qdrant_client::qdrant::Value { + kind: Some(qdrant_client::qdrant::value::Kind::ListValue( + qdrant_client::qdrant::ListValue { + values: topics_values, + })), + }); } // Custom metadata @@ -278,25 +293,15 @@ impl QdrantVectorStore { // Filter by topics - check if any of the requested topics are present if let Some(topics) = &filters.topics { if !topics.is_empty() { - let topic_conditions: Vec = topics - .iter() - .map(|topic| Condition { + for topic in topics { + conditions.push(Condition { condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition { key: "topics".to_string(), r#match: Some(Match { - match_value: Some(r#match::MatchValue::Text(topic.clone())), + match_value: Some(r#match::MatchValue::Keyword(topic.clone())), }), ..Default::default() })), - }) - .collect(); - - if !topic_conditions.is_empty() { - conditions.push(Condition { - condition_one_of: Some(condition::ConditionOneOf::Filter(Filter { - should: topic_conditions, - ..Default::default() - })), }); } } @@ -305,25 +310,15 @@ impl QdrantVectorStore { // Filter by entities - check if any of the requested entities are present if let Some(entities) = &filters.entities { if !entities.is_empty() { - let entity_conditions: Vec = entities - .iter() - .map(|entity| Condition { + for entity in entities { + conditions.push(Condition { condition_one_of: Some(condition::ConditionOneOf::Field(FieldCondition { key: "entities".to_string(), r#match: Some(Match { - match_value: Some(r#match::MatchValue::Text(entity.clone())), + match_value: Some(r#match::MatchValue::Keyword(entity.clone())), }), ..Default::default() })), - }) - .collect(); - - if !entity_conditions.is_empty() { - conditions.push(Condition { - condition_one_of: Some(condition::ConditionOneOf::Filter(Filter { - should: entity_conditions, - ..Default::default() - })), }); } } @@ -548,22 +543,46 @@ impl QdrantVectorStore { entities: payload .get("entities") .and_then(|v| match v { + qdrant_client::qdrant::Value { + kind: Some(qdrant_client::qdrant::value::Kind::ListValue(list)), + } => { + Some(list.values.iter().filter_map(|val| match val { + qdrant_client::qdrant::Value { + kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)), + } => Some(s.clone()), + _ => None, + }).collect::>()) + }, qdrant_client::qdrant::Value { kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)), - } => Some(s.as_str()), + } => { + // Backward compatibility: parse JSON string format + serde_json::from_str(s).ok() + }, _ => None, }) - .and_then(|s| serde_json::from_str(s).ok()) .unwrap_or_default(), topics: payload .get("topics") .and_then(|v| match v { + qdrant_client::qdrant::Value { + kind: Some(qdrant_client::qdrant::value::Kind::ListValue(list)), + } => { + Some(list.values.iter().filter_map(|val| match val { + qdrant_client::qdrant::Value { + kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)), + } => Some(s.clone()), + _ => None, + }).collect::>()) + }, qdrant_client::qdrant::Value { kind: Some(qdrant_client::qdrant::value::Kind::StringValue(s)), - } => Some(s.as_str()), + } => { + // Backward compatibility: parse JSON string format + serde_json::from_str(s).ok() + }, _ => None, }) - .and_then(|s| serde_json::from_str(s).ok()) .unwrap_or_default(), custom, }; From b858cf8ccbdd782b25ee57d84affc72450d80df2 Mon Sep 17 00:00:00 2001 From: Sopaco Date: Wed, 31 Dec 2025 13:15:23 +0800 Subject: [PATCH 3/3] ``` Update report styling to dark theme with improved layout - Changed background to dark blue (#1a1a2e) with white cards - Added shadow effects and refined spacing for better visual hierarchy - Updated typography with improved contrast and sizing - Converted summary section to responsive grid cards - Added mobile responsive --- examples/lomoco-evaluation/generate_report.py | 448 ++++++++++-------- 1 file changed, 243 insertions(+), 205 deletions(-) diff --git a/examples/lomoco-evaluation/generate_report.py b/examples/lomoco-evaluation/generate_report.py index 2b5b450..fcdf3fc 100644 --- a/examples/lomoco-evaluation/generate_report.py +++ b/examples/lomoco-evaluation/generate_report.py @@ -142,215 +142,266 @@ def generate_html(results_file, output_file="report.html"): }} body {{ - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - line-height: 1.6; - color: #333; - background: #f5f5f5; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + background: #1a1a2e; padding: 20px; + min-height: 100vh; }} .container {{ max-width: 1400px; margin: 0 auto; + background: white; + border-radius: 2px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + overflow: hidden; }} .header {{ - text-align: center; - margin-bottom: 40px; - padding: 30px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 10px; + background: #1e3a5f; color: white; + padding: 20px 30px; + text-align: center; }} .header h1 {{ - margin: 0 0 10px 0; - font-size: 2.5em; + font-size: 1.8em; + margin-bottom: 5px; + font-weight: 700; }} - .header p {{ - margin: 10px 0 0 0; - font-size: 1.1em; + .header .subtitle {{ + font-size: 0.9em; opacity: 0.9; }} - .section {{ + .header .date {{ + margin-top: 8px; + font-size: 0.8em; + opacity: 0.8; + }} + + .summary {{ + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 15px; + padding: 25px; + background: #f8fafc; + }} + + .summary-card {{ background: white; - border-radius: 10px; - padding: 30px; - margin-bottom: 30px; - box-shadow: 0 2px 8px rgba(0,0,0,0.1); + padding: 15px; + border-radius: 2px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + text-align: center; + transition: transform 0.2s ease; }} - .section-title {{ + .summary-card:hover {{ + transform: translateY(-1px); + }} + + .summary-card h3 {{ + color: #4a5568; + font-size: 0.8em; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 8px; + }} + + .summary-card .value {{ font-size: 1.8em; - color: #2c3e50; - margin-bottom: 20px; - padding-bottom: 10px; - border-bottom: 2px solid #e9ecef; + font-weight: 700; + color: #1e3a5f; + margin-bottom: 3px; + }} + + .summary-card .label {{ + color: #94a3b8; + font-size: 0.75em; + }} + + .content {{ + padding: 25px; + }} + + .section {{ + margin-bottom: 30px; + }} + + .section h2 {{ + color: #1e293b; + font-size: 1.4em; + margin-bottom: 15px; + padding-bottom: 8px; + border-bottom: 2px solid #1e3a5f; }} .card-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 20px; - margin-bottom: 30px; + gap: 15px; + margin-bottom: 25px; }} .card {{ - background: #f8f9fa; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 20px; - transition: box-shadow 0.3s; + background: white; + padding: 15px; + border-radius: 2px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + border-left: 3px solid #1e3a5f; }} .card:hover {{ - box-shadow: 0 4px 12px rgba(0,0,0,0.15); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12); }} .metric-name {{ - font-size: 1.3em; + font-size: 1em; font-weight: 600; - color: #495057; + color: #1e293b; margin-bottom: 10px; }} .metric-value {{ - font-size: 2.5em; + font-size: 2em; font-weight: 700; - color: #2c3e50; - margin: 15px 0; + color: #1e3a5f; + margin: 10px 0; }} .metric-details {{ - color: #6c757d; - font-size: 0.95em; + color: #64748b; + font-size: 0.85em; line-height: 1.5; }} - .badge {{ - display: inline-block; - padding: 4px 12px; - border-radius: 4px; - font-size: 0.9em; - font-weight: 500; - margin-bottom: 10px; + .metric-details > div {{ + margin-bottom: 4px; }} - .badge.success {{ - background: #d4edda; - color: #155724; - }} - - .badge.info {{ - background: #d1ecf1; - color: #0c5460; + .rating-badge {{ + display: inline-block; + padding: 4px 10px; + border-radius: 2px; + font-size: 0.8em; + font-weight: 600; + color: white; + margin-top: 10px; }} .table {{ width: 100%; border-collapse: collapse; - margin: 20px 0; background: white; - border-radius: 8px; + border-radius: 2px; overflow: hidden; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); }} .table thead {{ - background: #f8f9fa; + background: #1e3a5f; + color: white; }} .table th {{ - padding: 15px; + padding: 10px; text-align: left; font-weight: 600; - color: #495057; - border-bottom: 2px solid #dee2e6; + font-size: 0.85em; }} .table td {{ - padding: 12px 15px; - text-align: left; - border-bottom: 1px solid #e9ecef; - }} - - .table tbody tr:last-child td {{ - border-bottom: none; + padding: 10px; + border-bottom: 1px solid #e2e8f0; + font-size: 0.9em; }} .table tbody tr:hover {{ - background: #f1f5f9; - }} - - .bar-container {{ - margin: 10px 0; - background: #f1f5f9; - border-radius: 4px; - padding: 3px; - }} - - .bar {{ - height: 24px; - border-radius: 4px; - transition: width 0.3s; + background: #f8fafc; }} .info-grid {{ display: grid; - grid-template-columns: 1fr 1fr 1fr; - gap: 15px; + grid-template-columns: 1fr 1fr; + gap: 10px; margin-top: 10px; }} .info-item {{ - padding: 10px; - background: #f8f9fa; - border-radius: 4px; + padding: 8px 10px; + background: #f8fafc; + border-radius: 2px; }} .info-label {{ - font-size: 0.85em; - color: #6c757d; + font-size: 0.75em; + color: #64748b; font-weight: 500; }} .info-value {{ - font-size: 1.1em; + font-size: 1em; font-weight: 600; - color: #495057; + color: #1e3a5f; }} .legend {{ - background: #fff3cd; - border: 1px solid #d4edda; - border-radius: 6px; + background: #f0f4f8; padding: 15px; + border-radius: 2px; margin-top: 20px; - font-size: 0.9em; + border-left: 3px solid #1e3a5f; + font-size: 0.85em; + }} + + .legend h3 {{ + color: #1e3a5f; + margin-bottom: 12px; + font-size: 1em; }} .legend-item {{ display: flex; align-items: center; - margin-bottom: 8px; + margin-bottom: 6px; }} .legend-color {{ - width: 20px; - height: 20px; - border-radius: 4px; - margin-right: 10px; + width: 16px; + height: 16px; + border-radius: 2px; + margin-right: 8px; flex-shrink: 0; }} .footer {{ text-align: center; - margin-top: 40px; - padding-top: 20px; - border-top: 1px solid #e9ecef; - color: #6c757d; - font-size: 0.9em; + padding: 15px; + color: #64748b; + font-size: 0.8em; + background: #f8fafc; + border-top: 1px solid #e2e8f0; + }} + + @media (max-width: 768px) {{ + .summary {{ + grid-template-columns: 1fr; + }} + + .card-grid {{ + grid-template-columns: 1fr; + }} + + .table {{ + font-size: 0.85em; + }} + + .table th, + .table td {{ + padding: 8px; + }} }} @@ -358,18 +409,12 @@ def generate_html(results_file, output_file="report.html"):

📊 {system_name} 评估报告

-

生成时间: {datetime.now().strftime("%Y年%m月%d日 %H:%M")}

-
- 数据集 - 150 个问题 -
+

记忆系统性能评估报告

+

生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}

-
-

📈 总体指标概览

- -
+
""" # 生成核心指标卡片 @@ -385,43 +430,36 @@ def generate_html(results_file, output_file="report.html"): if metric_key in overall: metric_data = overall[metric_key] html_content += f""" -
-
{label}
-
{format_value(metric_data["mean"], 3)}
-
-
标准差: ±{format_value(metric_data["std"], 3)}
-
中位数: {format_value(metric_data["median"], 3)}
-
样本数: {metric_data["count"]}
-
- 评级: {get_rating_label(metric_data["mean"])} -
-
-
+
+

{label}

+
{format_value(metric_data["mean"], 3)}
+
标准差: ±{format_value(metric_data["std"], 3)}
+
""" html_content += """ -
""" # 指标对比表格 html_content += """ -
-

📊 指标对比表格

- - - - - - - - - - - - - - +
+
+

📊 指标对比表格

+ +
指标名称类别均值标准差95% 置信区间样本数评级
+ + + + + + + + + + + + """ for metric_key in sorted(overall.keys()): @@ -433,29 +471,29 @@ def generate_html(results_file, output_file="report.html"): ci_low, ci_high = metric_data["confidence_interval_95"] html_content += f""" - - - - - - - - - + + + + + + + + + """ html_content += """ - -
指标名称类别均值标准差95% 置信区间样本数评级
{info["name"]}{info["category"]}{format_value(metric_data["mean"], 4)}{format_value(metric_data["std"], 4)}{format_value(ci_low, 4)} - {format_value(ci_high, 4)}{metric_data["count"]}{rating}
{info["name"]}{info["category"]}{format_value(metric_data["mean"], 4)}{format_value(metric_data["std"], 4)}{format_value(ci_low, 4)} - {format_value(ci_high, 4)}{metric_data["count"]}{rating}
-
+ + +
""" # 按分类别的指标 html_content += """ -
-

📂 分类指标详情

+
+

📂 分类指标详情

-
+
""" category_names = { @@ -468,78 +506,78 @@ def generate_html(results_file, output_file="report.html"): if cat_key in categories: cat_data = categories[cat_key] html_content += f""" -
-

{cat_name}

-
-
-
问题数量
-
{cat_data.get("recall_at_1", {}).get("count", 0)}
-
-
-
Recall@1
-
{format_value(cat_data.get("recall_at_1", {}).get("mean", 0), 3)}
-
-
-
Precision@1
-
{format_value(cat_data.get("precision_at_1", {}).get("mean", 0), 3)}
-
-
-
MRR
-
{format_value(cat_data.get("mrr", {}).get("mean", 0), 3)}
-
-
-
语义相似度
-
{format_value(cat_data.get("answer_semantic_similarity", {}).get("mean", 0), 3)}
+
+

{cat_name}

+
+
+
问题数量
+
{cat_data.get("recall_at_1", {}).get("count", 0)}
+
+
+
Recall@1
+
{format_value(cat_data.get("recall_at_1", {}).get("mean", 0), 3)}
+
+
+
Precision@1
+
{format_value(cat_data.get("precision_at_1", {}).get("mean", 0), 3)}
+
+
+
MRR
+
{format_value(cat_data.get("mrr", {}).get("mean", 0), 3)}
+
+
+
语义相似度
+
{format_value(cat_data.get("answer_semantic_similarity", {}).get("mean", 0), 3)}
+
-
""" html_content += """ +
-
""" # 指标说明 html_content += """ -
-

📖 指标定义和说明

+
+

📖 指标定义和说明

-
+
""" for metric_key, info in metrics_info.items(): if metric_key in overall: html_content += f""" -
-
{info["name"]}
-
-
类别: {info["category"]}
-
说明: {info["description"]}
+
+
{info["name"]}
+
+
类别: {info["category"]}
+
说明: {info["description"]}
+
-
""" html_content += """ +
-
-
-

📊 评级说明

-
-
- 优秀 (≥ 0.90) -
-
-
- 良好 (0.70 - 0.89) -
-
-
- 需要改进 (< 0.70) +
+

📊 评级说明

+
+
+ 优秀 (≥ 0.90) +
+
+
+ 良好 (0.70 - 0.89) +
+
+
+ 需要改进 (< 0.70) +
-
""" # 页脚