From 8de517d6b764a3cc160ff10876562f8f94b1261e Mon Sep 17 00:00:00 2001 From: esafwan Date: Sat, 28 Mar 2026 11:04:29 +0800 Subject: [PATCH 1/2] docs: add HUF Memory System design documents - PRD: Programmable Memory & Learning Layer - DocType designs: Memory Record, Policy, Profile - Tech specs: Capture/Retrieval, Storage Architecture - Profiles: Travel Planning, Programming, Documentation - Implementation plan and project tracking --- IMPLEMENTATION_PLAN.md | 352 +++++++++ PRD.md | 577 ++++++++++++++ PROJECT_TRACKING.md | 244 ++++++ doctype_designs/memory_policy.json | 244 ++++++ doctype_designs/memory_profile.json | 179 +++++ doctype_designs/memory_record.json | 245 ++++++ profiles/documentation/README.md | 227 ++++++ profiles/documentation/capture_prompt.txt | 126 +++ profiles/documentation/example_records.json | 157 ++++ profiles/documentation/profile.json | 147 ++++ profiles/programming/README.md | 273 +++++++ profiles/programming/capture_prompt.txt | 120 +++ profiles/programming/example_records.json | 73 ++ profiles/programming/profile.json | 186 +++++ profiles/travel_planning/README.md | 146 ++++ profiles/travel_planning/capture-prompt.md | 121 +++ profiles/travel_planning/capture_prompt.txt | 55 ++ profiles/travel_planning/example_records.json | 226 ++++++ profiles/travel_planning/examples.json | 180 +++++ profiles/travel_planning/profile.json | 227 ++++++ tech_specs/CAPTURE_RETRIEVAL.md | 729 ++++++++++++++++++ tech_specs/STORAGE_ARCHITECTURE.md | 425 ++++++++++ 22 files changed, 5259 insertions(+) create mode 100644 IMPLEMENTATION_PLAN.md create mode 100644 PRD.md create mode 100644 PROJECT_TRACKING.md create mode 100644 doctype_designs/memory_policy.json create mode 100644 doctype_designs/memory_profile.json create mode 100644 doctype_designs/memory_record.json create mode 100644 profiles/documentation/README.md create mode 100644 profiles/documentation/capture_prompt.txt create mode 100644 profiles/documentation/example_records.json create mode 100644 profiles/documentation/profile.json create mode 100644 profiles/programming/README.md create mode 100644 profiles/programming/capture_prompt.txt create mode 100644 profiles/programming/example_records.json create mode 100644 profiles/programming/profile.json create mode 100644 profiles/travel_planning/README.md create mode 100644 profiles/travel_planning/capture-prompt.md create mode 100644 profiles/travel_planning/capture_prompt.txt create mode 100644 profiles/travel_planning/example_records.json create mode 100644 profiles/travel_planning/examples.json create mode 100644 profiles/travel_planning/profile.json create mode 100644 tech_specs/CAPTURE_RETRIEVAL.md create mode 100644 tech_specs/STORAGE_ARCHITECTURE.md diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md new file mode 100644 index 00000000..41227eb6 --- /dev/null +++ b/IMPLEMENTATION_PLAN.md @@ -0,0 +1,352 @@ +# HUF Memory Layer - Implementation Plan + +## Overview +Implementation of the HUF-native memory and learning subsystem, elevating "data management" into a first-class Agent Memory layer with portable records, flexible scopes, and opinionated profiles. + +--- + +## 1. Phase 1 (MVP) Tasks with Dependencies + +### Task Group A: Foundation (Week 1) +| Task | Description | Dependencies | Complexity | +|------|-------------|--------------|------------| +| A1 | Create Memory Record DocType | None | Medium | +| A2 | Create Memory Policy DocType | None | Medium | +| A3 | Create Memory Profile DocType | None | Low | +| A4 | Add memory section to Agent DocType | A2, A3 | Medium | + +### Task Group B: Conversation & Run Integration (Week 1-2) +| Task | Description | Dependencies | Complexity | +|------|-------------|--------------|------------| +| B1 | Add memory fields to Agent Conversation | A1 | Low | +| B2 | Add memory observability to Agent Run | A1 | Low | +| B3 | Rename "data management" to "Memory" in UI | None | Trivial | + +### Task Group C: Capture Infrastructure (Week 2-3) +| Task | Description | Dependencies | Complexity | +|------|-------------|--------------|------------| +| C1 | Implement in-prompt capture mode | A1, A4 | Medium | +| C2 | Implement post-run async capture | A1, A4, B2 | High | +| C3 | Implement specialized memory agent support | A2, A4 | Medium | +| C4 | Implement rule-only capture mode | A1 | Low | + +### Task Group D: Storage & Indexing (Week 3) +| Task | Description | Dependencies | Complexity | +|------|-------------|--------------|------------| +| D1 | Implement canonical storage in Memory Record | A1 | Low | +| D2 | Optional FTS indexing pipeline | A1 | Medium | +| D3 | Optional vector indexing pipeline | A1 | Medium | +| D4 | Index backend abstraction layer | D2, D3 | Medium | + +### Task Group E: Retrieval & Injection (Week 3-4) +| Task | Description | Dependencies | Complexity | +|------|-------------|--------------|------------| +| E1 | Memory retrieval service (scope-filtered) | A1 | Medium | +| E2 | Prompt injection integration | E1 | Medium | +| E3 | Memory search tool | E1 | Low | +| E4 | Memory write tool | A1 | Low | + +### Task Group F: Profiles & UI (Week 4) +| Task | Description | Dependencies | Complexity | +|------|-------------|--------------|------------| +| F1 | Create 5 opinionated profiles | A3 | Medium | +| F2 | Memory Explorer desk page | A1 | Low | +| F3 | Agent Memory tab UI | A4 | Low | +| F4 | Conversation memory inspector | B1 | Low | + +### Dependency Graph +``` +A1, A2, A3 (foundation) + ↓ +A4 (Agent integration) ← A2, A3 + ↓ +B1, B2 (conversation/run) ← A1 + ↓ +C1-C4 (capture) ← A1, A4, B2 + ↓ +D1-D4 (storage/indexing) ← A1 + ↓ +E1-E4 (retrieval) ← A1, D1-D4 + ↓ +F1-F4 (profiles/UI) ← A3, E1-E4 +``` + +--- + +## 2. New DocTypes to Create + +### 2.1 Memory Record +**Purpose:** Canonical portable unit of memory - replaces conversation-embedded JSON + +**Core Fields:** +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| title | Data | Yes | Human-readable memory title | +| agent | Link (Agent) | Yes | Source agent | +| conversation | Link (Agent Conversation) | No | Source conversation | +| run | Link (Agent Run) | No | Source run | +| source_type | Select | Yes | conversation, run, manual, event, scheduled, imported | +| producer_mode | Select | Yes | main_agent, memory_agent, post_run_llm, rules_only, manual | +| memory_type | Select | Yes | profile, session_state, preference, fact, plan, observation, insight, domain_object, custom | +| schema_name | Data | No | Name of schema used | +| profile_name | Link (Memory Profile) | No | Profile used for capture | +| data_json | JSON | Yes | Structured payload | +| summary_text | Text | No | Human summary | +| raw_context_excerpt | Text | No | Original context snippet | +| scope_type | Select | Yes | conversation, user, agent, namespace, global | +| scope_key | Data | Yes | Scoped identifier | +| visibility | Select | Yes | private, shared_with_agent, shared_with_namespace, global | +| status | Select | Yes | active, superseded, archived, expired, error | +| confidence | Float | No | 0.0-1.0 confidence score | +| importance_score | Float | No | 0.0-1.0 importance | +| ttl_days | Int | No | Time-to-live in days | +| effective_from | DateTime | No | Validity start | +| effective_until | DateTime | No | Validity end | +| supersedes_memory_record | Link (Memory Record) | No | Previous version | +| created_from_turn_count | Int | No | Conversation turn when captured | +| tags | Table | No | Tags for categorization | +| metadata_json | JSON | No | Additional metadata | +| fts_indexed | Check | No | FTS index status | +| vector_indexed | Check | No | Vector index status | +| index_backend | Select | No | none, sqlite_fts, sqlite_vec, pgvector, custom | +| last_indexed_at | DateTime | No | Last index timestamp | +| last_retrieved_at | DateTime | No | Last access timestamp | +| retrieval_count | Int | No | Number of retrievals | + +**Estimated Complexity: 3-4 days** + +--- + +### 2.2 Memory Policy +**Purpose:** Defines how an agent performs memory capture + +**Core Fields:** +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| policy_name | Data | Yes | Unique name | +| enabled | Check | Yes | Active flag | +| agent | Link (Agent) | No | Linked agent | +| memory_profile | Link (Memory Profile) | No | Default profile | +| capture_owner | Select | Yes | main_agent, memory_agent, post_run_llm, rules_only | +| memory_agent | Link (Agent) | No | Specialized agent for capture | +| capture_stage | Select | Yes | in_prompt, post_response_sync, post_response_async, conversation_end, scheduled | +| capture_frequency_type | Select | Yes | every_run, every_n_runs, every_n_turns, conversation_end, manual, scheduled | +| capture_frequency_value | Int | No | N value for frequency | +| conversation_end_strategy | Select | No | manual_close, idle_timeout, heuristic, never | +| idle_timeout_minutes | Int | No | Auto-close after idle | +| capture_prompt | Text | No | Prompt for extraction | +| capture_schema_json | JSON | No | Expected schema | +| allow_open_schema | Check | No | Allow flexible schema | +| require_json_schema_match | Check | No | Enforce schema strictness | +| allow_update_existing | Check | No | Enable updates | +| allow_merge | Check | No | Enable merging | +| allow_append | Check | No | Enable appending | +| min_confidence | Float | No | Minimum confidence threshold | +| store_raw_payload | Check | No | Store raw input | +| store_summary | Check | No | Generate summary | +| enable_fts_index | Check | No | Enable FTS | +| enable_vector_index | Check | No | Enable vector index | +| vector_backend | Select | No | sqlite_vec, pgvector, custom | +| fts_backend | Select | No | sqlite_fts, custom | +| retrieval_mode_default | Select | Yes | inject, tool_only, hybrid | +| max_items_to_inject | Int | No | Injection limit | +| max_tokens_to_inject | Int | No | Token budget | + +**Estimated Complexity: 2-3 days** + +--- + +### 2.3 Memory Profile +**Purpose:** Opinionated presets for common domains + +**Core Fields:** +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| profile_name | Data | Yes | Unique name | +| description | Text | No | Profile description | +| category | Select | Yes | programming, science, language, reasoning, general, travel, crm, support, documentation, custom | +| default_schema_json | JSON | Yes | Default capture schema | +| default_capture_prompt | Text | Yes | Extraction prompt template | +| recommended_model | Data | No | Suggested model | +| recommended_provider | Data | No | Suggested provider | +| default_capture_stage | Select | No | Default timing | +| default_frequency | Select | No | Default frequency | +| default_scope_type | Select | No | Default scope | +| default_indexing_mode | Select | No | fts, vector, both, none | +| default_retrieval_mode | Select | No | inject, tool_only, hybrid | +| default_memory_type_mapping | JSON | No | Type mapping rules | +| icon | Attach Image | No | Profile icon | +| is_system_profile | Check | Yes | Built-in flag | + +**Shipped Profiles (MVP):** +1. **Programming Memory** - code patterns, conventions, debugging context +2. **General Knowledge Memory** - facts, preferences, reusable info +3. **Travel Planning Memory** - destinations, dates, preferences, constraints +4. **CRM Memory** - customer context, history, preferences +5. **Documentation Memory** - requirements, decisions, API contracts + +**Estimated Complexity: 2 days** + +--- + +## 3. Modifications to Existing DocTypes + +### 3.1 Agent (New Memory Section) +**Estimated Complexity: 2 days** + +| Field | Type | Description | +|-------|------|-------------| +| enable_memory | Check | Master toggle | +| memory_policy | Link (Memory Policy) | Policy reference | +| default_memory_scope_type | Select | conversation, user, agent, namespace, global | +| default_memory_scope_key_template | Data | Jinja template for scope key | +| memory_retrieval_mode | Select | inject, tool_only, hybrid | +| memory_in_prompt_budget | Int | Token budget for injection | +| enable_memory_search_tool | Check | Add search tool | +| enable_memory_write_tool | Check | Add write tool | +| memory_profile | Link (Memory Profile) | Default profile | +| memory_agent | Link (Agent) | Specialized agent | +| memory_run_order | Select | before_main_response, after_main_response, background | +| memory_max_items | Int | Max memories per query | +| memory_index_backend_default | Select | none, sqlite_fts, sqlite_vec, pgvector | +| memory_visibility_default | Select | private, shared_with_agent, shared_with_namespace, global | + +### 3.2 Agent Conversation +**Estimated Complexity: 1 day** + +| Field | Type | Description | +|-------|------|-------------| +| memory_scope_override | Select | Override default scope | +| memory_scope_key_override | Data | Override scope key | +| memory_capture_enabled_override | Check | Force enable/disable | +| memory_turn_count | Int | Turns since last capture | +| memory_last_capture_at | DateTime | Last capture timestamp | +| conversation_end_state | Select | open, closing, closed | +| ended_at | DateTime | Close timestamp | +| idle_expires_at | DateTime | Idle timeout | + +### 3.3 Agent Run +**Estimated Complexity: 1 day** + +| Field | Type | Description | +|-------|------|-------------| +| memory_capture_triggered | Check | Capture attempted | +| memory_capture_mode | Select | Mode used | +| memory_records_created | Int | Records created | +| memory_records_updated | Int | Records updated | +| memory_records_skipped | Int | Records skipped | +| memory_index_jobs_started | Int | Index jobs queued | +| memory_capture_latency_ms | Int | Capture time | +| memory_capture_cost | Float | Estimated cost | +| memory_error_log | Text | Error details | + +--- + +## 4. File Changes Required + +### New Files +``` +huf_agent_memory/ +├── memory_record/ +│ ├── memory_record.json # DocType definition +│ ├── memory_record.py # Server controller +│ └── test_memory_record.py # Unit tests +├── memory_policy/ +│ ├── memory_policy.json # DocType definition +│ ├── memory_policy.py # Server controller +│ └── test_memory_policy.py +├── memory_profile/ +│ ├── memory_profile.json # DocType definition +│ ├── memory_profile.py # Server controller +│ ├── default_profiles.py # Shipped profiles +│ └── test_memory_profile.py +├── capture/ +│ ├── capture_service.py # Core capture orchestrator +│ ├── in_prompt_capture.py # In-prompt mode +│ ├── post_run_capture.py # Async capture worker +│ ├── memory_agent_capture.py # Specialized agent mode +│ └── rule_capture.py # Rule-only mode +├── storage/ +│ ├── storage_service.py # Canonical storage +│ ├── fts_indexer.py # FTS indexing +│ ├── vector_indexer.py # Vector indexing +│ └── index_backend.py # Backend abstraction +├── retrieval/ +│ ├── retrieval_service.py # Memory retrieval +│ ├── prompt_injector.py # Prompt injection +│ ├── memory_search_tool.py # Search tool implementation +│ └── memory_write_tool.py # Write tool implementation +├── scope/ +│ └── scope_resolver.py # Scope/visibility logic +└── utils/ + └── schema_validator.py # JSON schema validation + +huf_agent/doctype/agent/ +├── agent.json # MODIFY - add memory fields +└── agent.py # MODIFY - memory integration + +huf_agent/doctype/agent_conversation/ +├── agent_conversation.json # MODIFY - add memory fields +└── agent_conversation.py # MODIFY - lifecycle hooks + +huf_agent/doctype/agent_run/ +├── agent_run.json # MODIFY - add observability +└── agent_run.py # MODIFY - capture triggers + +huf_agent_memory/www/ +└── memory_explorer.html # Desk page + +huf_agent_memory/public/js/ +├── memory_profile_selector.js # UI component +└── memory_inspector.js # Conversation inspector +``` + +### Modified Files +``` +huf_agent/hooks.py # Register memory signals +huf_agent/patches.txt # Migration patches +huf_agent/doctype/agent/agent.js # Memory tab UI +``` + +--- + +## 5. Estimated Complexity Summary + +### By Component +| Component | Days | Risk Level | +|-----------|------|------------| +| Memory Record DocType | 3 | Low | +| Memory Policy DocType | 2 | Low | +| Memory Profile DocType + 5 profiles | 2 | Low | +| Agent modifications | 2 | Medium | +| Conversation/Run modifications | 1 | Low | +| Capture infrastructure | 4 | Medium | +| Storage & indexing | 3 | Medium | +| Retrieval & injection | 3 | Medium | +| UI components | 2 | Low | +| Testing & integration | 2 | Medium | +| **TOTAL** | **24 days** | **Medium** | + +### Risk Factors +- **Medium:** Post-run async capture requires background worker integration +- **Medium:** Vector indexing dependency on existing HUF vector infrastructure +- **Medium:** Prompt injection may affect token budgeting logic + +### Suggested Team +- 1 Backend Engineer (DocTypes, capture, storage) +- 1 Integration Engineer (retrieval, injection, UI) +- 0.5 QA Engineer (testing, profile validation) + +--- + +## 6. Success Criteria (MVP) + +- [ ] Memory Record created and queryable +- [ ] Memory Policy configures agent behavior +- [ ] 5 profiles shipped and functional +- [ ] In-prompt capture works end-to-end +- [ ] Post-run async capture queues successfully +- [ ] Scope filtering (conversation/user/agent/global) works +- [ ] FTS indexing optional and functional +- [ ] Memory appears in agent prompts when configured +- [ ] "Data management" renamed to "Memory" throughout UI diff --git a/PRD.md b/PRD.md new file mode 100644 index 00000000..b75f2d07 --- /dev/null +++ b/PRD.md @@ -0,0 +1,577 @@ +# HUF PRD — Programmable Memory & Learning Layer + +## 1. Summary + +HUF already has the foundation for a native memory system: agents, conversations, runs, tools, scoped persistence, and a knowledge subsystem with Knowledge Source, Knowledge Input, mandatory prompt injection, and optional knowledge_search over SQLite FTS and vector-capable backends. Agents already persist conversations, can persist per-user history in doc/schedule contexts, and HUF already supports both synchronous and orchestrated execution flows. + +Today's "data management" capability is an optional, prompt-driven structured capture mechanism. It is not automatic, not global, and not yet modeled as a first-class portable knowledge/memory object. The proposed product direction is to evolve this into a first-class, configurable memory/learning layer that remains native to HUF rather than integrating Hindsight first. This is because the HUF design we discussed already covers the core paradigm: optional capture, structured extraction, scoped sharing, pluggable storage, and native retrieval. Hindsight still provides a more mature retain/recall/reflect engine, but that is an advanced enhancement, not a prerequisite for HUF's first-party product maturity. + +## 2. Product Goal + +Turn HUF from "agent orchestration + RAG" into a platform where agents can optionally maintain durable, scoped, portable memory and reusable learned knowledge over time, while keeping the system composable, per-agent configurable, and aligned with HUF's existing DocType architecture. HUF should support both open-ended memory capture and opinionated, ready-to-use knowledge/memory profiles for common domains such as programming, science, language, reasoning, and general information. This aligns with the broader pattern seen in Agno's Learning Machine model, where agents use typed stores and configurable learning modes, but HUF should implement this in a more flexible, product-native way. + +## 3. Why This Matters + +A mature agent platform cannot stop at conversation persistence and document retrieval. It needs memory that can capture structured user context, reusable facts, preferences, plans, and shared insights, and make them available selectively across conversation, user, agent, or wider scopes. Agno formalizes this through stores such as User Profile, User Memory, Session Context, Entity Memory, and Learned Knowledge, each with configurable modes such as Always, Agentic, and Propose. HUF does not need to clone that exact shape, but it does need the same paradigm: configurable capture, durable storage, scoped sharing, and retrieval-aware reuse. + +## 4. Current State in HUF + +HUF already provides the structural substrate needed for this initiative. The current system has first-class Agent, Agent Conversation, Agent Message, and Agent Run records, along with configurable agent instructions, tools, provider/model selection, conversation persistence, and per-user history behavior. It also has a knowledge architecture with Knowledge Source, Knowledge Input, Agent Knowledge, an ingestion/indexing pipeline, SQLite FTS retrieval, optional vector backends, and both mandatory and optional knowledge access patterns. HUF also already supports orchestrated multi-step execution and scheduled/event-triggered execution, which gives it natural insertion points for memory capture and post-run learning. + +The important point is that HUF is not starting from zero. The missing piece is not raw storage or retrieval. The missing piece is to elevate "data management" from a prompt-level helper into a productized memory/learning layer with its own lifecycle, scope model, storage policy, triggers, and optional opinionated templates. + +## 5. Current "Data Management" Capability + +Today, data management is an optional capability enabled per agent. When enabled, a tool is injected that allows structured JSON-form capture during the conversation. The behavior is prompt-driven: the main agent prompt can decide what to capture, or a specialized agent can be used to decide extraction structure and semantics. This is already powerful because it supports domain-specific capture such as travel destination, dates, party size, purpose, or budget without forcing one universal schema. + +This means HUF already has the conceptual beginning of "learning," but in its current form it is still tightly coupled to run-time prompting and not yet portable as a reusable knowledge/memory object. It also lacks first-class controls for scope, lifecycle, indexing policy, sharing policy, and reusable domain presets. + +## 6. Product Direction + +HUF should evolve this feature into a first-class configurable subsystem. A better product name than "data management" is needed because the capability is no longer just data capture. Recommended names, in order: + 1. Memory Layer + 2. Learning Layer + 3. Agent Memory + 4. Context Memory + 5. Knowledge Capture + 6. Structured Memory + +Recommended final product name: Agent Memory for the user-facing feature, with Memory Record or Memory Layer as the core technical concept. + +"Data management" understates the feature. The new system is not only about storing JSON. It is about capturing, structuring, scoping, indexing, retrieving, and optionally consolidating memory for agents. + +## 7. Product Principles + +HUF's memory system should follow these principles: + - Memory is optional, not globally forced. + - Memory is agent-configurable, not hidden infrastructure. + - Capture can be performed by the main agent, a specialized memory agent, or a post-run processor. + - Storage is pluggable: raw structured data, SQLite FTS, SQLite vector, and future vector backends. + - Scope is explicitly controlled: conversation, user, agent, group/custom namespace, or global. + - Retrieval is native: memory should participate in prompt building and tool-based search just as knowledge sources do today. + - The system should support both flexible open schema and opinionated presets for faster adoption. + - HUF should build this natively first; Hindsight is not the first integration target unless HUF later wants a more advanced reflect/consolidation engine. Hindsight's differentiators are memory banks, richer extraction of facts/entities/relationships, multi-path retrieval, and dedicated reflect loops. + +## 8. What We Are Building + +We are building a first-class HUF-native memory and learning subsystem that generalizes the current data management feature into a portable, policy-driven layer with: + - optional structured capture + - configurable extraction ownership + - configurable trigger timing + - first-class storage objects + - explicit sharing and visibility scopes + - optional indexing into FTS and/or vector backends + - native retrieval and prompt injection + - opinionated presets with recommended models and extraction styles + - future room for consolidation, pruning, deduplication, and reflection + +## 9. Core Model + +The design centers on one new first-class record type plus supporting config records. + +### 9.1 Primary new DocType: Memory Record + +Recommended technical name: Agent Memory Record or Memory Record + +This is the canonical portable unit of memory. It replaces conversation-embedded JSON as the long-term model. + +Purpose: A structured or semi-structured memory object produced from a conversation, run, event, or post-run process, with configurable scope, storage, and retrieval behavior. + +Key fields: + - title + - agent + - conversation + - run + - source_type (conversation, run, manual, event, scheduled, imported) + - producer_mode (main_agent, memory_agent, post_run_llm, rules_only, manual) + - memory_type (profile, session_state, preference, fact, plan, observation, insight, domain_object, custom) + - schema_name or profile_name + - data_json (structured payload) + - summary_text + - raw_context_excerpt + - scope_type (conversation, user, agent, namespace, global) + - scope_key (e.g. conversation id, user id, agent name, namespace value) + - visibility (private, shared_with_agent, shared_with_namespace, global) + - status (active, superseded, archived, expired, error) + - confidence + - importance_score + - ttl_days + - effective_from + - effective_until + - supersedes_memory_record + - created_from_turn_count + - tags + - metadata_json + - fts_indexed + - vector_indexed + - index_backend (none, sqlite_fts, sqlite_vec, pgvector, custom) + - last_indexed_at + - last_retrieved_at + - retrieval_count + +This one record becomes the portable memory object that can be referenced by agents, searched, indexed, filtered, or reused across scopes. + +### 9.2 Config DocType: Memory Policy + +This defines how a given agent or workflow performs memory capture. + +Key fields: + - policy_name + - enabled + - agent + - memory_profile + - capture_owner (main_agent, memory_agent, post_run_llm, rules_only) + - memory_agent (Link to Agent, optional) + - capture_stage (in_prompt, post_response_sync, post_response_async, conversation_end, scheduled) + - capture_frequency_type (every_run, every_n_runs, every_n_turns, conversation_end, manual, scheduled) + - capture_frequency_value + - conversation_end_strategy (manual_close, idle_timeout, heuristic, never) + - idle_timeout_minutes + - capture_prompt + - capture_schema_json + - allow_open_schema + - require_json_schema_match + - allow_update_existing + - allow_merge + - allow_append + - min_confidence + - store_raw_payload + - store_summary + - enable_fts_index + - enable_vector_index + - vector_backend + - fts_backend + - retrieval_mode_default (inject, tool_only, hybrid) + - max_items_to_inject + - max_tokens_to_inject + +### 9.3 Optional DocType: Memory Profile + +This is the opinionated preset layer. + +Purpose: Provide ready-made capture schema, prompts, storage defaults, and recommended models for common domains. + +Example profiles: + - Programming Memory + - Science/Research Memory + - Language Learning Memory + - Reasoning/Mathematics Memory + - General Knowledge Memory + - Travel Planning Memory + - CRM / Customer Context Memory + - Support Ticket Context Memory + +Key fields: + - profile_name + - description + - category + - default_schema_json + - default_capture_prompt + - recommended_model + - recommended_provider + - default_capture_stage + - default_frequency + - default_scope_type + - default_indexing_mode + - default_retrieval_mode + - default_memory_type_mapping + - icon + - is_system_profile + +This is how HUF becomes easier to use than a purely raw, schema-less system. + +## 10. Changes to Existing DocTypes + +### 10.1 Agent + +Add a new Memory / Learning section to Agent. + +Recommended new fields: + - enable_memory + - memory_policy (Link) + - default_memory_scope_type + - default_memory_scope_key_template + - memory_retrieval_mode (inject, tool_only, hybrid) + - memory_in_prompt_budget + - enable_memory_search_tool + - enable_memory_write_tool + - memory_profile + - memory_agent (optional) + - memory_run_order (before_main_response, after_main_response, background) + - memory_max_items + - memory_index_backend_default + - memory_visibility_default + +This complements the existing agent settings around persistence, tools, and knowledge sources rather than replacing them. Existing agent features such as persist_conversation, persist_user_history, agent tools, and knowledge bindings remain relevant and should work alongside memory. + +### 10.2 Agent Conversation + +Add fields to support memory lifecycle: + - memory_scope_override + - memory_scope_key_override + - memory_capture_enabled_override + - memory_turn_count + - memory_last_capture_at + - conversation_end_state + - ended_at + - idle_expires_at + +This lets conversation state drive memory policies like "capture on end" or "capture every 10 turns." + +### 10.3 Agent Run + +Add observability fields: + - memory_capture_triggered + - memory_capture_mode + - memory_records_created + - memory_records_updated + - memory_records_skipped + - memory_index_jobs_started + - memory_capture_latency_ms + - memory_capture_cost + - memory_error_log + +HUF already tracks run status and token usage; these fields extend that for memory observability. + +### 10.4 Knowledge Source / Agent Knowledge + +No breaking change required, but add support for memory-backed knowledge ingestion or memory-backed retrieval. Existing Knowledge Source already supports indexed containers and Agent Knowledge already defines mandatory versus optional access. The new memory system should be able to optionally emit into a Knowledge Source or appear as a retrievable memory source under the same retrieval framework. + +## 11. Capture Modes + +HUF should support multiple capture modes, inspired by the same problem Agno solves with Always / Agentic / Propose, but implemented in HUF's more configurable style. + +### 11.1 In-prompt capture + +The main agent is instructed during its run to maintain/update structured memory. This is closest to today's model. + +Best for: + - low-latency, simple fields + - short-lived session state + - agent-specific domains where prompt quality is known + +### 11.2 Post-response synchronous capture + +After the user-facing response is generated, capture happens immediately in the same request. This guarantees consistency but adds latency. + +Best for: + - critical profile updates + - highly structured extractions + +### 11.3 Post-response asynchronous capture + +After the main response, a background job or queued hook performs extraction and storage. This avoids user-facing latency and is the recommended default for heavier extraction. + +Best for: + - travel capture + - CRM enrichment + - learned summaries + - long conversations + +### 11.4 Specialized memory agent + +Instead of the main agent prompt deciding what to store, a dedicated memory agent runs with its own prompt, model, and possibly cheaper or more extraction-tuned provider. + +Best for: + - strict schemas + - domain-specific extraction + - cost optimization + - different reasoning style from main agent + +### 11.5 Rule-only capture + +A no-LLM mode for deterministic fields or external system events. + +Best for: + - exact user IDs + - timestamps + - state transitions + - system-generated plans + +## 12. Capture Triggers + +HUF should support first-class trigger controls for memory capture. + +Supported trigger types: + - every run + - every N runs + - every N turns in a conversation + - after a tool call + - after final response only + - when conversation is marked complete + - when idle timeout is reached + - manual capture + - scheduled consolidation + +This aligns well with HUF's existing trigger-oriented architecture, since HUF already supports Agent Trigger for schedule/doc event/webhook/app/manual execution. The same philosophy should be applied to memory capture policy. + +### Conversation-end detection + +Support all of: + - manual close by user/admin + - automatic close after inactivity threshold + - heuristic close based on agent classification + - close-on-workflow-completion + +## 13. Scope and Sharing Model + +This is one of the biggest reasons to move from embedded conversation JSON to a standalone DocType. + +Supported scopes: + - Conversation: memory visible only inside the current conversation + - User: memory visible across the same user's interactions + - Agent: shared across all users of one agent + - Namespace: custom scope shared by a chosen group such as sales_west, project_alpha, or family_trip + - Global: visible to all eligible agents or the full site, subject to permissioning + +This is conceptually similar to Agno's configurable namespaces and Hindsight's memory bank separation, but HUF should implement it in its own DocType-native model. Agno explicitly supports user/global/custom namespace models in some stores, and Hindsight recommends one bank per user or per agent depending on the use case. + +Sharing examples: + - Travel agent shares trip memory only within one conversation + - Visa agent shares customer profile across all that user's sessions + - Programming tutor shares reusable best-practice snippets globally + - Three research agents share one custom namespace for a project + +## 14. Storage Architecture + +The new memory layer should separate canonical storage from indexing. + +### Canonical storage + +All memory lives first in Memory Record as the source of truth. + +### Optional indexing + +Memory may then be indexed into: + - SQLite FTS + - SQLite vector + - future pgvector + - future external vector backend + - hybrid FTS + vector + +The user asked specifically for the ability to store raw structured data in the primary table while optionally pushing to vector and/or FTS. That should be a first-class policy field, not an implementation detail. + +Why this matters: + +This keeps HUF portable and avoids coupling learning to any one backend. The record remains queryable and auditable even if vector indexing is disabled or rebuilt later. + +## 15. Retrieval Model + +HUF already has a strong knowledge retrieval pathway: mandatory context injection and optional knowledge_search tool. The new memory layer should integrate into the same conceptual pipeline. + +### Retrieval options: + - Inject into prompt as relevant memory context + - Tool-only search where the agent explicitly queries memory + - Hybrid, where top-ranked memory is injected and the rest remains searchable + +### Retrieval filters: + - scope + - agent + - user + - namespace + - memory type + - confidence + - recency + - tags + - profile + - source type + +### Retrieval ranking + +Phase 1: + - recency + confidence + scope weighting + backend relevance + +Phase 2: + - per-profile ranking strategy + - explicit importance weighting + - later optional consolidation/reflect + +## 16. Opinionated Profiles + +This is the productization layer. + +Each profile should define: + - schema + - extraction prompt + - recommended model + - capture stage + - storage defaults + - retrieval defaults + - UI labels and help text + +### Example: Programming Memory + +Captures: + - language + - stack + - coding conventions + - architectural decisions + - reusable fix patterns + - debugging context + +Storage: + - structured record + vector + - optional FTS + +Recommended models: + - low-cost extraction model for routine capture + - stronger reasoning model for consolidation + +### Example: Science / Research Memory + +Captures: + - concepts + - claims + - evidence level + - references + - contradictions + - open questions + +Storage: + - structured + vector + - high emphasis on citation metadata + +### Example: Travel Planning Memory + +Captures: + - destination + - dates + - travelers + - budget + - preferences + - purpose + - accommodation constraints + +Storage: + - structured + optional vector + - usually user- or conversation-scoped + +This profile-based approach gives HUF the benefits of Agno's typed stores and custom schemas, but without forcing HUF into a fixed schema catalog. + +## 17. Documentation-Aware Upgrades + +You asked for documentation-oriented behavior and targeted update capability. This should be built into the design. + +Add a documentation-oriented profile: + +Recommended profile name: Documentation Memory or Documentation Capture + +Purpose: +Capture and maintain structured documentation state from conversations and runs. + +Use cases: + - project requirements + - architecture notes + - implementation decisions + - API contracts + - status summaries + - codebase mapping + - domain glossary + +Special behavior: + - targeted update of an existing memory/document record rather than append-only creation + - section-aware updates + - optional merge into Markdown docs + - optional index emission for search + +This allows HUF to evolve data management into not just memory capture, but also structured living documentation. + +## 18. Proposed Lifecycle + +### Phase 1 lifecycle + +Capture → store canonical record → optional index → retrieve + +### Phase 2 lifecycle + +Capture → compare with prior → merge/supersede → store → optional index → retrieve + +### Phase 3 lifecycle + +Capture → consolidate → deduplicate → decay/archive → retrieve with profile-aware ranking + +This is the area where Hindsight remains ahead today. Hindsight's retain/recall/reflect model gives structured extraction, bank isolation, and an explicit reflect loop. HUF should not integrate Hindsight first, but should leave room for future optional Hindsight-style consolidation if later needed. + +## 19. Recommended MVP Scope + +### Build now: + - rename and elevate data management + - add Memory Record + - add Memory Policy + - add Memory Profile + - add agent-level memory settings + - add conversation/run observability + - support conversation/user/agent/global/custom scope + - support canonical structured storage + - support optional FTS and vector indexing + - support in-prompt and post-run async capture + - support specialized memory agent + - ship 3–5 opinionated profiles + +### Do later: + - consolidation engine + - deduplication + - expiry/pruning + - confidence re-ranking + - "reflect" style synthesis + - memory health dashboards + - cross-memory graphing + +## 20. UI / UX Changes + +### Agent form + +Add a new tab or section: Memory + +Controls: + - enable memory + - choose memory policy + - choose memory profile + - choose capture owner + - choose capture stage + - choose frequency + - choose scope defaults + - choose storage backends + - choose retrieval mode + - set budgets and limits + +### Conversation UI + +Add: + - mark conversation ended + - inspect memory produced + - force capture now + - see scoped memory attached to conversation + +### Memory Explorer + +A new desk page or DocType list view to browse and filter memory records by: + - scope + - agent + - user + - type + - profile + - indexed status + - tags + - confidence + +## 21. Permission and Safety Considerations + +Memory introduces privacy and leakage risks. HUF must treat scope and visibility as permission-bearing fields. This is especially important if a record is shared across users, agents, or globally. Existing HUF security patterns such as Password fields for secrets, careful tool permissioning, and explicit agent/document access patterns should be extended to memory records. + +Controls needed: + - explicit sharing flags + - permission checks for cross-user or global retrieval + - audit trail of producer and updater + - disable indexing for sensitive records + - ability to delete or expire user-scoped memory + +## 22. Why Not Hindsight First + +If HUF implements the above, it already owns the essential learning paradigm natively: capture, scope, storage, search, and retrieval. Hindsight still gives richer extraction of facts/entities/relationships, memory-bank semantics, and a dedicated reflect loop, but those are advanced accelerators, not blockers. Hindsight is best considered later if HUF wants to buy speed on higher-order consolidation or more advanced memory reasoning. + +## 23. Final Recommendation + +Build this natively in HUF first. + +The current HUF architecture already supports the underlying primitives: first-class agents, runs, conversations, optional knowledge, retrieval, triggers, and scoped persistence. The right next move is to turn today's optional "data management" into a first-class Agent Memory subsystem with portable records, flexible scopes, pluggable indexing, configurable capture stages, and opinionated profiles. This will give HUF a product-grade memory/learning layer without introducing an external dependency too early. Hindsight should remain a future optional integration if and when HUF specifically wants a stronger out-of-the-box consolidation/reflect engine. diff --git a/PROJECT_TRACKING.md b/PROJECT_TRACKING.md new file mode 100644 index 00000000..42dbbf2e --- /dev/null +++ b/PROJECT_TRACKING.md @@ -0,0 +1,244 @@ +# HUF Memory System — Project Tracking + +> **Project:** HUF Agent Memory & Learning Layer +> **Started:** 2026-03-28 +> **Status:** In Progress — Phase 1 (Design Complete, Implementation Pending) +> **Observer:** Coordinator Subagent + +--- + +## 1. Project Overview + +Transform HUF from "agent orchestration + RAG" into a platform where agents maintain durable, scoped, portable memory and reusable learned knowledge over time. + +### Key Deliverables +- **Memory Record** — First-class DocType for structured memory storage +- **Memory Policy** — Configurable capture and storage policies +- **Memory Profile** — Opinionated presets for common domains +- **Agent Integration** — Memory settings in Agent, Agent Conversation, Agent Run +- **Capture Pipeline** — Multiple capture modes (in-prompt, post-run sync/async, specialized agent) +- **Scope Model** — conversation, user, agent, namespace, global visibility +- **Storage Layer** — Canonical storage + optional FTS/vector indexing +- **Retrieval System** — Prompt injection, tool search, hybrid modes + +--- + +## 2. Project Phases & Milestones + +### Phase 1: Core Infrastructure (MVP) +| Milestone | Description | Status | Assigned | +|-----------|-------------|--------|----------| +| 1.1 | Memory Record DocType definition | ✅ Complete | Data Model Architect | +| 1.2 | Memory Policy DocType definition | ✅ Complete | Data Model Architect | +| 1.3 | Memory Profile DocType definition | ✅ Complete | Data Model Architect | +| 1.4 | Agent DocType memory fields | 🔲 Pending | TBD | +| 1.5 | Agent Conversation memory fields | 🔲 Pending | TBD | +| 1.6 | Agent Run observability fields | 🔲 Pending | TBD | + +### Phase 2: Capture Pipeline +| Milestone | Description | Status | Assigned | +|-----------|-------------|--------|----------| +| 2.1 | In-prompt capture mode | 🔲 Pending | TBD | +| 2.2 | Post-response sync capture | 🔲 Pending | TBD | +| 2.3 | Post-response async capture (background jobs) | 🔲 Pending | TBD | +| 2.4 | Specialized memory agent support | 🔲 Pending | TBD | +| 2.5 | Rule-only capture mode | 🔲 Pending | TBD | +| 2.6 | Conversation-end detection | 🔲 Pending | TBD | + +### Phase 3: Storage & Indexing +| Milestone | Description | Status | Assigned | +|-----------|-------------|--------|----------| +| 3.1 | Canonical storage implementation | 🔲 Pending | TBD | +| 3.2 | SQLite FTS indexing | 🔲 Pending | TBD | +| 3.3 | SQLite vector indexing | 🔲 Pending | TBD | +| 3.4 | Index backend abstraction | 🔲 Pending | TBD | + +### Phase 4: Retrieval & Integration +| Milestone | Description | Status | Assigned | +|-----------|-------------|--------|----------| +| 4.1 | Prompt injection system | 🔲 Pending | TBD | +| 4.2 | Memory search tool | 🔲 Pending | TBD | +| 4.3 | Hybrid retrieval mode | 🔲 Pending | TBD | +| 4.4 | Scope-aware filtering | 🔲 Pending | TBD | + +### Phase 5: Profiles & UX +| Milestone | Description | Status | Assigned | +|-----------|-------------|--------|----------| +| 5.1 | Programming Memory profile | 🔲 Pending | TBD | +| 5.2 | Science/Research Memory profile | 🔲 Pending | TBD | +| 5.3 | Language Learning profile | 🔲 Pending | TBD | +| 5.4 | Travel Planning profile | 🔲 Pending | TBD | +| 5.5 | General Knowledge profile | 🔲 Pending | TBD | +| 5.6 | Documentation Memory profile | 🔲 Pending | TBD | +| 5.7 | Agent form Memory tab UI | 🔲 Pending | TBD | +| 5.8 | Memory Explorer desk page | 🔲 Pending | TBD | + +### Phase 6: Polish & Future +| Milestone | Description | Status | Assigned | +|-----------|-------------|--------|----------| +| 6.1 | Consolidation engine | 🔲 Pending | TBD | +| 6.2 | Deduplication logic | 🔲 Pending | TBD | +| 6.3 | Expiry/pruning | 🔲 Pending | TBD | +| 6.4 | Memory health dashboards | 🔲 Pending | TBD | +| 6.5 | Hindsight integration (optional future) | 🔲 Pending | TBD | + +--- + +## 3. Agent Assignments + +| Agent ID | Role | Assigned Tasks | Status | Last Update | +|----------|------|----------------|--------|-------------| +| **data-model-architect** | Data Model Architect | DocType definitions (1.1-1.3) | ✅ Complete | 2026-03-28 | +| **capture-pipeline-engineer** | Capture Pipeline Engineer | Capture modes (2.1-2.6) | 🔲 Not started | — | +| **storage-engineer** | Storage Engineer | Indexing & storage (3.1-3.4) | 🔲 Not started | — | +| **retrieval-engineer** | Retrieval Engineer | Search & injection (4.1-4.4) | 🔲 Not started | — | +| **profile-ux-designer** | Profile/UX Designer | Profiles & UI (5.1-5.8) | 🔲 Not started | — | +| **tech-spec-writer** | Technical Spec Writer | Capture & Retrieval specs | ✅ Complete | 2026-03-28 | +| **coordinator** (this agent) | Observer/Coordinator | Tracking, review, coordination | 🟡 Active | 2026-03-28 | + +--- + +## 4. Current Status + +### Overall Progress: ~15% (Phase 1 Design Complete) + +### Recently Completed +- ✅ PRD finalized and documented +- ✅ Project directory structure created +- ✅ Tracking document established +- ✅ **Memory Record DocType design complete** (`~/code/huf-memory/doctype_designs/memory_record.json`) +- ✅ **Memory Policy DocType design complete** (`~/code/huf-memory/doctype_designs/memory_policy.json`) +- ✅ **Memory Profile DocType design complete** (`~/code/huf-memory/doctype_designs/memory_profile.json`) +- ✅ **Capture & Retrieval technical specifications complete** (`~/code/huf-memory/tech_specs/CAPTURE_RETRIEVAL.md`) + +### In Progress +- 🟡 Awaiting Phase 1 implementation (Frappe DocType creation) +- 🟡 Awaiting agent assignments for implementation tasks + +### Pending Tasks (Ready for Assignment) +1. Create Frappe DocType files from JSON designs: + - `huf/huf/doctype/memory_record/` + - `huf/huf/doctype/memory_policy/` + - `huf/huf/doctype/memory_profile/` +2. Add memory fields to existing DocTypes (Agent, Agent Conversation, Agent Run) +3. Implement Python controller classes for memory DocTypes +4. Set up database migrations + +--- + +## 5. Agent Output Files Summary + +| File Path | Description | Status | Created By | +|-----------|-------------|--------|------------| +| `~/code/huf-memory/doctype_designs/memory_record.json` | Memory Record DocType schema definition | ✅ Complete | data-model-architect | +| `~/code/huf-memory/doctype_designs/memory_policy.json` | Memory Policy DocType schema definition | ✅ Complete | data-model-architect | +| `~/code/huf-memory/doctype_designs/memory_profile.json` | Memory Profile DocType schema definition | ✅ Complete | data-model-architect | +| `~/code/huf-memory/tech_specs/CAPTURE_RETRIEVAL.md` | Capture modes & retrieval technical specs | ✅ Complete | tech-spec-writer | +| `~/code/huf-memory/PROJECT_TRACKING.md` | This tracking document | 🟡 Active | coordinator | + +--- + +## 6. Blockers & Issues + +| Issue ID | Description | Severity | Owner | Resolution | +|----------|-------------|----------|-------|------------| +| — | No active blockers | — | — | — | + +--- + +## 7. Technical Decisions Log + +| Date | Decision | Context | Status | +|------|----------|---------|--------| +| 2026-03-28 | Build natively in HUF first | Hindsight integration deferred to later phase | ✅ Finalized | +| 2026-03-28 | Rename "data management" → "Agent Memory" | Better product positioning | ✅ Finalized | +| 2026-03-28 | Support both flexible schema + opinionated presets | Balance power and usability | ✅ Finalized | +| 2026-03-28 | Use JSON-based DocType design first | Allows review before Frappe implementation | ✅ Finalized | + +--- + +## 8. Next Steps + +### Immediate (Next 24h) +1. [ ] Assign implementation agents to Phase 1 milestones +2. [ ] Convert JSON DocType designs to Frappe DocType files +3. [ ] Create Python controller classes for Memory Record, Policy, Profile +4. [ ] Set up development branch for memory system + +### Short Term (This Week) +1. [ ] Complete all Phase 1 DocType implementations +2. [ ] Begin Phase 2 capture pipeline implementation +3. [ ] Design storage backend abstraction layer + +### Medium Term (Next 2 Weeks) +1. [ ] Complete Phase 2 & 3 (capture + storage) +2. [ ] Begin Phase 4 retrieval system +3. [ ] Implement first 3 opinionated profiles + +--- + +## 9. Resource Links + +- **PRD:** `~/code/huf-memory/PRD.md` +- **This Tracking Doc:** `~/code/huf-memory/PROJECT_TRACKING.md` +- **AGENTS.md (Project Context):** `~/code/huf-memory/AGENTS.md` +- **DocType Designs:** `~/code/huf-memory/doctype_designs/` +- **Tech Specs:** `~/code/huf-memory/tech_specs/` +- **Code Repository:** `~/code/huf-memory/` (HUF Frappe app) + +--- + +## 10. Agent Communication Log + +| Timestamp | Agent | Message | Action Required | +|-----------|-------|---------|-----------------| +| 2026-03-28 04:46 | Coordinator | Project tracking initialized | Awaiting agent reports | +| 2026-03-28 04:47 | data-model-architect | Completed DocType designs for Memory Record, Memory Policy, Memory Profile | Review and approve for implementation | +| 2026-03-28 04:47 | tech-spec-writer | Completed Capture & Retrieval technical specifications | Review and use for implementation guidance | + +--- + +## 11. Design Artifacts Summary + +### Memory Record DocType +**Purpose:** Canonical portable unit of memory +**Key Fields:** +- Core: title, agent, conversation, run, source_type, producer_mode, memory_type +- Data: schema_name, profile_name, data_json, summary_text, raw_context_excerpt +- Scope: scope_type, scope_key, visibility +- Lifecycle: status, confidence, importance_score, ttl_days, effective_from/until +- Indexing: fts_indexed, vector_indexed, index_backend, last_indexed_at +- Retrieval Stats: last_retrieved_at, retrieval_count + +### Memory Policy DocType +**Purpose:** Configurable capture and storage policies +**Key Fields:** +- Basic: policy_name, enabled, agent, memory_profile +- Capture: capture_owner, memory_agent, capture_stage +- Frequency: capture_frequency_type, capture_frequency_value, conversation_end_strategy, idle_timeout_minutes +- Schema: capture_prompt, capture_schema_json, allow_open_schema, require_json_schema_match +- Merge: allow_update_existing, allow_merge, allow_append, min_confidence +- Storage: store_raw_payload, store_summary, enable_fts_index, enable_vector_index, vector_backend, fts_backend +- Retrieval: retrieval_mode_default, max_items_to_inject, max_tokens_to_inject + +### Memory Profile DocType +**Purpose:** Opinionated presets for common domains +**Key Fields:** +- Identity: profile_name, description, category, icon, is_system_profile +- Schema: default_schema_json, default_capture_prompt, default_memory_type_mapping +- Model: recommended_model, recommended_provider +- Defaults: default_capture_stage, default_frequency, default_scope_type, default_indexing_mode, default_retrieval_mode +- UI: ui_labels_json, example_memories_json, documentation_url + +### Capture & Retrieval Specs +**Document:** `CAPTURE_RETRIEVAL.md` +**Covers:** +- 5 capture modes: in_prompt, post_response_sync, post_response_async, specialized_agent, rules_only +- 10 trigger types: every_run, every_n_runs, every_n_turns, after_tool_call, final_response_only, conversation_end, idle_timeout, manual, scheduled +- 3 retrieval modes: inject, tool_only, hybrid +- Retrieval ranking algorithm +- Error handling strategies + +--- + +*Last updated: 2026-03-28 04:47 GMT+8 by coordinator* diff --git a/doctype_designs/memory_policy.json b/doctype_designs/memory_policy.json new file mode 100644 index 00000000..adf19fac --- /dev/null +++ b/doctype_designs/memory_policy.json @@ -0,0 +1,244 @@ +{ + "name": "Memory Policy", + "module": "HUF", + "doctype_type": "Document", + "description": "Defines how a given agent or workflow performs memory capture.", + "fields": [ + { + "fieldname": "policy_name", + "fieldtype": "Data", + "label": "Policy Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled", + "default": "1" + }, + { + "fieldname": "basic_section", + "fieldtype": "Section Break", + "label": "Basic Configuration" + }, + { + "fieldname": "agent", + "fieldtype": "Link", + "label": "Agent", + "options": "Agent" + }, + { + "fieldname": "memory_profile", + "fieldtype": "Link", + "label": "Memory Profile", + "options": "Memory Profile" + }, + { + "fieldname": "capture_configuration", + "fieldtype": "Section Break", + "label": "Capture Configuration" + }, + { + "fieldname": "capture_owner", + "fieldtype": "Select", + "label": "Capture Owner", + "options": "main_agent\nmemory_agent\npost_run_llm\nrules_only", + "reqd": 1, + "default": "main_agent" + }, + { + "fieldname": "memory_agent", + "fieldtype": "Link", + "label": "Memory Agent", + "options": "Agent", + "depends_on": "eval:doc.capture_owner=='memory_agent'" + }, + { + "fieldname": "capture_stage", + "fieldtype": "Select", + "label": "Capture Stage", + "options": "in_prompt\npost_response_sync\npost_response_async\nconversation_end\nscheduled", + "reqd": 1, + "default": "post_response_async" + }, + { + "fieldname": "frequency_section", + "fieldtype": "Section Break", + "label": "Capture Frequency" + }, + { + "fieldname": "capture_frequency_type", + "fieldtype": "Select", + "label": "Capture Frequency Type", + "options": "every_run\nevery_n_runs\nevery_n_turns\nconversation_end\nmanual\nscheduled", + "reqd": 1, + "default": "every_run" + }, + { + "fieldname": "capture_frequency_value", + "fieldtype": "Int", + "label": "Capture Frequency Value", + "description": "N value for every_n_runs or every_n_turns" + }, + { + "fieldname": "conversation_end_strategy", + "fieldtype": "Select", + "label": "Conversation End Strategy", + "options": "manual_close\nidle_timeout\nheuristic\nnever", + "default": "manual_close" + }, + { + "fieldname": "idle_timeout_minutes", + "fieldtype": "Int", + "label": "Idle Timeout (Minutes)", + "depends_on": "eval:doc.conversation_end_strategy=='idle_timeout'" + }, + { + "fieldname": "schema_section", + "fieldtype": "Section Break", + "label": "Schema & Validation" + }, + { + "fieldname": "capture_prompt", + "fieldtype": "Text Editor", + "label": "Capture Prompt" + }, + { + "fieldname": "capture_schema_json", + "fieldtype": "Code", + "label": "Capture Schema (JSON)", + "options": "JSON" + }, + { + "fieldname": "allow_open_schema", + "fieldtype": "Check", + "label": "Allow Open Schema", + "default": "0" + }, + { + "fieldname": "require_json_schema_match", + "fieldtype": "Check", + "label": "Require JSON Schema Match", + "default": "1" + }, + { + "fieldname": "merge_section", + "fieldtype": "Section Break", + "label": "Merge & Update Behavior" + }, + { + "fieldname": "allow_update_existing", + "fieldtype": "Check", + "label": "Allow Update Existing", + "default": "1" + }, + { + "fieldname": "allow_merge", + "fieldtype": "Check", + "label": "Allow Merge", + "default": "0" + }, + { + "fieldname": "allow_append", + "fieldtype": "Check", + "label": "Allow Append", + "default": "1" + }, + { + "fieldname": "min_confidence", + "fieldtype": "Float", + "label": "Minimum Confidence", + "default": "0.5" + }, + { + "fieldname": "storage_section", + "fieldtype": "Section Break", + "label": "Storage Configuration" + }, + { + "fieldname": "store_raw_payload", + "fieldtype": "Check", + "label": "Store Raw Payload", + "default": "1" + }, + { + "fieldname": "store_summary", + "fieldtype": "Check", + "label": "Store Summary", + "default": "1" + }, + { + "fieldname": "enable_fts_index", + "fieldtype": "Check", + "label": "Enable FTS Index", + "default": "1" + }, + { + "fieldname": "enable_vector_index", + "fieldtype": "Check", + "label": "Enable Vector Index", + "default": "0" + }, + { + "fieldname": "vector_backend", + "fieldtype": "Select", + "label": "Vector Backend", + "options": "none\nsqlite_vec\npgvector\ncustom", + "depends_on": "eval:doc.enable_vector_index==1", + "default": "sqlite_vec" + }, + { + "fieldname": "fts_backend", + "fieldtype": "Select", + "label": "FTS Backend", + "options": "sqlite_fts\ncustom", + "depends_on": "eval:doc.enable_fts_index==1", + "default": "sqlite_fts" + }, + { + "fieldname": "retrieval_section", + "fieldtype": "Section Break", + "label": "Retrieval Configuration" + }, + { + "fieldname": "retrieval_mode_default", + "fieldtype": "Select", + "label": "Default Retrieval Mode", + "options": "inject\ntool_only\nhybrid", + "reqd": 1, + "default": "hybrid" + }, + { + "fieldname": "max_items_to_inject", + "fieldtype": "Int", + "label": "Max Items to Inject", + "default": "5" + }, + { + "fieldname": "max_tokens_to_inject", + "fieldtype": "Int", + "label": "Max Tokens to Inject", + "default": "2000" + } + ], + "permissions": [ + { + "role": "System Manager", + "read": 1, + "write": 1, + "create": 1, + "delete": 1 + }, + { + "role": "Agent Manager", + "read": 1, + "write": 1, + "create": 1 + } + ], + "search_fields": ["policy_name", "agent", "capture_owner"], + "title_field": "policy_name", + "sort_field": "creation", + "sort_order": "DESC" +} diff --git a/doctype_designs/memory_profile.json b/doctype_designs/memory_profile.json new file mode 100644 index 00000000..d43ad47e --- /dev/null +++ b/doctype_designs/memory_profile.json @@ -0,0 +1,179 @@ +{ + "name": "Memory Profile", + "module": "HUF", + "doctype_type": "Document", + "description": "Opinionated preset layer providing ready-made capture schema, prompts, storage defaults, and recommended models for common domains.", + "fields": [ + { + "fieldname": "profile_name", + "fieldtype": "Data", + "label": "Profile Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "description", + "fieldtype": "Text", + "label": "Description" + }, + { + "fieldname": "category", + "fieldtype": "Select", + "label": "Category", + "options": "Programming\nScience/Research\nLanguage Learning\nReasoning/Mathematics\nGeneral Knowledge\nTravel Planning\nCRM/Customer Context\nSupport Ticket\nDocumentation\nCustom", + "reqd": 1 + }, + { + "fieldname": "appearance_section", + "fieldtype": "Section Break", + "label": "Appearance" + }, + { + "fieldname": "icon", + "fieldtype": "Data", + "label": "Icon", + "description": "Icon class or emoji for the profile" + }, + { + "fieldname": "is_system_profile", + "fieldtype": "Check", + "label": "Is System Profile", + "default": "0", + "description": "System profiles cannot be deleted or modified by users" + }, + { + "fieldname": "schema_section", + "fieldtype": "Section Break", + "label": "Default Schema" + }, + { + "fieldname": "default_schema_json", + "fieldtype": "Code", + "label": "Default Schema (JSON)", + "options": "JSON", + "description": "JSON Schema defining the structure of captured memory data" + }, + { + "fieldname": "default_capture_prompt", + "fieldtype": "Text Editor", + "label": "Default Capture Prompt", + "description": "Prompt template used when capturing memory with this profile" + }, + { + "fieldname": "default_memory_type_mapping", + "fieldtype": "Code", + "label": "Default Memory Type Mapping", + "options": "JSON", + "description": "JSON mapping of source types to memory types for this profile" + }, + { + "fieldname": "model_section", + "fieldtype": "Section Break", + "label": "Recommended Model" + }, + { + "fieldname": "recommended_model", + "fieldtype": "Data", + "label": "Recommended Model", + "description": "e.g. gpt-4, claude-3-sonnet, llama-3" + }, + { + "fieldname": "recommended_provider", + "fieldtype": "Select", + "label": "Recommended Provider", + "options": "openai\nanthropic\ncohere\ngoogle\nazure\nlocal\ncustom" + }, + { + "fieldname": "defaults_section", + "fieldtype": "Section Break", + "label": "Default Settings" + }, + { + "fieldname": "default_capture_stage", + "fieldtype": "Select", + "label": "Default Capture Stage", + "options": "in_prompt\npost_response_sync\npost_response_async\nconversation_end\nscheduled", + "default": "post_response_async" + }, + { + "fieldname": "default_frequency", + "fieldtype": "Select", + "label": "Default Frequency", + "options": "every_run\nevery_n_runs\nevery_n_turns\nconversation_end\nmanual\nscheduled", + "default": "every_run" + }, + { + "fieldname": "default_scope_type", + "fieldtype": "Select", + "label": "Default Scope Type", + "options": "conversation\nuser\nagent\nnamespace\nglobal", + "default": "user" + }, + { + "fieldname": "default_indexing_mode", + "fieldtype": "Select", + "label": "Default Indexing Mode", + "options": "none\nfts_only\nvector_only\nfts_and_vector", + "default": "fts_only" + }, + { + "fieldname": "default_retrieval_mode", + "fieldtype": "Select", + "label": "Default Retrieval Mode", + "options": "inject\ntool_only\nhybrid", + "default": "hybrid" + }, + { + "fieldname": "ui_section", + "fieldtype": "Section Break", + "label": "UI Configuration" + }, + { + "fieldname": "ui_labels_json", + "fieldtype": "Code", + "label": "UI Labels (JSON)", + "options": "JSON", + "description": "Custom labels and help text for form fields" + }, + { + "fieldname": "examples_section", + "fieldtype": "Section Break", + "label": "Examples & Documentation" + }, + { + "fieldname": "example_memories_json", + "fieldtype": "Code", + "label": "Example Memories (JSON)", + "options": "JSON", + "description": "Example memory records for documentation and testing" + }, + { + "fieldname": "documentation_url", + "fieldtype": "Data", + "label": "Documentation URL" + } + ], + "permissions": [ + { + "role": "System Manager", + "read": 1, + "write": 1, + "create": 1, + "delete": 1 + }, + { + "role": "Agent Manager", + "read": 1, + "write": 1, + "create": 1 + }, + { + "role": "All", + "read": 1 + } + ], + "search_fields": ["profile_name", "category", "description"], + "title_field": "profile_name", + "sort_field": "creation", + "sort_order": "DESC" +} diff --git a/doctype_designs/memory_record.json b/doctype_designs/memory_record.json new file mode 100644 index 00000000..5f9a9849 --- /dev/null +++ b/doctype_designs/memory_record.json @@ -0,0 +1,245 @@ +{ + "name": "Memory Record", + "module": "HUF", + "doctype_type": "Document", + "description": "A structured or semi-structured memory object produced from a conversation, run, event, or post-run process, with configurable scope, storage, and retrieval behavior.", + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "reqd": 1 + }, + { + "fieldname": "agent", + "fieldtype": "Link", + "label": "Agent", + "options": "Agent", + "reqd": 1 + }, + { + "fieldname": "conversation", + "fieldtype": "Link", + "label": "Conversation", + "options": "Agent Conversation" + }, + { + "fieldname": "run", + "fieldtype": "Link", + "label": "Run", + "options": "Agent Run" + }, + { + "fieldname": "source_type", + "fieldtype": "Select", + "label": "Source Type", + "options": "conversation\nrun\nmanual\nevent\nscheduled\nimported", + "reqd": 1, + "default": "conversation" + }, + { + "fieldname": "producer_mode", + "fieldtype": "Select", + "label": "Producer Mode", + "options": "main_agent\nmemory_agent\npost_run_llm\nrules_only\nmanual", + "reqd": 1, + "default": "main_agent" + }, + { + "fieldname": "memory_type", + "fieldtype": "Select", + "label": "Memory Type", + "options": "profile\nsession_state\npreference\nfact\nplan\nobservation\ninsight\ndomain_object\ncustom", + "reqd": 1 + }, + { + "fieldname": "schema_name", + "fieldtype": "Data", + "label": "Schema Name" + }, + { + "fieldname": "profile_name", + "fieldtype": "Link", + "label": "Memory Profile", + "options": "Memory Profile" + }, + { + "fieldname": "data_json", + "fieldtype": "Code", + "label": "Data (JSON)", + "options": "JSON" + }, + { + "fieldname": "summary_text", + "fieldtype": "Text", + "label": "Summary Text" + }, + { + "fieldname": "raw_context_excerpt", + "fieldtype": "Long Text", + "label": "Raw Context Excerpt" + }, + { + "fieldname": "scope_section", + "fieldtype": "Section Break", + "label": "Scope & Visibility" + }, + { + "fieldname": "scope_type", + "fieldtype": "Select", + "label": "Scope Type", + "options": "conversation\nuser\nagent\nnamespace\nglobal", + "reqd": 1, + "default": "conversation" + }, + { + "fieldname": "scope_key", + "fieldtype": "Data", + "label": "Scope Key", + "description": "e.g. conversation id, user id, agent name, namespace value" + }, + { + "fieldname": "visibility", + "fieldtype": "Select", + "label": "Visibility", + "options": "private\nshared_with_agent\nshared_with_namespace\nglobal", + "reqd": 1, + "default": "private" + }, + { + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status & Lifecycle" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "active\nsuperseded\narchived\nexpired\nerror", + "reqd": 1, + "default": "active" + }, + { + "fieldname": "confidence", + "fieldtype": "Float", + "label": "Confidence", + "default": "1.0" + }, + { + "fieldname": "importance_score", + "fieldtype": "Float", + "label": "Importance Score" + }, + { + "fieldname": "ttl_days", + "fieldtype": "Int", + "label": "TTL (Days)" + }, + { + "fieldname": "effective_from", + "fieldtype": "Datetime", + "label": "Effective From" + }, + { + "fieldname": "effective_until", + "fieldtype": "Datetime", + "label": "Effective Until" + }, + { + "fieldname": "supersedes_memory_record", + "fieldtype": "Link", + "label": "Supersedes Memory Record", + "options": "Memory Record" + }, + { + "fieldname": "created_from_turn_count", + "fieldtype": "Int", + "label": "Created From Turn Count" + }, + { + "fieldname": "metadata_section", + "fieldtype": "Section Break", + "label": "Metadata & Tags" + }, + { + "fieldname": "tags", + "fieldtype": "Table", + "label": "Tags", + "options": "Memory Record Tag" + }, + { + "fieldname": "metadata_json", + "fieldtype": "Code", + "label": "Metadata (JSON)", + "options": "JSON" + }, + { + "fieldname": "indexing_section", + "fieldtype": "Section Break", + "label": "Indexing" + }, + { + "fieldname": "fts_indexed", + "fieldtype": "Check", + "label": "FTS Indexed", + "default": "0" + }, + { + "fieldname": "vector_indexed", + "fieldtype": "Check", + "label": "Vector Indexed", + "default": "0" + }, + { + "fieldname": "index_backend", + "fieldtype": "Select", + "label": "Index Backend", + "options": "none\nsqlite_fts\nsqlite_vec\npgvector\ncustom", + "default": "none" + }, + { + "fieldname": "last_indexed_at", + "fieldtype": "Datetime", + "label": "Last Indexed At" + }, + { + "fieldname": "retrieval_section", + "fieldtype": "Section Break", + "label": "Retrieval Stats" + }, + { + "fieldname": "last_retrieved_at", + "fieldtype": "Datetime", + "label": "Last Retrieved At" + }, + { + "fieldname": "retrieval_count", + "fieldtype": "Int", + "label": "Retrieval Count", + "default": "0" + } + ], + "permissions": [ + { + "role": "System Manager", + "read": 1, + "write": 1, + "create": 1, + "delete": 1 + }, + { + "role": "Agent Manager", + "read": 1, + "write": 1, + "create": 1 + }, + { + "role": "All", + "read": 0 + } + ], + "search_fields": ["title", "memory_type", "status"], + "title_field": "title", + "sort_field": "creation", + "sort_order": "DESC" +} diff --git a/profiles/documentation/README.md b/profiles/documentation/README.md new file mode 100644 index 00000000..a7dcab47 --- /dev/null +++ b/profiles/documentation/README.md @@ -0,0 +1,227 @@ +# Documentation Memory Profile + +## Overview + +The **Documentation Memory** profile enables HUF to capture and maintain structured documentation state from conversations and runs. It goes beyond simple memory capture to support living documentation that can be updated, versioned, and referenced across projects. + +## Key Capabilities + +### 📄 Document Types Supported + +| Type | Purpose | Example Use Cases | +|------|---------|-------------------| +| `requirements` | Feature specifications, user stories | "The auth system must support OAuth2..." | +| `architecture` | System design, component relationships | "Three-layer architecture with..." | +| `decision` | ADR-style decision records | "We chose PostgreSQL over MongoDB..." | +| `api_contract` | API specifications, schemas | "POST /users accepts {...}" | +| `status` | Progress updates, blockers | "70% complete, blocked by..." | +| `codebase_mapping` | File locations, module purposes | "Entry point is src/main.py..." | +| `glossary` | Domain terms and definitions | "HUF: Human-Unified Framework..." | +| `migration_guide` | Version migration steps | "To migrate from v1 to v2..." | +| `deployment_notes` | Deployment procedures | "Deploy requires environment..." | +| `changelog` | Version changes, release notes | "v2.1.0 added support for..." | + +### 🔄 Section-Aware Updates + +Unlike append-only memory, Documentation Memory supports **targeted updates**: + +- **section_id**: Unique identifier for stable reference +- **supersedes**: Links to previous versions +- **is_update flag**: Indicates update vs. new content +- **update_summary**: Brief description of changes + +Example flow: +``` +1. Create ADR-001 (section_id: adr-001-storage) +2. Later decision updates it → new record with same section_id +3. Old record marked superseded, new record links to it +``` + +### 📋 Structured Data Extraction + +Each document type has type-specific structured fields: + +**Decision documents include:** +- `decision_rationale`: Why the decision was made +- `alternatives_considered`: Options evaluated +- `consequences`: Positive, negative, and neutral outcomes + +**API contracts include:** +- `request_schema`: Input structure +- `response_schema`: Output structure +- `auth_methods`: Authentication requirements +- `rate_limits`: Throttling policies + +## Configuration + +### Profile Settings + +```json +{ + "profile": "documentation", + "default_scope_type": "namespace", + "default_capture_stage": "post_response_async", + "storage": { + "enable_fts_index": true, + "enable_vector_index": true, + "allow_update_existing": true, + "allow_merge": true + } +} +``` + +### Scope Recommendations + +| Scope | Use Case | +|-------|----------| +| `conversation` | Temporary documentation drafts | +| `user` | Personal notes, learning journals | +| `agent` | Agent-specific documentation | +| `namespace` | **Recommended** - Project/team documentation | +| `global` | Shared patterns, company-wide standards | + +## Usage Examples + +### Creating Architecture Documentation + +``` +User: "Our system will use a microservices architecture with + API Gateway, Auth Service, and User Service." + +→ Extracts to: +{ + "document_type": "architecture", + "title": "System Architecture Overview", + "component": "system-design", + "structured_data": { + "layers": ["api-gateway", "auth-service", "user-service"], + "interfaces": ["REST API", "gRPC internal"] + } +} +``` + +### Capturing a Decision (ADR) + +``` +User: "We decided to use Redis for caching instead of Memcached + because it supports data structures we need." + +→ Extracts to: +{ + "document_type": "decision", + "title": "ADR-003: Redis for Caching Layer", + "decision_rationale": "Redis supports data structures...", + "alternatives_considered": ["Memcached", "In-memory"], + "consequences": { + "positive": ["Rich data structures", "Persistence option"], + "negative": ["Higher memory usage"] + } +} +``` + +### Updating Existing Documentation + +``` +User: "Update the API contract for /users - we now support + pagination with cursor-based navigation." + +→ Extracts to: +{ + "document_type": "api_contract", + "section_id": "api-users-list", // Same ID = update + "is_update": true, + "update_summary": "Added cursor-based pagination", + "supersedes": ["mem_abc123"] +} +``` + +## Integration with Markdown + +Documentation Memory supports optional merge into Markdown documents: + +```python +# Merge memory records into a single doc +memory.merge_to_markdown( + section_ids=["adr-001", "adr-002", "adr-003"], + output_path="/docs/decisions.md", + template="adr-index" +) +``` + +## Retrieval + +### Injected Context Format + +When retrieved for prompt injection: + +``` +## Relevant Documentation + +### [architecture] System Architecture Overview +**Project:** huf-memory-system | **Status:** approved +**Last updated:** 2024-03-18 + +The memory system uses SQLite as canonical storage with optional +vector indexing for semantic search... + +### [decision] ADR-001: Storage Backend Choice +**Status:** approved | **Supersedes:** none + +Decision: Use SQLite canonical + optional FTS/vector indexing +Rationale: Best balance of portability and capability... +``` + +### Search Capabilities + +- **Full-text search**: Match content, titles, tags +- **Semantic search**: Find conceptually related docs +- **Filtered search**: By project, component, status, type +- **Cross-reference**: Follow `related_sections` links + +## Best Practices + +### 1. Use Meaningful Section IDs + +Good: `adr-001-storage-backend`, `api-auth-oauth2-flow` +Poor: `doc-123`, `section-abc` + +### 2. Maintain Status Accurately + +- `draft`: Work in progress +- `review`: Ready for feedback +- `approved`: Accepted and active +- `deprecated`: Replaced by newer docs +- `archived`: Historical reference only + +### 3. Link Related Documentation + +Always populate `related_sections` to create a knowledge graph: +```json +"related_sections": [ + "adr-001-storage-backend", + "api-memory-record-create" +] +``` + +### 4. Tag Consistently + +Use a consistent tagging convention: +- Domain: `auth`, `storage`, `api` +- Type: `adr`, `spec`, `guide` +- Status: `critical-path`, `experimental` + +## File Structure + +``` +~/code/huf-memory/profiles/documentation/ +├── profile.json # Profile definition and schema +├── capture_prompt.txt # Extraction prompt for LLM +├── example_records.json # 2-3 example memory records +└── README.md # This documentation +``` + +## References + +- PRD Section 17: Documentation-Aware Upgrades +- ADR Template: [Markdown Any Decision Records](https://adr.github.io/madr/) +- API Documentation: [OpenAPI Specification](https://swagger.io/specification/) diff --git a/profiles/documentation/capture_prompt.txt b/profiles/documentation/capture_prompt.txt new file mode 100644 index 00000000..e1320094 --- /dev/null +++ b/profiles/documentation/capture_prompt.txt @@ -0,0 +1,126 @@ +# Documentation Memory Capture Prompt + +You are a Documentation Memory Extractor. Your task is to analyze the conversation/run context and extract structured documentation information. + +## Core Principles + +1. **Section-Aware Updates**: If an existing section_id is referenced or the content clearly updates a previously discussed topic, indicate this for targeted updates rather than creating new records. + +2. **Complete Context Preservation**: Capture the full context needed to understand this documentation without referring back to the original conversation. + +3. **Structured Extraction**: Always attempt to populate the `structured_data` field with type-specific fields based on `document_type`. + +## Document Type Guidelines + +### requirements +- Capture: features, constraints, acceptance criteria, user stories +- structured_data fields: priority_level, epic_id, user_story_format, dependencies + +### architecture +- Capture: system design, component relationships, data flow, technology choices +- structured_data fields: diagram_notation, layers, interfaces, dependencies, performance_targets + +### decision +- Capture: ADR-style records (Architecture Decision Records) +- structured_data fields: decision_date, status, scope, context, decision, consequences +- Always populate: decision_rationale, alternatives_considered, consequences + +### api_contract +- Capture: API specifications, endpoints, schemas, versioning +- structured_data fields: api_version, endpoint_paths, request_schema, response_schema, auth_methods, rate_limits + +### status +- Capture: current state, progress updates, blockers, next steps +- structured_data fields: completion_percentage, blockers, risks, milestones, next_milestone + +### codebase_mapping +- Capture: file locations, module purposes, entry points, configuration +- structured_data fields: repository_url, directory_structure, key_files, dependencies_file + +### glossary +- Capture: domain terms, definitions, acronyms, aliases +- structured_data fields: term, definition, aliases, related_terms, category + +### migration_guide +- Capture: migration steps, breaking changes, compatibility notes +- structured_data fields: from_version, to_version, breaking_changes, migration_steps, rollback_procedure + +### deployment_notes +- Capture: deployment procedures, environment configs, prerequisites +- structured_data fields: environment, prerequisites, deployment_steps, verification_steps, rollback_plan + +### changelog +- Capture: version changes, release notes, notable updates +- structured_data fields: version, release_date, changes_added, changes_fixed, changes_removed, contributors + +## Update Detection Rules + +Detect if this is an UPDATE to existing documentation: + +1. **Explicit Reference**: User says "update the requirements for..." or "change section X..." +2. **Implicit Update**: Content clearly refines or modifies a previously captured section +3. **New Content**: Entirely new topic with no clear predecessor + +For updates: +- Generate the same section_id if referenced or clearly identifiable +- Populate `supersedes` with the previous section_id +- Include a summary of what changed + +## Extraction Output Format + +```json +{ + "document_type": "", + "project_name": "", + "component": "", + "section_id": "", + "title": "", + "content": "", + "structured_data": { }, + "related_sections": [""], + "supersedes": [""], + "tags": [""], + "priority": "", + "status": "", + "authors": [""], + "stakeholders": [""], + "decision_rationale": "", + "alternatives_considered": [""], + "consequences": { + "positive": [""], + "negative": [""], + "neutral": [""] + }, + "is_update": , + "update_summary": "" +} +``` + +## Confidence Scoring + +Rate your extraction confidence (0.0-1.0): +- 0.9-1.0: Explicit documentation discussion with clear structure +- 0.7-0.9: Clear documentation content inferred from context +- 0.5-0.7: Partial documentation, some ambiguity +- Below 0.5: Do not capture, or mark as "draft" priority + +## Special Behaviors + +1. **Markdown Merge Ready**: Format content so it can be cleanly merged into Markdown documents if needed. + +2. **Cross-Reference Detection**: Identify and link related documentation sections. + +3. **Deprecation Handling**: If content indicates something is deprecated or replaced, set status to "deprecated" and populate `supersedes`. + +4. **Multi-Document Capture**: If multiple distinct documentation topics appear in one context, extract each as separate records with appropriate section_ids. + +5. **Index Emission**: Ensure key terms, API names, and important concepts appear in the content for FTS indexing. + +## Example User Messages to Extract + +- "We decided to use PostgreSQL instead of MongoDB because..." → decision document +- "The API should accept a POST request to /users with this schema..." → api_contract +- "For the auth module, we need to support OAuth2 and JWT..." → requirements +- "The system has three layers: presentation, business logic, and data..." → architecture +- "Current status: 70% complete, blocked by API review..." → status +- "This term 'HUF' refers to the orchestration framework..." → glossary diff --git a/profiles/documentation/example_records.json b/profiles/documentation/example_records.json new file mode 100644 index 00000000..5c591763 --- /dev/null +++ b/profiles/documentation/example_records.json @@ -0,0 +1,157 @@ +[ + { + "memory_id": "mem_docs_001", + "profile": "documentation", + "document_type": "decision", + "project_name": "huf-memory-system", + "component": "storage-layer", + "section_id": "adr-001-storage-backend", + "title": "ADR-001: SQLite with Optional Vector Backend for Memory Storage", + "content": "# ADR-001: SQLite with Optional Vector Backend for Memory Storage\n\n## Status\nAccepted\n\n## Context\nWe need a storage solution for the memory system that balances portability, performance, and flexibility. The system must work in single-node deployments while leaving room for future scale-out.\n\n## Decision\nUse SQLite as the canonical storage with optional indexing into SQLite FTS and SQLite-vec for vector search. This keeps HUF portable while supporting both text and semantic search.\n\n## Consequences\n\n### Positive\n- Zero external dependencies for basic operation\n- Single-file portability\n- Easy backups and migrations\n- FTS and vector capabilities available when needed\n\n### Negative\n- Not suitable for high-write distributed scenarios\n- Vector performance limited compared to dedicated vector DBs\n\n### Neutral\n- Can migrate to pgvector later without changing application code", + "structured_data": { + "decision_date": "2024-03-15", + "status": "accepted", + "scope": "memory storage architecture", + "context": "Need portable, flexible storage for memory records", + "decision": "SQLite canonical + optional FTS/vector indexing" + }, + "related_sections": ["adr-002-retrieval-model", "docs-storage-architecture"], + "supersedes": [], + "tags": ["architecture", "storage", "sqlite", "vector", "adr"], + "priority": "high", + "status": "approved", + "authors": ["system-architect"], + "stakeholders": ["backend-team", "devops"], + "decision_rationale": "SQLite provides the best balance of portability and capability for HUF's target deployment scenarios. The optional indexing layer allows users to enable advanced search only when needed.", + "alternatives_considered": [ + "PostgreSQL with pgvector - too heavy for single-node", + "Dedicated vector DB (Pinecone, Weaviate) - external dependency", + "Pure JSON files - no search capability", + "LevelDB/RocksDB - good but no built-in FTS/vector" + ], + "consequences": { + "positive": [ + "Zero external dependencies for basic operation", + "Single-file portability", + "Easy backups and migrations", + "FTS and vector capabilities available when needed" + ], + "negative": [ + "Not suitable for high-write distributed scenarios", + "Vector performance limited compared to dedicated vector DBs" + ], + "neutral": [ + "Can migrate to pgvector later without changing application code" + ] + }, + "scope_type": "namespace", + "scope_key": "huf-core", + "confidence": 0.98, + "importance_score": 0.95, + "created_at": "2024-03-15T10:30:00Z", + "updated_at": "2024-03-15T10:30:00Z", + "retrieval_count": 42, + "is_update": false, + "update_summary": null + }, + { + "memory_id": "mem_docs_002", + "profile": "documentation", + "document_type": "api_contract", + "project_name": "huf-memory-system", + "component": "memory-api", + "section_id": "api-memory-record-create", + "title": "Memory Record Create API", + "content": "# Memory Record Create API\n\n## Endpoint\n`POST /api/v1/memory/records`\n\n## Request\n\n### Headers\n- `Content-Type: application/json`\n- `Authorization: Bearer `\n\n### Body\n```json\n{\n \"profile\": \"documentation\",\n \"document_type\": \"requirements\",\n \"project_name\": \"my-project\",\n \"title\": \"User Authentication Requirements\",\n \"content\": \"The system must support OAuth2...\",\n \"structured_data\": {\n \"priority_level\": \"high\",\n \"epic_id\": \"auth-001\"\n },\n \"tags\": [\"auth\", \"oauth2\"],\n \"priority\": \"high\",\n \"scope_type\": \"namespace\",\n \"scope_key\": \"my-team\"\n}\n```\n\n## Response\n\n### Success (201 Created)\n```json\n{\n \"memory_id\": \"mem_abc123\",\n \"section_id\": \"req-auth-001\",\n \"status\": \"active\",\n \"created_at\": \"2024-03-20T14:30:00Z\",\n \"indexed\": true\n}\n```\n\n### Error (400 Bad Request)\n```json\n{\n \"error\": \"validation_error\",\n \"message\": \"Missing required field: document_type\",\n \"field\": \"document_type\"\n}\n```\n\n### Error (409 Conflict)\n```json\n{\n \"error\": \"duplicate_section\",\n \"message\": \"Section ID already exists. Use PUT for updates.\",\n \"existing_memory_id\": \"mem_xyz789\"\n}\n```", + "structured_data": { + "api_version": "v1", + "endpoint_paths": ["/api/v1/memory/records"], + "request_schema": { + "type": "object", + "required": ["profile", "document_type", "title", "content"], + "properties": { + "profile": {"type": "string"}, + "document_type": {"type": "string"}, + "title": {"type": "string"}, + "content": {"type": "string"} + } + }, + "response_schema": { + "success": {"status": 201, "body": "memory_id, section_id, status"}, + "error_validation": {"status": 400, "body": "error, message, field"}, + "error_duplicate": {"status": 409, "body": "error, message, existing_memory_id"} + }, + "auth_methods": ["bearer_token"], + "rate_limits": "100 req/min per user" + }, + "related_sections": ["api-memory-record-update", "api-memory-record-get", "api-memory-search"], + "supersedes": ["api-draft-memory-create-v0"], + "tags": ["api", "rest", "memory-record", "v1"], + "priority": "critical", + "status": "approved", + "authors": ["api-lead", "backend-team"], + "stakeholders": ["frontend-team", "integrations", "qa"], + "scope_type": "namespace", + "scope_key": "huf-api-docs", + "confidence": 0.97, + "importance_score": 0.95, + "created_at": "2024-03-20T14:00:00Z", + "updated_at": "2024-03-25T09:15:00Z", + "retrieval_count": 128, + "is_update": true, + "update_summary": "Added 409 Conflict response for duplicate section IDs, clarified rate limits" + }, + { + "memory_id": "mem_docs_003", + "profile": "documentation", + "document_type": "architecture", + "project_name": "huf-memory-system", + "component": "capture-pipeline", + "section_id": "arch-capture-modes", + "title": "Memory Capture Pipeline Architecture", + "content": "# Memory Capture Pipeline Architecture\n\n## Overview\nThe memory system supports multiple capture modes to balance latency, consistency, and cost.\n\n## Capture Modes\n\n### 1. In-Prompt Capture\nThe main agent maintains structured memory during its run.\n\n**Flow:**\n```\nUser Input → Agent Processing → Memory Update in Context → Response\n```\n\n**Best for:**\n- Low-latency simple fields\n- Session state\n- Known domain patterns\n\n**Trade-offs:**\n- Consumes context window\n- Limited to main agent capabilities\n\n### 2. Post-Response Synchronous\nCapture happens immediately after user-facing response.\n\n**Flow:**\n```\nUser Input → Agent Response → Memory Extraction (sync) → Store → Return to User\n```\n\n**Best for:**\n- Critical profile updates\n- High-confidence structured extraction\n\n**Trade-offs:**\n- Adds latency to response\n- Failures block completion\n\n### 3. Post-Response Asynchronous (Recommended)\nBackground job performs extraction after main response.\n\n**Flow:**\n```\nUser Input → Agent Response (returned immediately)\n ↓\n Background Job → Extract → Store → Index\n```\n\n**Best for:**\n- Complex extraction\n- Cost optimization\n- Non-critical documentation\n\n**Trade-offs:**\n- Slight delay before searchable\n- Needs queue infrastructure\n\n### 4. Specialized Memory Agent\nDedicated agent for extraction with its own model/prompt.\n\n**Flow:**\n```\nConversation Context → Memory Agent → Structured Extraction → Store\n```\n\n**Best for:**\n- Strict schemas\n- Domain-specific extraction\n- Different model for cost/quality\n\n### 5. Rule-Only Capture\nNo LLM involved, deterministic extraction.\n\n**Flow:**\n```\nEvent → Rule Engine → Structured Data → Store\n```\n\n**Best for:**\n- Timestamps, IDs\n- State transitions\n- System events\n\n## Pipeline Components\n\n```\n┌─────────────────┐\n│ Capture Trigger │\n└────────┬────────┘\n ↓\n┌─────────────────┐ ┌──────────────────┐\n│ Context Buffer │───→│ Extraction Stage │\n└─────────────────┘ └────────┬─────────┘\n ↓\n ┌───────────────────────┐\n │ Validation & Merge │\n └───────────┬───────────┘\n ↓\n ┌───────────────────────┐\n │ Canonical Storage │\n └───────────┬───────────┘\n ↓\n ┌───────────────────────┐\n │ Optional Indexing │\n │ (FTS / Vector) │\n └───────────────────────┘\n```", + "structured_data": { + "diagram_notation": "mermaid", + "layers": [ + "capture-triggers", + "context-buffer", + "extraction-stage", + "validation-merge", + "canonical-storage", + "optional-indexing" + ], + "interfaces": [ + "agent-memory-api", + "background-job-queue", + "storage-backend", + "search-index" + ], + "dependencies": [ + "sqlite", + "optional-vector-backend", + "job-queue" + ], + "performance_targets": { + "in_prompt_latency": "< 10ms", + "async_extraction": "< 5s", + "indexing_delay": "< 30s" + } + }, + "related_sections": ["adr-001-storage-backend", "api-memory-record-create"], + "supersedes": [], + "tags": ["architecture", "capture", "pipeline", "async", "extraction"], + "priority": "high", + "status": "approved", + "authors": ["system-architect"], + "stakeholders": ["backend-team", "ml-team", "devops"], + "scope_type": "namespace", + "scope_key": "huf-core", + "confidence": 0.96, + "importance_score": 0.92, + "created_at": "2024-03-18T11:00:00Z", + "updated_at": "2024-03-18T11:00:00Z", + "retrieval_count": 67, + "is_update": false, + "update_summary": null + } +] diff --git a/profiles/documentation/profile.json b/profiles/documentation/profile.json new file mode 100644 index 00000000..2a010ceb --- /dev/null +++ b/profiles/documentation/profile.json @@ -0,0 +1,147 @@ +{ + "profile_name": "documentation", + "display_name": "Documentation Memory", + "description": "Capture and maintain structured documentation state from conversations and runs. Supports project requirements, architecture notes, implementation decisions, API contracts, status summaries, codebase mapping, and domain glossary.", + "category": "engineering", + "version": "1.0.0", + "is_system_profile": true, + "icon": "📄", + + "default_memory_type": "domain_object", + "default_scope_type": "namespace", + "default_retrieval_mode": "hybrid", + "default_capture_stage": "post_response_async", + + "schema_definition": { + "type": "object", + "properties": { + "document_type": { + "type": "string", + "enum": [ + "requirements", + "architecture", + "decision", + "api_contract", + "status", + "codebase_mapping", + "glossary", + "migration_guide", + "deployment_notes", + "changelog" + ], + "description": "Type of documentation being captured" + }, + "project_name": { + "type": "string", + "description": "Name of the project or system this documentation relates to" + }, + "component": { + "type": "string", + "description": "Specific component, module, or subsystem" + }, + "section_id": { + "type": "string", + "description": "Unique identifier for this section or record for targeted updates" + }, + "title": { + "type": "string", + "description": "Human-readable title of this documentation entry" + }, + "content": { + "type": "string", + "description": "The main documentation content" + }, + "structured_data": { + "type": "object", + "description": "Typed fields specific to the document_type" + }, + "related_sections": { + "type": "array", + "items": { "type": "string" }, + "description": "List of related section_ids for cross-linking" + }, + "supersedes": { + "type": "array", + "items": { "type": "string" }, + "description": "Section IDs that this entry replaces or supersedes" + }, + "tags": { + "type": "array", + "items": { "type": "string" }, + "description": "Tags for categorization" + }, + "priority": { + "type": "string", + "enum": ["critical", "high", "medium", "low", "draft"], + "description": "Documentation priority level" + }, + "status": { + "type": "string", + "enum": ["draft", "review", "approved", "deprecated", "archived"], + "description": "Current status of this documentation" + }, + "authors": { + "type": "array", + "items": { "type": "string" }, + "description": "Contributors to this documentation" + }, + "stakeholders": { + "type": "array", + "items": { "type": "string" }, + "description": "People or teams who should be aware of this documentation" + }, + "decision_rationale": { + "type": "string", + "description": "For decision documents: why this decision was made" + }, + "alternatives_considered": { + "type": "array", + "items": { "type": "string" }, + "description": "Options that were evaluated" + }, + "consequences": { + "type": "object", + "properties": { + "positive": { "type": "array", "items": { "type": "string" } }, + "negative": { "type": "array", "items": { "type": "string" } }, + "neutral": { "type": "array", "items": { "type": "string" } } + }, + "description": "Expected consequences of decisions or changes" + } + }, + "required": ["document_type", "title", "content"] + }, + + "recommended_model": { + "provider": "anthropic", + "model": "claude-3-5-sonnet", + "rationale": "Strong structured output capabilities and reasoning for documentation extraction" + }, + + "storage_defaults": { + "store_raw_payload": true, + "store_summary": true, + "enable_fts_index": true, + "enable_vector_index": true, + "index_backend": "sqlite_vec", + "allow_update_existing": true, + "allow_merge": true, + "ttl_days": null + }, + + "retrieval_defaults": { + "max_items_to_inject": 5, + "max_tokens_to_inject": 2000, + "ranking_strategy": "recency_and_relevance", + "context_window_overlap": true + }, + + "ui_labels": { + "create_button": "Capture Documentation", + "edit_button": "Update Section", + "search_placeholder": "Search documentation...", + "filter_by_type": "Document Type", + "filter_by_project": "Project", + "filter_by_status": "Status" + } +} diff --git a/profiles/programming/README.md b/profiles/programming/README.md new file mode 100644 index 00000000..1f44b194 --- /dev/null +++ b/profiles/programming/README.md @@ -0,0 +1,273 @@ +# Programming Memory Profile + +## Overview + +The Programming Memory profile captures structured programming knowledge from conversations to build a persistent technical memory. It helps agents provide better assistance by remembering your coding preferences, architectural decisions, debugging insights, and reusable patterns. + +## When to Use + +Use this profile when: +- Discussing code or technical architecture +- Making decisions about technology choices +- Debugging issues and finding solutions +- Establishing project conventions +- Sharing reusable code snippets +- Configuring development tools + +## Memory Types + +### 1. Language Preferences (`language_pref`) +Captures primary language choices and version constraints. + +**Captured fields:** +- Preferred language(s) +- Version requirements +- Language-specific settings + +**Example use case:** Remembering the user prefers TypeScript with strict mode enabled. + +--- + +### 2. Stack Configuration (`stack_config`) +Records the technology stack for projects. + +**Captured fields:** +- Frontend framework +- Backend technology +- Database choice +- Infrastructure/platform +- Development tools + +**Example use case:** Recalling a project uses Next.js + tRPC + PostgreSQL + Vercel. + +--- + +### 3. Coding Conventions (`convention`) +Stores style preferences and project organization rules. + +**Captured fields:** +- Naming conventions +- Style guide references +- Project structure patterns +- Custom linting rules + +**Example use case:** Remembering the team uses snake_case for Python but camelCase for JavaScript. + +--- + +### 4. Architectural Decisions (`architecture_decision`) +Documents significant design choices with context and rationale. + +**Captured fields:** +- Decision title and context +- Options considered +- Chosen approach +- Rationale and tradeoffs +- Reversibility assessment + +**Example use case:** Recording why GraphQL was chosen over REST for a specific service. + +--- + +### 5. Fix Patterns (`fix_pattern`) +Captures reusable solutions to bugs and issues. + +**Captured fields:** +- Problem description +- Identifying symptoms +- Root cause analysis +- Solution steps +- Prevention strategies + +**Example use case:** Recording how to fix SQLAlchemy connection pool exhaustion in FastAPI apps. + +--- + +### 6. Debugging Context (`debug_context`) +Stores context from debugging sessions. + +**Captured fields:** +- Error messages and codes +- Environment details +- Diagnostic steps taken +- Resolution found + +**Example use case:** Remembering a specific error pattern and its fix for a recurring issue. + +--- + +### 7. Code Snippets (`snippet`) +Saves reusable code patterns and utilities. + +**Captured fields:** +- Snippet title and description +- Code content +- Usage instructions +- Tags for discovery + +**Example use case:** Storing a custom React hook or utility function used across projects. + +--- + +### 8. Tool Configurations (`tool_config`) +Records editor, CLI, and environment preferences. + +**Captured fields:** +- Editor/IDE settings +- CLI tool preferences +- Environment variables +- Aliases and shortcuts + +**Example use case:** Remembering custom VS Code settings or shell aliases. + +--- + +### 9. Learning & Insights (`learning`) +Captures new knowledge and best practices discovered. + +**Captured fields:** +- Concepts learned +- Best practices identified +- Lessons from mistakes +- Performance insights + +**Example use case:** Recording insights about async Rust patterns or new language features. + +--- + +## Schema Reference + +See `profile.json` for the complete JSON schema. Key fields for all memory types: + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `memory_type` | string | Yes | One of the 9 memory type identifiers | +| `language` | string | Yes | Primary programming language | +| `importance` | enum | Yes | `critical`, `high`, `medium`, or `low` | +| `confidence` | number | Yes | 0.0 to 1.0 confidence score | +| `tags` | array | No | Searchable tags for retrieval | +| `project_context` | string | No | Project identifier for scoping | +| `related_files` | array | No | File paths mentioned in context | + +## Capture Configuration + +### Default Settings + +| Setting | Value | Description | +|---------|-------|-------------| +| Capture Stage | `post_response_async` | Capture happens after response, asynchronously | +| Frequency | `conversation_end` | Trigger at end of conversation | +| Scope | `user` | Memory scoped to user across conversations | +| Indexing | `fts_and_vector` | Full-text and vector indexing enabled | +| Retrieval | `hybrid` | Both injection and tool-based search | + +### Recommended Model + +- **Primary:** Claude 3.5 Sonnet (Anthropic) +- **Fallback:** GPT-4o Mini for cost-effective extraction + +## Usage Examples + +### Example 1: Capturing an Architecture Decision + +**Conversation:** +> User: "We're going with tRPC for the new dashboard. I evaluated REST and GraphQL but the type safety and DX win for our team size. The tradeoff is tighter coupling but we don't need mobile support yet." + +**Extracted Memory:** +```json +{ + "memory_type": "architecture_decision", + "language": "TypeScript", + "decision": { + "title": "Chose tRPC over REST for API layer", + "options_considered": ["REST with OpenAPI", "GraphQL with Apollo", "tRPC"], + "chosen_approach": "tRPC with Next.js integration", + "rationale": "End-to-end type safety without code generation, excellent DX", + "tradeoffs": "Tighter coupling between frontend and backend" + }, + "importance": "critical", + "confidence": 0.95, + "tags": ["typescript", "trpc", "architecture"] +} +``` + +### Example 2: Capturing a Fix Pattern + +**Conversation:** +> User: "Finally fixed that SQLAlchemy pool exhaustion. The default pool size of 5 was too small for FastAPI. Had to bump it to 20 with overflow to 40 and add pool_pre_ping. Always use explicit pool sizes with async SQLAlchemy!" + +**Extracted Memory:** +```json +{ + "memory_type": "fix_pattern", + "language": "Python", + "fix_pattern": { + "problem": "Database connection pool exhaustion under async load", + "symptoms": ["TimeoutError", "QueuePool limit reached"], + "solution": "Increase pool_size to 20, max_overflow to 40, add pool_pre_ping=True", + "prevention": "Always set explicit pool sizes for async SQLAlchemy" + }, + "importance": "high", + "confidence": 0.92, + "tags": ["python", "fastapi", "sqlalchemy", "performance"] +} +``` + +## Retrieval Behavior + +### Injected Context + +When `retrieval_mode` is `inject` or `hybrid`, relevant memories are automatically included in the agent's prompt context. + +**Prioritization:** +1. Memories from the same `project_context` +2. High importance memories +3. Recently accessed memories +4. Memories matching current conversation tags + +### Tool-Based Search + +Agents can explicitly search memories using the `memory_search` tool with filters: +- `memory_type`: Filter by type (e.g., only `fix_pattern`) +- `language`: Filter by programming language +- `tags`: Filter by tags +- `project_context`: Filter by project + +## Best Practices + +1. **Use consistent project_context** - Tag memories with project identifiers to keep contexts separate +2. **Tag liberally** - Include language, framework, and domain tags for better retrieval +3. **Set appropriate importance** - Reserve "critical" for fundamental preferences and decisions +4. **Review and prune** - Periodically review memories and archive outdated ones +5. **Link related files** - Include file paths when relevant for better context + +## Integration with Agents + +To enable Programming Memory for an agent: + +1. Set `enable_memory: true` in agent configuration +2. Set `memory_profile: "programming"` +3. Configure capture stage and frequency as needed +4. Optionally set `memory_agent` for specialized extraction + +## File Structure + +``` +~/code/huf-memory/profiles/programming/ +├── profile.json # Profile definition and schema +├── capture_prompt.txt # LLM prompt for extraction +├── example_records.json # Example memory records +└── README.md # This documentation +``` + +## Related Profiles + +- **Documentation Memory** - For capturing documentation and requirements +- **Science/Research Memory** - For academic and research contexts +- **Language Learning Memory** - For natural language learning + +## Version History + +| Version | Date | Changes | +|---------|------|---------| +| 1.0.0 | 2026-03-28 | Initial release with 9 memory types | diff --git a/profiles/programming/capture_prompt.txt b/profiles/programming/capture_prompt.txt new file mode 100644 index 00000000..26844fb3 --- /dev/null +++ b/profiles/programming/capture_prompt.txt @@ -0,0 +1,120 @@ +You are a Programming Memory extraction specialist. Your task is to analyze the conversation context and extract structured programming-related knowledge worth remembering. + +## Extraction Guidelines + +Capture memories that fall into these categories: + +### 1. Language Preferences (memory_type: language_pref) +- Primary languages the user prefers +- Version constraints or requirements +- Language-specific preferences (strict mode, type checking, etc.) + +### 2. Stack Configuration (memory_type: stack_config) +- Frameworks and libraries in use +- Database choices +- Infrastructure and deployment setup +- Tooling preferences (build tools, linters, formatters) + +### 3. Coding Conventions (memory_type: convention) +- Naming conventions (camelCase, snake_case, PascalCase, etc.) +- Style guide preferences +- Project structure patterns +- Custom rules specific to the user's workflow + +### 4. Architectural Decisions (memory_type: architecture_decision) +- Significant design choices and their rationale +- Options considered and why they were rejected +- Trade-offs accepted +- Whether the decision is reversible + +### 5. Fix Patterns (memory_type: fix_pattern) +- Bugs or issues encountered +- Symptoms that identify the problem +- Root cause analysis +- Solution applied +- Prevention strategies + +### 6. Debugging Context (memory_type: debug_context) +- Error messages and codes +- Environment details +- Steps taken to diagnose +- Resolution found + +### 7. Code Snippets (memory_type: snippet) +- Reusable code patterns +- Utility functions +- Configuration templates +- Helper scripts + +### 8. Tool Configurations (memory_type: tool_config) +- Editor/IDE settings +- CLI tool preferences +- Environment variable setups +- Alias or shortcut definitions + +### 9. Learning & Insights (memory_type: learning) +- New concepts discovered +- Best practices identified +- Lessons from mistakes +- Performance optimizations found + +## Extraction Rules + +1. **Be selective** - Not every conversation needs memory capture. Focus on: + - New information not previously captured + - Clarifications or changes to prior preferences + - Specific technical decisions with context + - Reusable solutions to problems + +2. **Extract facts, not speculation** - Only capture what was explicitly stated or strongly implied + +3. **Use appropriate confidence scores**: + - 0.9-1.0: Explicitly stated by user + - 0.7-0.9: Strongly implied, high confidence inference + - 0.5-0.7: Reasonable inference, medium confidence + - Below 0.5: Do not capture + +4. **Assign importance**: + - critical: Fundamental to how the user works (language choice, core conventions) + - high: Significant decisions or frequently referenced patterns + - medium: Useful context that may be relevant + - low: Minor preferences or situational details + +5. **Tag appropriately** - Use tags that will help with retrieval: + - Language names (typescript, python, rust, etc.) + - Framework names (react, django, rails, etc.) + - Domain tags (frontend, backend, api, database, etc.) + - Specific tools (docker, kubernetes, webpack, etc.) + +6. **Link related files** - When files are mentioned, include their paths + +## Output Format + +Produce a valid JSON object matching the Programming Memory schema. Include only fields relevant to the memory_type being captured. + +For each memory extracted, provide: +- memory_type (required) +- language (required) +- Appropriate nested objects based on memory_type +- importance (required) +- confidence (required) +- tags (at least one tag recommended) +- project_context (if available from conversation) + +If no memories should be extracted from this conversation, return an empty array: [] + +## Examples of Good Extractions + +- User mentions they prefer snake_case in Python but camelCase in JavaScript → Capture as convention memory +- User explains why they chose PostgreSQL over MongoDB → Capture as architecture_decision +- User describes how they fixed a specific race condition → Capture as fix_pattern +- User shares a custom eslint configuration → Capture as tool_config +- User mentions they're learning Rust and struggling with borrow checker → Capture as learning + +## Examples of What NOT to Capture + +- Transient code that won't be reused +- One-off commands without context +- Errors that were immediately resolved without learning +- Personal opinions not related to technical work +- Vague statements without actionable detail diff --git a/profiles/programming/example_records.json b/profiles/programming/example_records.json new file mode 100644 index 00000000..fbcce286 --- /dev/null +++ b/profiles/programming/example_records.json @@ -0,0 +1,73 @@ +[ + { + "memory_type": "architecture_decision", + "language": "TypeScript", + "frameworks": ["React", "Next.js", "tRPC"], + "stack": { + "frontend": "Next.js 14 with App Router", + "backend": "tRPC with Next.js API routes", + "database": "PostgreSQL via Prisma ORM", + "infra": "Vercel", + "tools": ["TypeScript", "Tailwind CSS", "Zod"] + }, + "decision": { + "title": "Chose tRPC over REST for API layer", + "context": "Building a full-stack application with tight coupling between frontend and backend", + "options_considered": [ + "REST with OpenAPI", + "GraphQL with Apollo", + "tRPC for end-to-end type safety" + ], + "chosen_approach": "tRPC with Next.js integration", + "rationale": "End-to-end type safety without code generation, excellent DX with autocomplete, simpler mental model than GraphQL for this team size", + "tradeoffs": "Tighter coupling between frontend and backend, harder to support mobile clients later", + "reversibility": "costly_to_change" + }, + "importance": "critical", + "confidence": 0.95, + "tags": ["typescript", "trpc", "nextjs", "api-design", "type-safety", "architecture"], + "related_files": ["/src/server/routers/index.ts", "/src/lib/trpc.ts"], + "project_context": "internal-dashboard-v2" + }, + { + "memory_type": "fix_pattern", + "language": "Python", + "frameworks": ["FastAPI", "SQLAlchemy", "asyncpg"], + "fix_pattern": { + "problem": "Database connection pool exhaustion under async load", + "symptoms": [ + "TimeoutError on database queries under load", + "'QueuePool limit of size 5 overflow 10 reached' errors", + "Requests hanging indefinitely" + ], + "root_cause": "Default SQLAlchemy async engine pool size too small for FastAPI's concurrent request handling; asyncpg connections not being released properly in some error paths", + "solution": "Increase pool_size to 20, max_overflow to 40, and add pool_pre_ping=True. Ensure all async session contexts use proper try/finally blocks.", + "prevention": "Always set explicit pool sizes for async SQLAlchemy. Use context managers for all DB operations. Add connection pool monitoring metrics.", + "related_issues": ["SQLAlchemy #5811", "asyncpg #1188"] + }, + "importance": "high", + "confidence": 0.92, + "tags": ["python", "fastapi", "sqlalchemy", "asyncpg", "database", "pooling", "performance"], + "related_files": ["/app/db/session.py", "/app/core/config.py"], + "project_context": "api-service" + }, + { + "memory_type": "convention", + "language": "Rust", + "conventions": { + "naming": "Use snake_case for functions/variables, PascalCase for types/traits, SCREAMING_SNAKE_CASE for constants", + "style_guide": "Follow rustfmt defaults with imports_granularity = 'Module' and group_imports = 'StdExternalCrate'", + "project_structure": "Prefer workspace layout with crates for: core, api, cli. Keep tests in src/ with #[cfg(test)] modules", + "custom_rules": [ + "Always derive Debug for public types", + "Use thiserror for error types, anyhow for application code", + "Prefer &str over &String, impl AsRef over &PathBuf in public APIs" + ] + }, + "importance": "high", + "confidence": 0.88, + "tags": ["rust", "conventions", "naming", "project-structure", "best-practices"], + "related_files": ["rustfmt.toml", "Cargo.toml", ".clippy.toml"], + "project_context": "huf-core" + } +] diff --git a/profiles/programming/profile.json b/profiles/programming/profile.json new file mode 100644 index 00000000..be34fff0 --- /dev/null +++ b/profiles/programming/profile.json @@ -0,0 +1,186 @@ +{ + "profile_name": "programming", + "display_name": "Programming Memory", + "description": "Captures coding context, technical decisions, debugging insights, and reusable patterns to improve development workflows over time.", + "category": "development", + "version": "1.0.0", + "is_system_profile": true, + "icon": "code-2", + "schema_version": "1.0", + "default_schema_json": { + "type": "object", + "required": ["memory_type", "language"], + "properties": { + "memory_type": { + "type": "string", + "enum": ["language_pref", "stack_config", "convention", "architecture_decision", "fix_pattern", "debug_context", "snippet", "tool_config", "learning"], + "description": "Type of programming memory being captured" + }, + "language": { + "type": "string", + "description": "Primary programming language or runtime (e.g., TypeScript, Python, Rust, Go)" + }, + "frameworks": { + "type": "array", + "items": { "type": "string" }, + "description": "Frameworks and libraries in use" + }, + "stack": { + "type": "object", + "properties": { + "frontend": { "type": "string" }, + "backend": { "type": "string" }, + "database": { "type": "string" }, + "infra": { "type": "string" }, + "tools": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "conventions": { + "type": "object", + "properties": { + "naming": { "type": "string", "description": "Naming conventions followed" }, + "style_guide": { "type": "string", "description": "Style guide or linter rules" }, + "project_structure": { "type": "string", "description": "Directory and file organization patterns" }, + "custom_rules": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "decision": { + "type": "object", + "properties": { + "title": { "type": "string" }, + "context": { "type": "string" }, + "options_considered": { + "type": "array", + "items": { "type": "string" } + }, + "chosen_approach": { "type": "string" }, + "rationale": { "type": "string" }, + "tradeoffs": { "type": "string" }, + "reversibility": { "type": "string", "enum": ["reversible", "irreversible", "costly_to_change"] } + } + }, + "fix_pattern": { + "type": "object", + "properties": { + "problem": { "type": "string", "description": "Description of the bug or issue" }, + "symptoms": { + "type": "array", + "items": { "type": "string" } + }, + "root_cause": { "type": "string" }, + "solution": { "type": "string" }, + "prevention": { "type": "string" }, + "related_issues": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "debug_context": { + "type": "object", + "properties": { + "error_message": { "type": "string" }, + "error_code": { "type": "string" }, + "stack_trace_hash": { "type": "string" }, + "environment": { "type": "string" }, + "steps_tried": { + "type": "array", + "items": { "type": "string" } + }, + "resolution": { "type": "string" } + } + }, + "snippet": { + "type": "object", + "properties": { + "title": { "type": "string" }, + "description": { "type": "string" }, + "code": { "type": "string" }, + "usage": { "type": "string" }, + "tags": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "importance": { + "type": "string", + "enum": ["critical", "high", "medium", "low"], + "description": "How important this memory is for future context" + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Confidence in the accuracy of this memory" + }, + "tags": { + "type": "array", + "items": { "type": "string" } + }, + "related_files": { + "type": "array", + "items": { "type": "string" } + }, + "project_context": { + "type": "string", + "description": "Project name or identifier for scoping" + } + } + }, + "recommended_model": "claude-3-5-sonnet", + "recommended_provider": "anthropic", + "default_capture_stage": "post_response_async", + "default_frequency": "conversation_end", + "default_scope_type": "user", + "default_indexing_mode": "fts_and_vector", + "default_retrieval_mode": "hybrid", + "default_memory_type_mapping": { + "language_pref": "preference", + "stack_config": "preference", + "convention": "preference", + "architecture_decision": "insight", + "fix_pattern": "insight", + "debug_context": "observation", + "snippet": "domain_object", + "tool_config": "preference", + "learning": "insight" + }, + "ui_config": { + "display_fields": ["memory_type", "language", "title"], + "filter_fields": ["memory_type", "language", "importance", "tags"], + "sort_default": "-created_at", + "icon_map": { + "language_pref": "languages", + "stack_config": "layers", + "convention": "file-check", + "architecture_decision": "git-branch", + "fix_pattern": "bug", + "debug_context": "terminal", + "snippet": "code", + "tool_config": "settings", + "learning": "book-open" + }, + "color_map": { + "language_pref": "blue", + "stack_config": "purple", + "convention": "green", + "architecture_decision": "orange", + "fix_pattern": "red", + "debug_context": "gray", + "snippet": "cyan", + "tool_config": "slate", + "learning": "yellow" + } + }, + "help_text": { + "short": "Capture coding context, decisions, and patterns", + "long": "This profile captures structured programming knowledge including language preferences, stack configurations, coding conventions, architectural decisions, reusable fix patterns, debugging context, and code snippets. Use it to build a persistent technical memory that improves code assistance over time." + } +} diff --git a/profiles/travel_planning/README.md b/profiles/travel_planning/README.md new file mode 100644 index 00000000..62d18e40 --- /dev/null +++ b/profiles/travel_planning/README.md @@ -0,0 +1,146 @@ +# Travel Planning Memory Profile + +## Overview + +The Travel Planning Memory profile captures structured travel information to provide personalized recommendations and assistance throughout the trip planning process. + +## Use Cases + +- **Trip Planning**: Extract and store destination preferences, dates, budget, and traveler details +- **Booking Management**: Track confirmed reservations for flights, hotels, and activities +- **Personalization**: Remember preferences for accommodation types, activity styles, and dietary needs +- **Constraint Tracking**: Ensure must-see attractions, accessibility needs, and restrictions are respected + +## Schema Overview + +### Required Fields +- `destination` - Primary travel destination (city, country, or region) + +### Optional Fields + +#### Dates +| Field | Type | Description | +|-------|------|-------------| +| `start_date` | date | Trip start date (YYYY-MM-DD) | +| `end_date` | date | Trip end date (YYYY-MM-DD) | +| `duration_days` | integer | Total number of days | +| `flexibility` | enum | fixed, flexible_1_3_days, flexible_week, flexible_month, open | + +#### Travelers +| Field | Type | Description | +|-------|------|-------------| +| `count` | integer | Number of travelers | +| `composition` | array | solo, partner, family_with_children, friends, group, seniors, pets | +| `ages` | object | adults, children, infants counts | + +#### Budget +| Field | Type | Description | +|-------|------|-------------| +| `total_budget` | number | Total trip budget | +| `currency` | string | Currency code (USD, EUR, etc.) | +| `budget_tier` | enum | budget, mid_range, luxury, ultra_luxury | +| `per_person_max` | number | Maximum per person | + +#### Preferences +| Field | Type | Description | +|-------|------|-------------| +| `accommodation_type` | array | hotel, resort, airbnb, hostel, boutique, camping, glamping, all_inclusive | +| `room_preferences` | array | Specific room requirements | +| `activity_style` | array | relaxing, adventure, cultural, foodie, nightlife, nature, shopping, wellness | +| `pace` | enum | packed, moderate, relaxed, very_relaxed | +| `dietary_restrictions` | array | Dietary requirements | +| `accessibility_needs` | array | Accessibility accommodations | + +#### Purpose +| Field | Type | Description | +|-------|------|-------------| +| `primary` | enum | vacation, business, bleisure, honeymoon, anniversary, family_reunion, celebration, adventure, wellness, education | +| `secondary` | array | Additional purposes | +| `special_occasion` | string | Description if applicable | + +#### Constraints +| Field | Type | Description | +|-------|------|-------------| +| `must_visit` | array | Required attractions/locations | +| `must_avoid` | array | Places/activities to avoid | +| `transportation_preferences` | array | flight, train, car_rental, public_transit, walking, cruise, private_driver | +| `visa_requirements` | object | Visa needs and status | +| `health_requirements` | array | Vaccinations, medications | +| `time_constraints` | string | Time limitations | + +#### Booked Items +Array of confirmed bookings with type, name, confirmation number, date, and cost. + +## Capture Behavior + +### Default Settings +- **Capture Stage**: Post-response async (to avoid latency) +- **Trigger**: Conversation end +- **Scope**: Conversation (can be changed to user-scoped for persistent preferences) +- **Indexing**: FTS only (for text search) +- **Retrieval**: Injected into prompts + +### Confidence Levels +The extraction agent assigns confidence based on how explicitly the information was stated: +- **HIGH**: User explicitly stated +- **MEDIUM**: Strongly implied +- **LOW**: Weakly inferred + +## Usage Examples + +### Creating a Travel Memory + +When a user mentions travel plans, the memory agent extracts: + +``` +User: "I'm going to Japan for 2 weeks in April with my partner" +→ Extracts: destination=Japan, dates.duration_days=14, travelers.composition=[partner] + +User: "Budget is around $5000, we're staying at boutique hotels" +→ Extracts: budget.total_budget=5000, budget.currency=USD, preferences.accommodation_type=[boutique] +``` + +### Retrieving Travel Memories + +Memories can be retrieved by: +- Destination (e.g., all Japan trips) +- Date range (e.g., upcoming trips) +- Traveler composition (e.g., family trips) +- Purpose (e.g., honeymoons) + +### Memory Lifecycle + +1. **Planning Phase**: Memory created with available details +2. **Booking Phase**: `booked_items` populated as reservations are made +3. **Pre-trip Phase**: Memory retrieved for final recommendations +4. **Post-trip Phase**: Memory archived or updated with trip notes + +## Integration Tips + +### With Travel Agents +Enable this profile on travel planning agents to remember user preferences across sessions. + +### Scope Recommendations +- **Conversation scope**: For one-off trip planning +- **User scope**: For persistent travel preferences (frequent travelers) +- **Global scope**: For shared destination knowledge + +### Customization +Modify the schema to add domain-specific fields: +- Cruise-specific fields (ship, cabin type, ports) +- Business travel fields (company policy, loyalty programs) +- Adventure travel fields (gear requirements, fitness level) + +## File Structure + +``` +travel_planning/ +├── profile.json # Schema and configuration +├── capture_prompt.txt # Extraction instructions for the memory agent +├── example_records.json # Sample memory records +└── README.md # This documentation +``` + +## Version History + +- **1.0.0** - Initial profile with core travel planning fields diff --git a/profiles/travel_planning/capture-prompt.md b/profiles/travel_planning/capture-prompt.md new file mode 100644 index 00000000..bf29b5a7 --- /dev/null +++ b/profiles/travel_planning/capture-prompt.md @@ -0,0 +1,121 @@ +# Travel Planning Memory — Capture Prompt Template + +## Purpose +Extract structured travel planning information from natural conversation to populate the Travel Planning Memory profile. + +## Instructions + +You are a memory extraction assistant. Your task is to analyze the user's message and extract travel planning details into a structured format. Only extract information explicitly stated or strongly implied by the user. Do not invent or assume details not present in the conversation. + +## Extraction Rules + +1. **Be conservative**: Only extract what is explicitly stated or can be reasonably inferred +2. **Use null/undefined for missing fields**: Don't guess or fill in defaults +3. **Normalize formats**: + - Dates: Convert to ISO 8601 format (YYYY-MM-DD) when possible + - Numbers: Use integers for counts, numbers for currency + - Arrays: Use empty array `[]` for explicitly empty lists, omit for unknown +4. **Confidence scoring**: Rate extraction confidence (0.0-1.0) based on clarity +5. **Preserve user language**: Keep original terms for destinations, amenities, etc. + +## Field Extraction Guide + +### destination (required if mentioned) +- `primary`: Main destination city/country +- `secondary`: Additional stops or day trip destinations +- `region`: Broader geographic area + +### dates (required if mentioned) +- `check_in`: Arrival date in YYYY-MM-DD format +- `check_out`: Departure date in YYYY-MM-DD format +- `duration_nights`: Calculate if dates provided +- `flexibility`: Note if dates are fixed or flexible + +### travelers (required if mentioned) +- `adults`: Count of adult travelers +- `children`: Count of children +- `infants`: Count of infants under 2 +- `children_ages`: Extract ages if mentioned +- `group_type`: Classify as solo/couple/family/friends/business/mixed + +### budget (optional) +- `total_budget`: Total amount if stated +- `currency`: Currency code (USD, EUR, etc.) +- `budget_tier`: Map descriptions to budget/mid-range/luxury/ultra-luxury +- `accommodation_per_night`: Per-night budget if mentioned + +### preferences (optional) +- `accommodation_types`: Map to allowed types +- `activity_style`: Extract from activity descriptions +- `pace`: Determine from schedule descriptions +- `dietary_restrictions`: Note any mentioned +- `must_see`: List specific attractions mentioned +- `avoid`: Note anything they want to avoid + +### purpose (optional) +- `primary`: Main reason for travel +- `secondary`: Additional goals +- `special_occasion`: Specific celebration +- `occasion_date`: Date of occasion if different from trip + +### accommodation_constraints (optional) +- `room_configuration`: Specific room needs +- `amenities_required`: Must-haves +- `amenities_preferred`: Nice-to-haves +- `location_preference`: Preferred area type +- `accessibility_needs`: Any requirements +- `loyalty_programs`: Programs mentioned + +## Example Extractions + +### User Message: +"I'm planning a trip to Japan with my wife and our 5-year-old twins. We'll be there from April 5-15 for cherry blossom season. Looking for family-friendly hotels in Tokyo with a budget around $300/night." + +### Extracted: +```json +{ + "destination": { + "primary": "Tokyo", + "region": "Japan" + }, + "dates": { + "check_in": "2026-04-05", + "check_out": "2026-04-15", + "duration_nights": 10 + }, + "travelers": { + "adults": 2, + "children": 2, + "children_ages": [5, 5], + "group_type": "family" + }, + "budget": { + "currency": "USD", + "accommodation_per_night": 300, + "budget_tier": "mid-range" + }, + "preferences": { + "accommodation_types": ["hotel"], + "must_see": ["cherry blossoms"] + }, + "accommodation_constraints": { + "amenities_preferred": ["family-friendly"] + }, + "metadata": { + "confidence": 0.95 + } +} +``` + +## Output Format + +Return ONLY a valid JSON object matching the Travel Planning Memory schema. Do not include markdown code blocks, explanations, or additional text. + +If no travel planning information is present in the user's message, return: +```json +{"extracted": false, "reason": "No travel planning information detected"} +``` + +## Conversation Context + +Previous messages may contain additional context. Consider the conversation history when extracting, but prioritize the current message for new or updated information. diff --git a/profiles/travel_planning/capture_prompt.txt b/profiles/travel_planning/capture_prompt.txt new file mode 100644 index 00000000..26054bc1 --- /dev/null +++ b/profiles/travel_planning/capture_prompt.txt @@ -0,0 +1,55 @@ +You are a travel planning memory extraction agent. Your job is to identify and extract travel-related information from conversations and structure it according to the Travel Planning schema. + +## Extraction Guidelines + +### What to Capture +- Trip destinations (cities, countries, regions) +- Travel dates and duration +- Number and type of travelers +- Budget information (total or per-person) +- Accommodation preferences +- Activity interests and travel style +- Special occasions or purposes +- Constraints (must-see, must-avoid, accessibility needs) +- Already booked items + +### How to Extract +1. Read the conversation carefully for explicit mentions of travel details +2. Infer implicit information only when confidence is high +3. Use exact values when provided; estimate only when clearly indicated +4. Map free-form descriptions to schema enum values when applicable +5. Leave fields null/omitted if not mentioned or unclear + +### Confidence Levels +- HIGH: User explicitly stated the information +- MEDIUM: Strongly implied or partially stated +- LOW: Weakly implied or inferred from context + +### Output Format +Return a valid JSON object matching the Travel Planning schema. Only include fields with non-null values. Add a "_confidence" field at the top level with values: "high", "medium", or "low". + +### Special Handling +- Dates: Convert all dates to YYYY-MM-DD format. Use ISO 8601 for partial dates. +- Budget: Extract both numeric amounts and currency. Map descriptive terms ("cheap", "luxury") to budget_tier enums. +- Accommodation: Map descriptions like "4-star hotel" or "Airbnb" to accommodation_type enums. +- Activities: Map interests like "hiking" → "adventure", "museums" → "cultural". +- Travelers: Count adults/children when specified. Map descriptions like "with my kids" to composition. + +### Examples of Extraction + +User: "I'm planning a trip to Japan in April" +→ Capture: destination=Japan, dates.flexibility=flexible_month + +User: "Going to Paris for our honeymoon, budget around $5000" +→ Capture: destination=Paris, purpose.primary=honeymoon, budget.total_budget=5000, budget.currency=USD + +User: "Family of 4, need a place with pool, kids are 6 and 8" +→ Capture: travelers.count=4, travelers.composition=[family_with_children], travelers.ages.adults=2, travelers.ages.children=2, preferences.room_preferences=[pool access] + +### Exclusion Rules +- Do NOT capture personal identification documents +- Do NOT capture exact payment card information +- Do NOT capture precise home addresses (city/region is fine) +- Do NOT capture passwords or booking credentials + +Always prioritize accuracy over completeness. A smaller, accurate extraction is better than a complete but speculative one. diff --git a/profiles/travel_planning/example_records.json b/profiles/travel_planning/example_records.json new file mode 100644 index 00000000..d3530757 --- /dev/null +++ b/profiles/travel_planning/example_records.json @@ -0,0 +1,226 @@ +[ + { + "record_id": "mem_travel_001", + "title": "Tokyo Spring Trip 2026", + "source_type": "conversation", + "producer_mode": "memory_agent", + "memory_type": "domain_object", + "scope_type": "user", + "scope_key": "user_12345", + "status": "active", + "confidence": 0.95, + "importance_score": 0.8, + "data_json": { + "destination": "Tokyo, Japan", + "dates": { + "start_date": "2026-04-05", + "end_date": "2026-04-15", + "duration_days": 10, + "flexibility": "fixed" + }, + "travelers": { + "count": 2, + "composition": ["partner"], + "ages": { + "adults": 2, + "children": 0, + "infants": 0 + } + }, + "budget": { + "total_budget": 6000, + "currency": "USD", + "budget_tier": "mid_range", + "per_person_max": 3000 + }, + "preferences": { + "accommodation_type": ["hotel", "boutique"], + "room_preferences": ["non-smoking", "city view", "near subway"], + "activity_style": ["cultural", "foodie", "nature"], + "pace": "moderate", + "dietary_restrictions": ["pescatarian"], + "accessibility_needs": [] + }, + "purpose": { + "primary": "vacation", + "secondary": ["anniversary"], + "special_occasion": "10-year anniversary celebration" + }, + "constraints": { + "must_visit": ["Mount Fuji day trip", "Tsukiji Outer Market", "teamLab Planets"], + "must_avoid": ["crowded tourist buses", "chain restaurants"], + "transportation_preferences": ["train", "walking"], + "visa_requirements": { + "needs_visa": false, + "visa_status": "visa-free for 90 days" + }, + "health_requirements": ["COVID vaccination certificate"], + "time_constraints": null + }, + "booked_items": [ + { + "type": "flight", + "name": "United Airlines UA881", + "confirmation": "ABC123", + "date": "2026-04-05", + "cost": 1200 + }, + { + "type": "hotel", + "name": "Shibuya Excel Hotel Tokyu", + "confirmation": "HTL789", + "date": "2026-04-05 to 2026-04-15", + "cost": 2100 + } + ], + "itinerary_notes": "First time in Japan. Interested in cherry blossoms, ramen tours, and onsen experience. Prefers walking neighborhoods over major tourist sites." + }, + "summary_text": "10-day anniversary trip to Tokyo for 2 adults in April 2026. Mid-range budget, cultural and foodie interests. Flights and hotel booked.", + "tags": ["anniversary", "japan", "tokyo", "spring", "booked"], + "created_at": "2026-02-15T10:30:00Z", + "last_retrieved_at": "2026-03-01T14:22:00Z", + "retrieval_count": 5 + }, + { + "record_id": "mem_travel_002", + "title": "Bali Family Vacation", + "source_type": "conversation", + "producer_mode": "memory_agent", + "memory_type": "domain_object", + "scope_type": "conversation", + "scope_key": "conv_98765", + "status": "active", + "confidence": 0.88, + "importance_score": 0.7, + "data_json": { + "destination": "Bali, Indonesia", + "dates": { + "start_date": null, + "end_date": null, + "duration_days": 14, + "flexibility": "flexible_1_3_days" + }, + "travelers": { + "count": 4, + "composition": ["family_with_children"], + "ages": { + "adults": 2, + "children": 2, + "infants": 0 + } + }, + "budget": { + "total_budget": 8000, + "currency": "USD", + "budget_tier": "mid_range", + "per_person_max": 2000 + }, + "preferences": { + "accommodation_type": ["villa", "resort"], + "room_preferences": ["private pool", "kid-friendly", "kitchen"], + "activity_style": ["relaxing", "nature", "wellness"], + "pace": "relaxed", + "dietary_restrictions": ["gluten-free", "dairy-free"], + "accessibility_needs": ["stroller accessible"] + }, + "purpose": { + "primary": "vacation", + "secondary": ["family_reunion"], + "special_occasion": null + }, + "constraints": { + "must_visit": ["Ubud Monkey Forest", "beach clubs in Seminyak"], + "must_avoid": ["crowded day tours", "long car rides with kids"], + "transportation_preferences": ["private_driver", "walking"], + "visa_requirements": { + "needs_visa": true, + "visa_status": "visa on arrival" + }, + "health_requirements": ["travel insurance required"], + "time_constraints": "avoid school holidays" + }, + "booked_items": [], + "itinerary_notes": "Kids are 4 and 7 years old. Need activities suitable for young children. Interested in family yoga, cooking class, and gentle nature walks." + }, + "summary_text": "2-week family vacation to Bali for family of 4. Mid-range budget, relaxed pace. Looking for kid-friendly villa with pool. Dates flexible within a few days.", + "tags": ["bali", "family", "kids", "villa", "planning"], + "created_at": "2026-03-10T09:15:00Z", + "last_retrieved_at": null, + "retrieval_count": 0 + }, + { + "record_id": "mem_travel_003", + "title": "Iceland Northern Lights Adventure", + "source_type": "run", + "producer_mode": "post_run_llm", + "memory_type": "domain_object", + "scope_type": "global", + "scope_key": "iceland_trips", + "status": "active", + "confidence": 0.92, + "importance_score": 0.75, + "data_json": { + "destination": "Iceland", + "dates": { + "start_date": "2026-11-20", + "end_date": "2026-11-27", + "duration_days": 7, + "flexibility": "fixed" + }, + "travelers": { + "count": 1, + "composition": ["solo"], + "ages": { + "adults": 1, + "children": 0, + "infants": 0 + } + }, + "budget": { + "total_budget": 4500, + "currency": "USD", + "budget_tier": "mid_range", + "per_person_max": 4500 + }, + "preferences": { + "accommodation_type": ["hostel", "camping"], + "room_preferences": ["shared kitchen access", "northern lights viewing spot"], + "activity_style": ["adventure", "nature", "photography"], + "pace": "packed", + "dietary_restrictions": ["vegetarian"], + "accessibility_needs": [] + }, + "purpose": { + "primary": "adventure", + "secondary": ["education"], + "special_occasion": "Bucket list photography trip" + }, + "constraints": { + "must_visit": ["Blue Lagoon", "Golden Circle", "Jökulsárlón glacier lagoon", "northern lights viewing"], + "must_avoid": ["tourist bus tours", "guided group activities"], + "transportation_preferences": ["car_rental"], + "visa_requirements": { + "needs_visa": false, + "visa_status": "Schengen - visa free for 90 days" + }, + "health_requirements": ["warm weather gear required", "travel insurance with emergency evacuation"], + "time_constraints": "November only for northern lights season" + }, + "booked_items": [ + { + "type": "car", + "name": "4x4 Rental - Budget Iceland", + "confirmation": "CAR456", + "date": "2026-11-20 to 2026-11-27", + "cost": 800 + } + ], + "itinerary_notes": "Solo photography-focused trip. Planning to chase auroras and capture landscape shots. Comfortable with cold weather. Renting camper van for flexibility." + }, + "summary_text": "7-day solo adventure to Iceland in November 2026 for northern lights and photography. Mid-range budget, camper van rental booked. Packed itinerary.", + "tags": ["iceland", "solo", "photography", "northern_lights", "camper_van"], + "created_at": "2026-03-20T16:45:00Z", + "last_retrieved_at": "2026-03-22T11:30:00Z", + "retrieval_count": 2 + } +] diff --git a/profiles/travel_planning/examples.json b/profiles/travel_planning/examples.json new file mode 100644 index 00000000..deedf9cb --- /dev/null +++ b/profiles/travel_planning/examples.json @@ -0,0 +1,180 @@ +{ + "examples": [ + { + "id": "example-001", + "title": "Family Spring Break to Orlando", + "description": "A family of four planning a theme park vacation to Orlando during spring break", + "data": { + "destination": { + "primary": "Orlando, Florida", + "region": "United States" + }, + "dates": { + "check_in": "2026-03-15", + "check_out": "2026-03-22", + "duration_nights": 7, + "flexibility": "±1-2 days" + }, + "travelers": { + "adults": 2, + "children": 2, + "children_ages": [8, 11], + "group_type": "family" + }, + "budget": { + "total_budget": 8000, + "currency": "USD", + "budget_tier": "mid-range", + "accommodation_per_night": 250, + "notes": "Trying to keep total under $8000 including flights, hotels, and park tickets" + }, + "preferences": { + "accommodation_types": ["hotel", "resort"], + "activity_style": ["adventure", "relaxation"], + "pace": "balanced", + "must_see": ["Magic Kingdom", "Universal Studios", "Harry Potter World"], + "avoid": ["long lines", "crowded restaurants"] + }, + "purpose": { + "primary": "leisure", + "secondary": ["family bonding", "celebration"], + "special_occasion": "Daughter's 11th birthday" + }, + "accommodation_constraints": { + "room_configuration": ["2 connecting rooms or family suite"], + "amenities_required": ["pool", "wifi", "parking"], + "amenities_preferred": ["breakfast", "kids_club", "shuttle to parks"], + "location_preference": "near_attractions" + }, + "metadata": { + "created_at": "2026-02-10T14:30:00Z", + "updated_at": "2026-02-15T09:15:00Z", + "source": "chat_conversation", + "confidence": 0.92, + "status": "planning" + } + } + }, + { + "id": "example-002", + "title": "Solo Backpacking Through Southeast Asia", + "description": "A solo traveler planning a budget backpacking adventure across multiple Southeast Asian countries", + "data": { + "destination": { + "primary": "Thailand", + "secondary": ["Vietnam", "Cambodia", "Laos"], + "region": "Southeast Asia" + }, + "dates": { + "check_in": "2026-06-01", + "check_out": "2026-07-31", + "duration_nights": 60, + "flexibility": "flexible" + }, + "travelers": { + "adults": 1, + "children": 0, + "infants": 0, + "group_type": "solo" + }, + "budget": { + "total_budget": 3500, + "currency": "USD", + "budget_tier": "budget", + "accommodation_per_night": 25, + "notes": "Targeting $50/day total including accommodation, food, and local transport" + }, + "preferences": { + "accommodation_types": ["hostel", "vacation_rental"], + "activity_style": ["cultural", "adventure", "food_wine"], + "pace": "packed", + "dietary_restrictions": ["vegetarian"], + "must_see": ["Angkor Wat", "Ha Long Bay", "Chiang Mai temples", "street food markets"], + "avoid": ["tourist traps", "expensive restaurants", "organized tours"] + }, + "purpose": { + "primary": "cultural_exploration", + "secondary": ["adventure", "food tourism", "personal growth"] + }, + "accommodation_constraints": { + "room_configuration": ["dorm bed or private room in hostel"], + "amenities_required": ["wifi", "laundry"], + "amenities_preferred": ["kitchen", "common area", "bike rental"], + "location_preference": "near_transit" + }, + "metadata": { + "created_at": "2026-01-20T10:00:00Z", + "updated_at": "2026-03-01T16:45:00Z", + "source": "voice_conversation", + "confidence": 0.88, + "status": "planning" + } + } + }, + { + "id": "example-003", + "title": "Luxury Anniversary Trip to Santorini", + "description": "A couple celebrating their 25th wedding anniversary with a luxury getaway to Greece", + "data": { + "destination": { + "primary": "Santorini", + "secondary": ["Athens"], + "region": "Greece" + }, + "dates": { + "check_in": "2026-09-12", + "check_out": "2026-09-19", + "duration_nights": 7, + "flexibility": "fixed" + }, + "travelers": { + "adults": 2, + "children": 0, + "group_type": "couple" + }, + "budget": { + "total_budget": 15000, + "currency": "USD", + "budget_tier": "luxury", + "accommodation_per_night": 800, + "notes": "Splurging for the 25th anniversary, no strict budget within reason" + }, + "preferences": { + "accommodation_types": ["boutique", "resort"], + "activity_style": ["relaxation", "cultural", "food_wine"], + "pace": "relaxed", + "must_see": ["Oia sunset", "wine tasting", "Akrotiri ruins", "private boat tour"], + "avoid": ["crowds", "packed schedules", "budget accommodations"] + }, + "purpose": { + "primary": "celebration", + "special_occasion": "25th wedding anniversary", + "occasion_date": "2026-09-15" + }, + "accommodation_constraints": { + "room_configuration": ["suite with caldera view"], + "amenities_required": ["wifi", "pool", "spa", "breakfast"], + "amenities_preferred": ["private terrace", "jacuzzi", "concierge"], + "location_preference": "quiet_area", + "loyalty_programs": ["Marriott Bonvoy"] + }, + "metadata": { + "created_at": "2025-12-01T09:00:00Z", + "updated_at": "2026-03-20T11:30:00Z", + "source": "chat_conversation", + "confidence": 0.96, + "status": "booked" + } + } + } + ], + "_meta": { + "version": "1.0.0", + "description": "Example memory records demonstrating various travel planning scenarios", + "coverage": [ + "Family vacation with children", + "Solo backpacking on a budget", + "Luxury couple's celebration" + ] + } +} diff --git a/profiles/travel_planning/profile.json b/profiles/travel_planning/profile.json new file mode 100644 index 00000000..8941e9f8 --- /dev/null +++ b/profiles/travel_planning/profile.json @@ -0,0 +1,227 @@ +{ + "profile_name": "Travel Planning Memory", + "description": "Captures travel plans, trip details, and preferences for personalized travel assistance", + "category": "lifestyle", + "version": "1.0.0", + "is_system_profile": true, + "icon": "✈️", + "default_schema_json": { + "type": "object", + "required": ["destination"], + "properties": { + "destination": { + "type": "string", + "description": "Primary travel destination (city, country, or region)" + }, + "dates": { + "type": "object", + "properties": { + "start_date": { + "type": "string", + "format": "date", + "description": "Trip start date (YYYY-MM-DD)" + }, + "end_date": { + "type": "string", + "format": "date", + "description": "Trip end date (YYYY-MM-DD)" + }, + "duration_days": { + "type": "integer", + "description": "Total number of days for the trip" + }, + "flexibility": { + "type": "string", + "enum": ["fixed", "flexible_1_3_days", "flexible_week", "flexible_month", "open"], + "description": "How flexible the travel dates are" + } + } + }, + "travelers": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Number of travelers" + }, + "composition": { + "type": "array", + "items": { + "type": "string", + "enum": ["solo", "partner", "family_with_children", "friends", "group", "seniors", "pets"] + }, + "description": "Composition of travel party" + }, + "ages": { + "type": "object", + "properties": { + "adults": { "type": "integer" }, + "children": { "type": "integer" }, + "infants": { "type": "integer" } + } + } + } + }, + "budget": { + "type": "object", + "properties": { + "total_budget": { + "type": "number", + "description": "Total trip budget in local currency" + }, + "currency": { + "type": "string", + "description": "Currency code (USD, EUR, etc.)" + }, + "budget_tier": { + "type": "string", + "enum": ["budget", "mid_range", "luxury", "ultra_luxury"], + "description": "General budget category" + }, + "per_person_max": { + "type": "number", + "description": "Maximum budget per person" + } + } + }, + "preferences": { + "type": "object", + "properties": { + "accommodation_type": { + "type": "array", + "items": { + "type": "string", + "enum": ["hotel", "resort", "airbnb", "hostel", "boutique", "camping", "glamping", "all_inclusive"] + } + }, + "room_preferences": { + "type": "array", + "items": { + "type": "string" + }, + "description": "e.g., 'ocean view', 'king bed', 'ground floor'" + }, + "activity_style": { + "type": "array", + "items": { + "type": "string", + "enum": ["relaxing", "adventure", "cultural", "foodie", "nightlife", "nature", "shopping", "wellness"] + } + }, + "pace": { + "type": "string", + "enum": ["packed", "moderate", "relaxed", "very_relaxed"], + "description": "Preferred trip pace" + }, + "dietary_restrictions": { + "type": "array", + "items": { "type": "string" } + }, + "accessibility_needs": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "purpose": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "enum": ["vacation", "business", "bleisure", "honeymoon", "anniversary", "family_reunion", "celebration", "adventure", "wellness", "education"] + }, + "secondary": { + "type": "array", + "items": { "type": "string" } + }, + "special_occasion": { + "type": "string", + "description": "Description of special occasion if applicable" + } + } + }, + "constraints": { + "type": "object", + "properties": { + "must_visit": { + "type": "array", + "items": { "type": "string" }, + "description": "Must-see attractions or locations" + }, + "must_avoid": { + "type": "array", + "items": { "type": "string" }, + "description": "Places or activities to avoid" + }, + "transportation_preferences": { + "type": "array", + "items": { + "type": "string", + "enum": ["flight", "train", "car_rental", "public_transit", "walking", "cruise", "private_driver"] + } + }, + "visa_requirements": { + "type": "object", + "properties": { + "needs_visa": { "type": "boolean" }, + "visa_status": { "type": "string" } + } + }, + "health_requirements": { + "type": "array", + "items": { "type": "string" }, + "description": "Vaccinations, medications, etc." + }, + "time_constraints": { + "type": "string", + "description": "Specific time limitations or restrictions" + } + } + }, + "booked_items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["flight", "hotel", "car", "tour", "activity", "restaurant", "insurance"] + }, + "name": { "type": "string" }, + "confirmation": { "type": "string" }, + "date": { "type": "string" }, + "cost": { "type": "number" } + } + } + }, + "itinerary_notes": { + "type": "string", + "description": "Additional itinerary details or notes" + } + } + }, + "recommended_model": "gpt-4o-mini", + "recommended_provider": "openai", + "default_capture_stage": "post_response_async", + "default_frequency": "conversation_end", + "default_scope_type": "conversation", + "default_indexing_mode": "fts_only", + "default_retrieval_mode": "inject", + "default_memory_type_mapping": "domain_object", + "ui_labels": { + "title": "Travel Planning Memory", + "description": "Stores trip details, preferences, and constraints", + "create_button": "Record Trip Details", + "view_button": "View Travel Memories" + }, + "help_text": { + "overview": "This profile captures structured travel information to provide personalized recommendations and assistance throughout the trip planning process.", + "key_fields": [ + "Destination is required; all other fields are optional", + "Dates can be specific or flexible", + "Budget can be total or per-person", + "Preferences help tailor recommendations", + "Constraints ensure important requirements aren't missed" + ] + } +} diff --git a/tech_specs/CAPTURE_RETRIEVAL.md b/tech_specs/CAPTURE_RETRIEVAL.md new file mode 100644 index 00000000..24e9e50c --- /dev/null +++ b/tech_specs/CAPTURE_RETRIEVAL.md @@ -0,0 +1,729 @@ +# Capture & Retrieval Technical Specifications + +This document specifies the technical implementation for HUF's memory capture modes, trigger system, and retrieval model. + +--- + +## 1. Capture Modes + +Capture modes define **who performs the extraction** and **when** during the request lifecycle. + +### 1.1 In-Prompt Capture + +| Attribute | Specification | +|-----------|---------------| +| **Mode ID** | `in_prompt` | +| **Execution Phase** | During main agent inference | +| **Latency Impact** | Zero (part of main request) | +| **Producer** | Main agent | + +**Implementation:** +- Memory instructions are prepended to the main agent system prompt +- Agent outputs memory updates as part of its JSON response structure +- Memory fields are extracted from the response and committed synchronously +- Schema validation occurs post-response before write + +**Best For:** +- Low-latency session state tracking +- Simple preference updates +- Domain-specific fields where prompt engineering is mature + +**Configuration:** +```json +{ + "capture_mode": "in_prompt", + "capture_prompt": "System instructions for what to extract...", + "schema_json": { "type": "object", "properties": {...} }, + "require_json_schema_match": true, + "allow_open_schema": false +} +``` + +--- + +### 1.2 Post-Response Synchronous Capture + +| Attribute | Specification | +|-----------|---------------| +| **Mode ID** | `post_response_sync` | +| **Execution Phase** | After main response, before returning to user | +| **Latency Impact** | High (blocks user response) | +| **Producer** | Main agent or memory agent | + +**Implementation:** +- Main agent response is returned to controller +- Capture prompt + conversation context is sent to extraction model +- Memory record is created/updated synchronously +- User receives response only after memory commit succeeds + +**Flow:** +``` +User Input → Main Agent → Response Ready → Extract Memory → Commit → Return to User +``` + +**Best For:** +- Critical profile updates requiring consistency +- Highly structured extractions with strict schemas +- Low-volume, high-importance memory types + +**Configuration:** +```json +{ + "capture_mode": "post_response_sync", + "capture_agent": null, + "timeout_seconds": 10, + "fallback_on_error": "skip" +} +``` + +--- + +### 1.3 Post-Response Asynchronous Capture + +| Attribute | Specification | +|-----------|---------------| +| **Mode ID** | `post_response_async` | +| **Execution Phase** | Background job after user response sent | +| **Latency Impact** | Zero (non-blocking) | +| **Producer** | Main agent, memory agent, or post-run processor | + +**Implementation:** +- User response is returned immediately +- Capture job is enqueued to background queue (RQ/background job) +- Job runs with conversation context snapshot +- Memory record creation is eventual consistency +- Failure is logged but does not block user + +**Flow:** +``` +User Input → Main Agent → Return Response → Queue Capture Job → Process Async +``` + +**Best For:** +- Travel/itinerary capture +- CRM enrichment +- Long conversation summarization +- Learned insight extraction +- Default mode for most profiles + +**Configuration:** +```json +{ + "capture_mode": "post_response_async", + "capture_agent": "memory-extractor-v1", + "queue_name": "memory_capture", + "retry_count": 3, + "max_context_turns": 20 +} +``` + +--- + +### 1.4 Specialized Memory Agent + +| Attribute | Specification | +|-----------|---------------| +| **Mode ID** | `specialized_agent` | +| **Execution Phase** | Configurable (sync or async) | +| **Latency Impact** | Varies by execution phase | +| **Producer** | Dedicated memory agent instance | + +**Implementation:** +- Memory agent is a separate Agent record with specialized prompt +- May use cheaper/faster model optimized for extraction +- Receives conversation context + extraction instructions +- Returns structured memory payload +- Can run sync or async depending on trigger + +**Agent Configuration:** +- `is_memory_agent: true` — marks agent as special-purpose +- Optimized system prompt for extraction tasks +- Optional model override (e.g., gpt-4o-mini for cost efficiency) +- Fixed output schema via response_format + +**Best For:** +- Strict schema compliance +- Domain-specific extraction requiring different reasoning +- Cost optimization (cheaper model for extraction) +- Separation of concerns (main agent focuses on user, memory agent on storage) + +**Configuration:** +```json +{ + "capture_mode": "specialized_agent", + "memory_agent": "extractor-agent-uuid", + "execution_timing": "post_response_async", + "pass_full_history": true, + "pass_summary_only": false +} +``` + +--- + +### 1.5 Rule-Only Capture + +| Attribute | Specification | +|-----------|---------------| +| **Mode ID** | `rules_only` | +| **Execution Phase** | Deterministic (sync) | +| **Latency Impact** | Minimal (no LLM call) | +| **Producer** | Rule engine | + +**Implementation:** +- No LLM inference for extraction +- Memory fields populated from: + - Exact context values (user_id, timestamp, state flags) + - Regex/template extractions + - Tool call outputs + - System event payloads +- JSON Path or Jinja2 template mapping +- Direct commit to memory record + +**Rule Types:** +1. **Static:** Fixed value assignment +2. **Context:** Extract from conversation/run context +3. **Regex:** Pattern match on user/agent messages +4. **Tool:** Capture tool inputs/outputs +5. **Computed:** Derived from other fields + +**Best For:** +- Exact identifiers (user_id, conversation_id) +- Timestamps and versioning +- State transitions (ticket opened → closed) +- System-generated plans/events +- Deterministic metadata capture + +**Configuration:** +```json +{ + "capture_mode": "rules_only", + "rules": [ + { "field": "user_id", "source": "context", "path": "user.id" }, + { "field": "captured_at", "source": "static", "value": "{{ now() }}" }, + { "field": "intent", "source": "regex", "pattern": "book.*flight", "on_match": "flight_booking" } + ] +} +``` + +--- + +## 2. Trigger System + +Triggers define **when** capture is executed. Each trigger maps to a lifecycle event. + +### 2.1 Trigger Types + +| Trigger ID | Description | Execution Timing | +|------------|-------------|------------------| +| `every_run` | Every agent run/turn | Post-response | +| `every_n_runs` | Every N runs (configurable) | Post-response | +| `every_n_turns` | Every N turns in conversation | Post-response | +| `after_tool_call` | After specific tool execution | Post-tool | +| `final_response_only` | Only on final assistant response | Post-response | +| `conversation_end` | When conversation is marked complete | End event | +| `idle_timeout` | After inactivity threshold | Background job | +| `manual` | Explicit user/admin trigger | On-demand | +| `scheduled` | Cron-based consolidation | Scheduled job | + +--- + +### 2.2 Every Run + +**Trigger ID:** `every_run` + +**Implementation:** +- Fire after every non-error agent response +- Configurable debounce (min_seconds_between) +- Deduplication via hash of context snapshot + +**Use Cases:** +- Session state tracking +- Continuous preference learning +- High-granularity observation capture + +--- + +### 2.3 Every N Runs + +**Trigger ID:** `every_n_runs` + +**Implementation:** +- Counter maintained on Agent Run record +- Fire when `run_count % N == 0` +- Counter resets per conversation or global (configurable) + +**Parameters:** +```json +{ + "trigger_type": "every_n_runs", + "frequency_value": 5, + "counter_scope": "conversation" +} +``` + +**Use Cases:** +- Periodic summarization +- Batch insight extraction +- Reducing capture frequency for cost control + +--- + +### 2.4 Every N Turns + +**Trigger ID:** `every_n_turns` + +**Implementation:** +- Counter maintained on Agent Conversation record +- Fire when `turn_count % N == 0` +- Turn counting includes user + assistant exchanges + +**Parameters:** +```json +{ + "trigger_type": "every_n_turns", + "frequency_value": 10, + "count_both_roles": true +} +``` + +**Use Cases:** +- Chunked conversation summarization +- Progressive profile building +- Milestone-based capture + +--- + +### 2.5 After Tool Call + +**Trigger ID:** `after_tool_call` + +**Implementation:** +- Registered tool names trigger capture +- Tool input/output included in context +- Fire after tool completes, before final response + +**Parameters:** +```json +{ + "trigger_type": "after_tool_call", + "watched_tools": ["search_flights", "book_hotel"], + "capture_tool_output": true +} +``` + +**Use Cases:** +- Structured booking capture after travel tool use +- CRM updates after customer lookup +- State capture after significant actions + +--- + +### 2.6 Final Response Only + +**Trigger ID:** `final_response_only` + +**Implementation:** +- Fire only on assistant message (not tool calls) +- Skip intermediate tool-turns in multi-step runs + +**Use Cases:** +- Summary capture only +- Reducing noise from tool-only turns +- Final state capture for workflows + +--- + +### 2.7 Conversation End + +**Trigger ID:** `conversation_end` + +**Implementation:** +- Triggered by conversation state change to `ended` +- Can be synchronous or queued for processing +- Always captures full conversation context + +**End Detection Strategies:** + +| Strategy | Detection Method | +|----------|------------------| +| `manual_close` | User/admin explicitly closes conversation | +| `idle_timeout` | No activity for `idle_timeout_minutes` | +| `heuristic` | Agent classifies conversation as complete | +| `workflow_complete` | Workflow reaches terminal state | + +**Configuration:** +```json +{ + "trigger_type": "conversation_end", + "end_strategy": "idle_timeout", + "idle_timeout_minutes": 30, + "capture_full_summary": true +} +``` + +**Use Cases:** +- Final summarization +- Complete profile extraction +- Conversation archival +- Post-conversation analytics + +--- + +### 2.8 Idle Timeout + +**Trigger ID:** `idle_timeout` + +**Implementation:** +- Background job checks for idle conversations +- Fire on timeout (conversation not explicitly closed) +- May auto-close conversation or just capture state + +**Configuration:** +```json +{ + "trigger_type": "idle_timeout", + "idle_timeout_minutes": 30, + "auto_close_on_timeout": true, + "capture_before_close": true +} +``` + +**Use Cases:** +- Capturing abandoned conversations +- Session cleanup with memory preservation +- Timeout-based state snapshots + +--- + +### 2.9 Manual Trigger + +**Trigger ID:** `manual` + +**Implementation:** +- Explicit API endpoint `/api/memory/capture` +- UI button "Capture Memory Now" +- Admin/operator initiated + +**Parameters:** +```json +{ + "trigger_type": "manual", + "conversation_id": "conv_xxx", + "capture_mode": "specialized_agent" +} +``` + +**Use Cases:** +- On-demand capture +- Admin override +- Testing and debugging + +--- + +### 2.10 Scheduled Consolidation + +**Trigger ID:** `scheduled` + +**Implementation:** +- Cron-based trigger via HUF scheduler +- Processes batches of memory records +- Typically used for Phase 2+ operations (merge, dedupe) + +**Configuration:** +```json +{ + "trigger_type": "scheduled", + "cron_expression": "0 2 * * *", + "operation": "consolidate", + "scope_filter": { "memory_type": "observation" } +} +``` + +**Use Cases:** +- Daily memory consolidation +- Batch deduplication +- Expiry and pruning +- Reflection and synthesis + +--- + +### 2.11 Trigger Selection Matrix + +| If you need... | Use Trigger | +|----------------|-------------| +| Always capture | `every_run` | +| Cost-conscious periodic capture | `every_n_runs` / `every_n_turns` | +| Action-driven capture | `after_tool_call` | +| Summary only | `final_response_only` | +| Complete conversation capture | `conversation_end` | +| Handle abandonment | `idle_timeout` | +| On-demand | `manual` | +| Batch processing | `scheduled` | + +--- + +## 3. Retrieval Model + +The retrieval model defines **how memory is accessed** during agent execution. + +### 3.1 Retrieval Modes + +| Mode ID | Description | +|---------|-------------| +| `inject` | Memory auto-injected into system prompt | +| `tool_only` | Agent must call tool to query memory | +| `hybrid` | Top-K memory injected + full search via tool | + +--- + +### 3.2 Inject Mode + +**Mode ID:** `inject` + +**Implementation:** +- Query memory store at conversation start and/or before each run +- Format results as structured context block +- Prepend to system prompt (before knowledge, after core instructions) + +**Query Construction:** +```python +filters = { + "scope_type": policy.scope_type, + "scope_key": resolve_scope_key(policy, conversation), + "memory_type": policy.memory_types or None, + "status": "active", + "effective_date": "<= now" +} +results = memory_store.search( + filters=filters, + order_by=["-importance_score", "-last_retrieved_at", "-created_at"], + limit=policy.max_items_to_inject +) +``` + +**Context Format:** +```markdown +## Relevant Memory + +### Profile: User Preferences +- Language: English +- Timezone: America/New_York + +### Session: Travel Planning +- Destination: Tokyo +- Dates: 2024-05-01 to 2024-05-10 +``` + +**Budgeting:** +- `max_items_to_inject`: Hard limit on memory records +- `max_tokens_to_inject`: Soft limit via token estimation +- Priority ranking: importance_score × recency_weight × retrieval_count_decay + +**Best For:** +- High-priority profile data +- Small, stable context sets +- Latency-sensitive applications + +--- + +### 3.3 Tool-Only Mode + +**Mode ID:** `tool_only` + +**Implementation:** +- No automatic injection +- Memory search tool available to agent +- Agent explicitly queries when needed + +**Tool Definition:** +```json +{ + "name": "memory_search", + "description": "Search memory records for relevant context", + "parameters": { + "query": "Search query text", + "memory_type": "Optional filter by type", + "scope": "conversation|user|agent|global", + "limit": 10, + "min_confidence": 0.5 + } +} +``` + +**Tool Response:** +```json +{ + "results": [ + { + "memory_id": "mem_xxx", + "title": "User prefers formal tone", + "data": { "tone_preference": "formal" }, + "confidence": 0.95, + "source_conversation": "conv_xxx" + } + ] +} +``` + +**Best For:** +- Large memory corpora +- Rarely-needed historical data +- Memory as secondary knowledge source +- Agent-controlled retrieval strategy + +--- + +### 3.4 Hybrid Mode + +**Mode ID:** `hybrid` + +**Implementation:** +- Top-K high-priority memory auto-injected +- Full memory search tool also available +- Injected memory marked to avoid double-retrieval + +**Configuration:** +```json +{ + "retrieval_mode": "hybrid", + "inject": { + "max_items": 5, + "selection": "highest_importance" + }, + "tool": { + "enabled": true, + "default_limit": 10 + }, + "deduplicate": true +} +``` + +**Deduplication:** +- Injected memory IDs passed to tool context +- Tool excludes already-injected IDs from results +- Or tool results include `already_injected` flag + +**Best For:** +- Balanced approach +- Profile injection + searchable archive +- Most general-purpose configuration + +--- + +### 3.5 Retrieval Filters + +All retrieval modes support filtering: + +| Filter | Description | +|--------|-------------| +| `scope_type` | conversation / user / agent / namespace / global | +| `scope_key` | Specific scope identifier | +| `agent` | Source agent filter | +| `user` | Source user filter | +| `memory_type` | profile / preference / fact / plan / observation / insight / domain_object | +| `profile_name` | Specific memory profile | +| `tags` | Array match (any or all) | +| `status` | active / superseded / archived / expired | +| `min_confidence` | Confidence threshold | +| `min_importance` | Importance threshold | +| `created_after` | Recency filter | +| `effective_date` | Current validity filter | +| `source_type` | conversation / run / manual / event / scheduled | + +--- + +### 3.6 Retrieval Ranking (Phase 1) + +**Base Score:** +``` +score = importance_score × 0.4 + + recency_weight × 0.3 + + scope_relevance × 0.2 + + (1 / (1 + retrieval_count)) × 0.1 +``` + +**Components:** +- `importance_score`: 0.0-1.0, explicit or derived +- `recency_weight`: exponential decay from creation date +- `scope_relevance`: exact match = 1.0, parent scope = 0.8, global = 0.5 +- `retrieval_count_decay`: penalize over-fetched items + +**Backend Relevance:** +- FTS: BM25 score normalized +- Vector: cosine similarity +- Hybrid: weighted combination + +--- + +### 3.7 Configuration Summary + +```json +{ + "retrieval": { + "mode": "hybrid", + "inject": { + "max_items": 5, + "max_tokens": 2000, + "order_by": ["-importance_score", "-created_at"] + }, + "tool": { + "enabled": true, + "default_limit": 10, + "max_limit": 100 + }, + "filters": { + "default_status": ["active"], + "default_scope": "inherit_from_conversation" + } + } +} +``` + +--- + +## 4. Mode + Trigger + Retrieval Combinations + +### Recommended Configurations + +| Use Case | Capture Mode | Trigger | Retrieval | +|----------|--------------|---------|-----------| +| Session state | `in_prompt` | `every_run` | `inject` | +| User profile | `post_response_sync` | `every_n_runs` | `inject` | +| Travel capture | `specialized_agent` + async | `conversation_end` | `hybrid` | +| CRM enrichment | `post_response_async` | `after_tool_call` | `tool_only` | +| Learning/insights | `specialized_agent` + async | `every_n_turns` | `hybrid` | +| System events | `rules_only` | `manual` | `inject` | +| Documentation | `post_response_async` | `conversation_end` | `tool_only` | + +--- + +## 5. Implementation Notes + +### Priority Order + +1. **Mode** determines **who extracts** +2. **Trigger** determines **when** extraction runs +3. **Retrieval** determines **how** memory is consumed + +### Error Handling + +| Mode | Error Behavior | +|------|----------------| +| `in_prompt` | Validation error → skip, log warning | +| `post_response_sync` | Configurable: skip / retry / fail request | +| `post_response_async` | Queue retry with backoff, alert after N failures | +| `specialized_agent` | Same as sync/async depending on timing | +| `rules_only` | Hard error (no LLM fallback), log and skip | + +### Observability + +All capture operations log: +- `capture_mode` +- `trigger_type` +- `latency_ms` +- `records_created` +- `records_updated` +- `tokens_consumed` +- `error` (if any) + +To Agent Run: `memory_capture_triggered`, `memory_capture_mode`, `memory_records_created`, `memory_capture_latency_ms` diff --git a/tech_specs/STORAGE_ARCHITECTURE.md b/tech_specs/STORAGE_ARCHITECTURE.md new file mode 100644 index 00000000..444e8a94 --- /dev/null +++ b/tech_specs/STORAGE_ARCHITECTURE.md @@ -0,0 +1,425 @@ +# Storage Architecture: Agent Memory Subsystem + +> **Status:** Draft +> **Scope:** HUF Native Memory Layer +> **Date:** 2026-03-28 + +--- + +## 1. Overview + +This document defines the storage architecture for HUF's Agent Memory subsystem. The design separates **canonical storage** (source of truth) from **optional indexing** (retrieval acceleration), enabling portability across deployment configurations while maintaining queryable, auditable records. + +**Core Principle:** *All memory lives first in Memory Record as the source of truth.* + +--- + +## 2. Design Decisions + +### 2.1 Separation of Concerns + +| Layer | Responsibility | Backend | +|-------|---------------|---------| +| **Canonical Storage** | Source of truth, structured data, lifecycle management | MariaDB (DocType) | +| **FTS Index** | Full-text search over `summary_text`, `data_json`, `tags` | SQLite FTS5 | +| **Vector Index** | Semantic similarity search | sqlite-vec (MVP), pgvector (future) | +| **Index Backend** | Abstraction layer for index operations | HUF Index Backend API | + +**Rationale:** +- Keeps HUF portable — works without vector dependencies in minimal deployments +- Allows index rebuilds without data loss +- Enables per-record indexing policy decisions +- Supports hybrid retrieval (FTS + vector) when both enabled + +### 2.2 Indexing Policy + +Indexing is **opt-in per record**, controlled by `Memory Policy` fields: + +```python +enable_fts_index: bool = True +enable_vector_index: bool = False +vector_backend: Literal["none", "sqlite_vec", "pgvector"] = "none" +``` + +Records with `index_backend="none"` are queryable via DocType filters but excluded from vector/FTS search. + +### 2.3 Backend Abstraction + +The `Index Backend` abstraction provides a uniform interface regardless of underlying storage: + +```python +class MemoryIndexBackend(ABC): + """Abstract interface for memory indexing backends.""" + + async def index(self, record: MemoryRecord) -> IndexResult: + """Index a memory record for retrieval.""" + pass + + async def search( + self, + query: str, + scope: ScopeFilter, + limit: int = 10 + ) -> list[SearchResult]: + """Search indexed memories.""" + pass + + async def delete(self, record_id: str) -> None: + """Remove a record from the index.""" + pass + + async def reindex(self, record: MemoryRecord) -> IndexResult: + """Update an existing indexed record.""" + pass +``` + +Concrete implementations: +- `SQLiteFTSBackend` — FTS5 virtual tables +- `SqliteVecBackend` — sqlite-vec extension +- `PgvectorBackend` — PostgreSQL pgvector (future) + +--- + +## 3. Schema Design + +### 3.1 Memory Record (Canonical Storage) + +**DocType:** `Memory Record` +**Table:** `tabMemory Record` + +| Field | Type | Description | +|-------|------|-------------| +| `name` | Data | Primary key (autogenerated hash) | +| `title` | Data | Human-readable summary | +| `agent` | Link | → Agent | +| `conversation` | Link | → Agent Conversation (optional) | +| `run` | Link | → Agent Run (optional) | +| `source_type` | Select | `conversation`, `run`, `manual`, `event`, `scheduled`, `imported` | +| `producer_mode` | Select | `main_agent`, `memory_agent`, `post_run_llm`, `rules_only`, `manual` | +| `memory_type` | Select | `profile`, `session_state`, `preference`, `fact`, `plan`, `observation`, `insight`, `domain_object`, `custom` | +| `schema_name` | Data | JSON schema identifier or profile name | +| `profile_name` | Link | → Memory Profile (optional) | +| `data_json` | JSON | Structured payload (canonical) | +| `summary_text` | Text | Human-readable summary for display/FTS | +| `raw_context_excerpt` | Long Text | Original context snapshot (for debugging/audit) | +| `scope_type` | Select | `conversation`, `user`, `agent`, `namespace`, `global` | +| `scope_key` | Data | Scope identifier (conversation_id, user_id, etc.) | +| `visibility` | Select | `private`, `shared_with_agent`, `shared_with_namespace`, `global` | +| `status` | Select | `active`, `superseded`, `archived`, `expired`, `error` | +| `confidence` | Float | 0.0-1.0 extraction confidence | +| `importance_score` | Float | 0.0-1.0 ranking weight | +| `ttl_days` | Int | Auto-expiry duration | +| `effective_from` | Datetime | Validity start | +| `effective_until` | Datetime | Validity end | +| `supersedes_memory_record` | Link | → Memory Record (version chain) | +| `created_from_turn_count` | Int | Conversation turn at capture | +| `tags` | Table MultiSelect | User/system tags | +| `metadata_json` | JSON | System metadata (capture latency, model used, cost) | + +#### Indexing Fields + +| Field | Type | Description | +|-------|------|-------------| +| `fts_indexed` | Check | FTS index status | +| `vector_indexed` | Check | Vector index status | +| `index_backend` | Select | `none`, `sqlite_fts`, `sqlite_vec`, `pgvector`, `custom` | +| `last_indexed_at` | Datetime | Last successful index operation | +| `last_retrieved_at` | Datetime | Last read (for LRU/decay) | +| `retrieval_count` | Int | Usage counter | + +### 3.2 FTS Index Schema (SQLite FTS5) + +**Virtual Table:** `memory_fts_idx` + +```sql +CREATE VIRTUAL TABLE memory_fts_idx USING fts5( + record_id UNINDEXED, -- Link back to tabMemory Record + title, + summary_text, + data_json, -- Serialized for text search + tags, + scope_type UNINDEXED, + scope_key UNINDEXED, + memory_type UNINDEXED, + agent UNINDEXED, + tokenize='porter unicode61' +); +``` + +**Contentless design:** FTS table stores only index, not content. Retrieval joins to `tabMemory Record`. + +**Triggers:** DocType hooks maintain FTS index on insert/update/delete. + +### 3.3 Vector Index Schema (sqlite-vec) + +**Virtual Table:** `memory_vec_idx` + +```sql +CREATE VIRTUAL TABLE memory_vec_idx USING vec0( + record_id TEXT PRIMARY KEY, -- Link back to tabMemory Record + embedding FLOAT[1536] -- Dimension matches embedding model +); +``` + +**Embedding source:** Generated via configured embedding model (OpenAI, local, etc.) from `summary_text` or `data_json` serialization. + +**Note:** sqlite-vec requires the extension to be loaded. Graceful fallback to no vector search if unavailable. + +### 3.4 pgvector Schema (Future) + +**Table:** `memory_pgvector_idx` + +```sql +CREATE TABLE memory_pgvector_idx ( + record_id TEXT PRIMARY KEY REFERENCES memory_record(name), + embedding vector(1536), + metadata JSONB +); + +CREATE INDEX ON memory_pgvector_idx USING ivfflat (embedding vector_cosine_ops); +``` + +--- + +## 4. Index Backend Abstraction + +### 4.1 Backend Selection Logic + +```python +def get_index_backend(config: MemoryPolicy) -> MemoryIndexBackend: + """Return appropriate backend based on policy and availability.""" + + backends = [] + + if config.enable_fts_index: + backends.append(SQLiteFTSBackend()) + + if config.enable_vector_index: + if config.vector_backend == "sqlite_vec" and sqlite_vec_available(): + backends.append(SqliteVecBackend()) + elif config.vector_backend == "pgvector" and pgvector_available(): + backends.append(PgvectorBackend()) + + if len(backends) == 0: + return NoOpBackend() # Canonical only, no indexing + + if len(backends) == 1: + return backends[0] + + return HybridBackend(backends) # Combines FTS + vector results +``` + +### 4.2 Hybrid Search Strategy + +When both FTS and vector indexes exist: + +1. **Parallel query** — Execute both searches concurrently +2. **Reciprocal Rank Fusion (RRF)** — Combine rankings: + ``` + score = Σ 1 / (k + rank_i) for each backend i + default k = 60 + ``` +3. **Re-rank** — Apply scope weights, recency boost, importance score + +### 4.3 Index Lifecycle + +| Event | Action | +|-------|--------| +| Record created | If policy matches, generate embedding and insert to index(es) | +| Record updated | Re-generate embedding, update index, update `last_indexed_at` | +| Record deleted | Remove from all indexes | +| Record superseded | Mark old `status="superseded"`, optional: remove from index | +| Record expired | Mark `status="expired"`, remove from index | +| Policy changed (disable vector) | Async reindex job: remove vector, keep FTS | +| Backend unavailable | Queue for retry, mark `vector_indexed=false` | + +--- + +## 5. Migration from Current Data Management + +### 5.1 Current State + +Today's "Data Management" stores structured JSON in conversation context or ad-hoc storage. No FTS, no vector, no scoping, no lifecycle. + +### 5.2 Migration Strategy + +**Phase 1: Schema Migration** + +1. Create `Memory Record` DocType +2. Create `Memory Policy` DocType +3. Create `Memory Profile` DocType +4. Add memory fields to Agent, Agent Conversation, Agent Run + +**Phase 2: Data Migration** + +```python +def migrate_existing_data(): + """One-time migration of existing data management records.""" + + # Query existing data management records + for old_record in frappe.get_all("Data Management Record"): + # Transform to Memory Record + memory_record = frappe.new_doc("Memory Record") + memory_record.update({ + "title": old_record.get("title", "Migrated Record"), + "agent": old_record.get("agent"), + "conversation": old_record.get("conversation"), + "source_type": "imported", + "producer_mode": "manual", + "memory_type": "domain_object", + "data_json": old_record.get("data"), + "scope_type": "conversation", # Conservative default + "status": "active", + "index_backend": "none" # Re-index separately if needed + }) + memory_record.insert() +``` + +**Phase 3: Policy Migration** + +- Agents with `enable_data_management=true` → create default `Memory Policy` +- Map existing capture prompts → `Memory Policy.capture_prompt` +- Map existing schemas → `Memory Profile` or inline `capture_schema_json` + +**Phase 4: Index Backfill** + +- Async job to index migrated records based on new policy settings +- `frappe.enqueue("huf.memory.backfill_index", queue="long")` + +### 5.3 Rollback Considerations + +- Keep old tables until migration verified +- Memory Record `source_type="imported"` allows identification of migrated data +- Dual-write period optional: write to both old and new during transition + +--- + +## 6. Configuration Examples + +### 6.1 Minimal (No Indexing) + +```json +{ + "enable_fts_index": false, + "enable_vector_index": false, + "index_backend": "none" +} +``` +*Use case:* Development, low-resource deployments, privacy-sensitive records. + +### 6.2 FTS Only + +```json +{ + "enable_fts_index": true, + "enable_vector_index": false, + "index_backend": "sqlite_fts" +} +``` +*Use case:* Keyword-heavy domains (CRM, support tickets), no semantic search needed. + +### 6.3 Vector Only (sqlite-vec) + +```json +{ + "enable_fts_index": false, + "enable_vector_index": true, + "vector_backend": "sqlite_vec", + "index_backend": "sqlite_vec" +} +``` +*Use case:* Semantic similarity search, moderate scale. + +### 6.4 Hybrid (Recommended) + +```json +{ + "enable_fts_index": true, + "enable_vector_index": true, + "vector_backend": "sqlite_vec", + "index_backend": "sqlite_fts" // Primary, but both used in search +} +``` +*Use case:* Best retrieval quality, combines keyword + semantic. + +### 6.5 Enterprise (pgvector) + +```json +{ + "enable_fts_index": true, + "enable_vector_index": true, + "vector_backend": "pgvector", + "index_backend": "pgvector" +} +``` +*Use case:* Large scale, existing PostgreSQL infrastructure. + +--- + +## 7. Operational Considerations + +### 7.1 Index Rebuild + +```python +# CLI or admin action +def rebuild_index(backend_type: str): + """Rebuild specified index from canonical records.""" + + # Clear existing index + backend = get_backend(backend_type) + backend.clear() + + # Re-index all active records + for record in frappe.get_all("Memory Record", + filters={"status": "active"}, + fields=["name"]): + doc = frappe.get_doc("Memory Record", record.name) + if should_index(doc): + backend.index(doc) + doc.vector_indexed = True + doc.last_indexed_at = now() + doc.save() +``` + +### 7.2 Monitoring + +| Metric | Source | +|--------|--------| +| Index lag | `last_indexed_at` vs `modified` | +| Index coverage | Count of `vector_indexed=true` / total active | +| Query latency | Backend implementation metrics | +| Embedding cost | `metadata_json.embedding_cost` | + +### 7.3 Backup and Restore + +- **Canonical:** Standard MariaDB backup (included in Frappe backup) +- **FTS:** Rebuild from canonical (not separately backed up) +- **Vector:** Rebuild from canonical (not separately backed up) + +*Design choice:* Indexes are disposable accelerators. Source of truth is always `tabMemory Record`. + +--- + +## 8. Future Extensions + +| Extension | Description | +|-----------|-------------| +| Multi-vector per record | Store multiple embeddings (summary vs. full data) | +| Sparse vectors | BM25 or learned sparse representations | +| HNSW indexing | When sqlite-vec adds native HNSW support | +| Cross-encoder re-ranking | Second-stage re-ranking for search results | +| Memory graph | Neo4j backend for relationship indexing | + +--- + +## 9. Summary + +This architecture provides: + +1. **Canonical storage** in `Memory Record` DocType — always available, always queryable +2. **Optional FTS indexing** via SQLite FTS5 — lightweight keyword search +3. **Optional vector indexing** via sqlite-vec (MVP) or pgvector (scale) — semantic search +4. **Backend abstraction** — uniform interface, pluggable implementations +5. **Clean migration path** from current data management — phased, reversible, auditable + +The design prioritizes **portability** (works without vector dependencies), **auditability** (canonical records always accessible), and **flexibility** (per-record, per-policy indexing decisions). From d3b38606e9f80268d69669a06934ac0eb0d722ec Mon Sep 17 00:00:00 2001 From: esafwan Date: Mon, 30 Mar 2026 11:34:15 +0800 Subject: [PATCH 2/2] feat(knowledge): implement Weaviate vector backend Implement WeaviateBackend with the following features: - Self-hosted (Docker) and Weaviate Cloud support - Hybrid search (vector + BM25 keyword search) - Schema auto-creation and management - Full CRUD operations with metadata filtering - Health check and statistics Includes: - weaviate_backend.py: Main backend implementation - factory.py: BackendFactory for registration and caching - test_weaviate_backend.py: Comprehensive test suite Refs: kimi/vector-store-weaviate-backend --- huf/ai/knowledge/backends/__init__.py | 3 +- huf/ai/knowledge/backends/factory.py | 58 ++++ huf/ai/knowledge/backends/weaviate_backend.py | 260 ++++++++++++++++++ .../knowledge/tests/test_weaviate_backend.py | 260 ++++++++++++++++++ 4 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 huf/ai/knowledge/backends/factory.py create mode 100644 huf/ai/knowledge/backends/weaviate_backend.py create mode 100644 huf/ai/knowledge/tests/test_weaviate_backend.py diff --git a/huf/ai/knowledge/backends/__init__.py b/huf/ai/knowledge/backends/__init__.py index 6c67154d..6df413c5 100644 --- a/huf/ai/knowledge/backends/__init__.py +++ b/huf/ai/knowledge/backends/__init__.py @@ -2,7 +2,7 @@ Knowledge Backend Abstraction This module provides a unified interface for knowledge storage backends. -Supported: SQLite FTS (keyword search), SQLite Vec (vector search) +Supported: SQLite FTS (keyword search), SQLite Vec (vector search), Weaviate (vector + hybrid search) """ from abc import ABC, abstractmethod @@ -65,6 +65,7 @@ def get_backend(backend_type: str) -> type: backends = { "sqlite_fts": "huf.ai.knowledge.backends.sqlite_fts.SQLiteFTSBackend", "sqlite_vec": "huf.ai.knowledge.backends.sqlite_vec_backend.SQLiteVecBackend", + "weaviate": "huf.ai.knowledge.backends.weaviate_backend.WeaviateBackend", } if backend_type not in backends: diff --git a/huf/ai/knowledge/backends/factory.py b/huf/ai/knowledge/backends/factory.py new file mode 100644 index 00000000..17abb208 --- /dev/null +++ b/huf/ai/knowledge/backends/factory.py @@ -0,0 +1,58 @@ +"""Backend Factory for knowledge backends.""" + +from typing import Dict, Type, Optional, Any, Tuple + +import frappe + +from . import KnowledgeBackend + + +class BackendFactory: + """Factory for creating knowledge backends.""" + + _registry: Dict[str, Type[KnowledgeBackend]] = {} + _instances: Dict[str, KnowledgeBackend] = {} + + @classmethod + def register(cls, backend_type: str, backend_class: Type[KnowledgeBackend]) -> None: + """Register a backend class.""" + cls._registry[backend_type] = backend_class + + @classmethod + def get_backend( + cls, + backend_type: str, + knowledge_source: str, + config: Optional[Dict[str, Any]] = None + ) -> KnowledgeBackend: + """Get or create a backend instance.""" + # Create cache key using Frappe's site-aware pattern + cache_key = f"{frappe.local.site}:{knowledge_source}:{backend_type}" + + # Return cached if exists + if cache_key in cls._instances: + return cls._instances[cache_key] + + # Create new instance + if backend_type not in cls._registry: + raise ValueError(f"Unknown backend type: {backend_type}") + + backend_class = cls._registry[backend_type] + instance = backend_class() + instance.initialize(knowledge_source, config or {}) + + cls._instances[cache_key] = instance + return instance + + @classmethod + def health_check_all(cls) -> Dict[str, Tuple[bool, str]]: + """Check health of all cached backends.""" + results = {} + for cache_key, backend in cls._instances.items(): + results[cache_key] = backend.health_check() + return results + + @classmethod + def clear_cache(cls) -> None: + """Clear all cached backend instances.""" + cls._instances.clear() diff --git a/huf/ai/knowledge/backends/weaviate_backend.py b/huf/ai/knowledge/backends/weaviate_backend.py new file mode 100644 index 00000000..a3c830fa --- /dev/null +++ b/huf/ai/knowledge/backends/weaviate_backend.py @@ -0,0 +1,260 @@ +# Copyright (c) 2025, Huf and contributors +# For license information, please see license.txt + +"""Weaviate backend using LlamaIndex adapter.""" + +import re +from typing import Any, Dict, List, Optional, Tuple + +import frappe + +from ..backends import ChunkResult, KnowledgeBackend +from ..backends.factory import BackendFactory + +# LlamaIndex imports - optional dependency +try: + from llama_index.vector_stores.weaviate import WeaviateVectorStore + from llama_index.core import VectorStoreIndex, StorageContext, Document + LLAMAINDEX_AVAILABLE = True +except ImportError: + LLAMAINDEX_AVAILABLE = False + + +class WeaviateBackend(KnowledgeBackend): + """Weaviate vector backend for Huf knowledge storage. + + Supports both self-hosted (Docker) and Weaviate Cloud deployments. + Features best-in-class hybrid search combining vector similarity with BM25. + """ + + def __init__(self): + self.knowledge_source = None + self.config = {} + self.vector_store = None + self.storage_context = None + self.index = None + self._initialized = False + self._weaviate_client = None + + def initialize(self, knowledge_source: str, config: Dict[str, Any]) -> None: + """Initialize Weaviate backend.""" + if not LLAMAINDEX_AVAILABLE: + raise ImportError( + "llama-index-vector-stores-weaviate not installed. " + "Install with: pip install llama-index-vector-stores-weaviate" + ) + + self.knowledge_source = knowledge_source + self.config = config + + # Get connection parameters + connection_params = self._get_connection_params() + + # Create vector store + self.vector_store = WeaviateVectorStore(**connection_params) + self.storage_context = StorageContext.from_defaults( + vector_store=self.vector_store + ) + + self._initialized = True + + def _get_connection_params(self) -> Dict[str, Any]: + """Build connection parameters from config.""" + # Sanitize class name - Weaviate class names must start with uppercase letter + # and contain only alphanumeric characters + sanitized = frappe.scrub(self.knowledge_source) + class_name = f"Huf_{sanitized}" + # Ensure starts with uppercase and only alphanumeric + class_name = re.sub(r'[^a-zA-Z0-9]', '_', class_name) + if class_name[0].isdigit(): + class_name = f"Class_{class_name}" + class_name = class_name[0].upper() + class_name[1:] + + params = { + "class_name": class_name, + } + + # Handle different connection modes + if self.config.get("weaviate_cloud_url"): + # Weaviate Cloud + params["url"] = self.config["weaviate_cloud_url"] + if self.config.get("api_key"): + params["api_key"] = self.config["api_key"] + else: + # Self-hosted + host = self.config.get("host", "localhost") + port = self.config.get("port", 8080) + params["url"] = f"http://{host}:{port}" + + return params + + def add_chunks(self, chunks: List[Dict[str, Any]]) -> int: + """Add chunks to Weaviate.""" + if not chunks: + return 0 + + # Convert to LlamaIndex Documents + documents = [] + for chunk in chunks: + doc = Document( + text=chunk["text"], + metadata={ + "input_id": chunk["input_id"], + "input_type": chunk["input_type"], + "chunk_id": chunk.get("chunk_id"), + "source_title": chunk.get("source_title"), + "knowledge_source": self.knowledge_source, + } + ) + documents.append(doc) + + # Create/update index + self.index = VectorStoreIndex.from_documents( + documents, + storage_context=self.storage_context, + ) + + return len(chunks) + + def search( + self, + query: str, + top_k: int = 5, + filters: Optional[Dict] = None, + use_hybrid: bool = True + ) -> List[ChunkResult]: + """Search Weaviate using hybrid search (vector + BM25). + + Args: + query: Search query string + top_k: Number of results to return + filters: Optional metadata filters + use_hybrid: If True, use hybrid search (vector + keyword). + If False, use pure vector search. + """ + if not self.index: + # Try to load existing index + self.index = VectorStoreIndex.from_vector_store( + self.vector_store, + storage_context=self.storage_context, + ) + + # Create retriever with hybrid search if enabled + if use_hybrid and self.supports_hybrid_search(): + retriever = self.index.as_retriever( + similarity_top_k=top_k, + vector_store_kwargs={ + "alpha": 0.7, # Balance between vector (1.0) and BM25 (0.0) + "fusion_type": "relative_score", + } + ) + else: + retriever = self.index.as_retriever(similarity_top_k=top_k) + + # Search + nodes = retriever.retrieve(query) + + # Convert to ChunkResult + results = [] + for node in nodes: + result = ChunkResult( + chunk_id=node.metadata.get("chunk_id", ""), + text=node.text, + title=node.metadata.get("source_title"), + score=float(node.score) if hasattr(node, "score") else 0.0, + source=node.metadata.get("knowledge_source"), + metadata=node.metadata, + ) + results.append(result) + + return results + + def delete_chunks(self, input_id: str) -> int: + """Delete chunks by input_id.""" + try: + # Weaviate supports deletion by metadata filter + # Using where filter to delete objects with matching input_id + from weaviate.classes.query import Filter + + collection = self.vector_store._collection + collection.data.delete_many( + where=Filter.by_property("input_id").equal(input_id) + ) + return 1 # Weaviate doesn't return count, assume success + except Exception as e: + frappe.logger().warning( + f"Weaviate delete_chunks failed for {input_id}: {str(e)}" + ) + return 0 + + def clear(self) -> None: + """Clear all vectors from the class.""" + try: + if self.vector_store: + # Get the Weaviate client from vector store + client = self.vector_store._client + class_name = self._get_connection_params()["class_name"] + + # Delete all objects in the class + client.collections.delete(class_name) + + # Re-initialize to recreate the schema + connection_params = self._get_connection_params() + self.vector_store = WeaviateVectorStore(**connection_params) + self.storage_context = StorageContext.from_defaults( + vector_store=self.vector_store + ) + self.index = None + except Exception as e: + frappe.logger().error(f"Weaviate clear failed: {str(e)}") + raise + + def get_stats(self) -> Dict[str, Any]: + """Get backend statistics.""" + stats = { + "backend_type": "weaviate", + "knowledge_source": self.knowledge_source, + "initialized": self._initialized, + "class_name": self._get_connection_params().get("class_name"), + "url": self._get_connection_params().get("url"), + } + + # Try to get object count + try: + if self.vector_store: + collection = self.vector_store._collection + # Get aggregate count + result = collection.aggregate.over_all(total_count=True) + stats["object_count"] = result.total_count + except Exception as e: + stats["object_count"] = None + stats["count_error"] = str(e) + + return stats + + def health_check(self) -> Tuple[bool, str]: + """Check backend health.""" + try: + if self.vector_store: + # Test connection with a simple query + client = self.vector_store._client + client.get_meta() + return (True, "Healthy") + except Exception as e: + return (False, str(e)) + + def supports_filters(self) -> bool: + """Weaviate supports metadata filtering.""" + return True + + def supports_hybrid_search(self) -> bool: + """Weaviate supports hybrid search (vector + BM25).""" + return True + + def get_class_name(self) -> str: + """Get the Weaviate class name for this knowledge source.""" + return self._get_connection_params().get("class_name", "") + + +# Register backend +BackendFactory.register("weaviate", WeaviateBackend) diff --git a/huf/ai/knowledge/tests/test_weaviate_backend.py b/huf/ai/knowledge/tests/test_weaviate_backend.py new file mode 100644 index 00000000..f3e4f3e0 --- /dev/null +++ b/huf/ai/knowledge/tests/test_weaviate_backend.py @@ -0,0 +1,260 @@ +# Copyright (c) 2025, Huf and contributors +# For license information, please see license.txt + +"""Tests for Weaviate knowledge backend.""" + +import unittest +from unittest.mock import Mock, patch, MagicMock + +import frappe +from frappe.tests.utils import FrappeTestCase + +from huf.ai.knowledge.backends.weaviate_backend import WeaviateBackend, LLAMAINDEX_AVAILABLE + + +class TestWeaviateBackend(FrappeTestCase): + """Test cases for WeaviateBackend.""" + + def setUp(self): + """Set up test fixtures.""" + self.backend = WeaviateBackend() + self.config = { + "host": "localhost", + "port": 8080, + "vector_dimension": 1536, + } + + @unittest.skipUnless(LLAMAINDEX_AVAILABLE, "llama-index-vector-stores-weaviate not installed") + @patch("huf.ai.knowledge.backends.weaviate_backend.WeaviateVectorStore") + @patch("huf.ai.knowledge.backends.weaviate_backend.StorageContext") + def test_initialize_self_hosted(self, mock_storage_context, mock_vector_store): + """Test initialization with self-hosted Weaviate.""" + # Setup mocks + mock_vector_store_instance = MagicMock() + mock_vector_store.from_params.return_value = mock_vector_store_instance + mock_storage_context_instance = MagicMock() + mock_storage_context.from_defaults.return_value = mock_storage_context_instance + + # Initialize + self.backend.initialize("test_source", self.config) + + # Assertions + self.assertTrue(self.backend._initialized) + self.assertEqual(self.backend.knowledge_source, "test_source") + mock_vector_store.assert_called_once() + mock_storage_context.from_defaults.assert_called_once() + + @unittest.skipUnless(LLAMAINDEX_AVAILABLE, "llama-index-vector-stores-weaviate not installed") + @patch("huf.ai.knowledge.backends.weaviate_backend.WeaviateVectorStore") + @patch("huf.ai.knowledge.backends.weaviate_backend.StorageContext") + def test_initialize_cloud(self, mock_storage_context, mock_vector_store): + """Test initialization with Weaviate Cloud.""" + config = { + "weaviate_cloud_url": "https://test-cluster.weaviate.cloud", + "api_key": "test-api-key", + "vector_dimension": 1536, + } + + # Setup mocks + mock_vector_store_instance = MagicMock() + mock_vector_store.from_params.return_value = mock_vector_store_instance + + # Initialize + self.backend.initialize("test_cloud_source", config) + + # Check that cloud URL and API key were used + call_kwargs = mock_vector_store.call_args.kwargs + self.assertEqual(call_kwargs.get("url"), "https://test-cluster.weaviate.cloud") + self.assertEqual(call_kwargs.get("api_key"), "test-api-key") + + @unittest.skipUnless(LLAMAINDEX_AVAILABLE, "llama-index-vector-stores-weaviate not installed") + def test_class_name_sanitization(self): + """Test that class names are properly sanitized for Weaviate.""" + # Test various source names + test_cases = [ + ("my_source", "Huf_my_source"), + ("My Source", "Huf_my_source"), + ("123-source", "Class_123_source"), + ("special!@#chars", "Huf_special___chars"), + ] + + for source_name, expected_pattern in test_cases: + backend = WeaviateBackend() + backend.knowledge_source = source_name + class_name = backend.get_class_name() + + # Class name should start with uppercase letter + self.assertTrue(class_name[0].isupper()) + # Should only contain alphanumeric and underscore + self.assertTrue(all(c.isalnum() or c == "_" for c in class_name)) + + @unittest.skipUnless(LLAMAINDEX_AVAILABLE, "llama-index-vector-stores-weaviate not installed") + @patch("huf.ai.knowledge.backends.weaviate_backend.WeaviateVectorStore") + @patch("huf.ai.knowledge.backends.weaviate_backend.StorageContext") + @patch("huf.ai.knowledge.backends.weaviate_backend.VectorStoreIndex") + def test_add_chunks(self, mock_index_class, mock_storage_context, mock_vector_store): + """Test adding chunks to Weaviate.""" + # Setup mocks + mock_vector_store_instance = MagicMock() + mock_vector_store.return_value = mock_vector_store_instance + mock_storage_context_instance = MagicMock() + mock_storage_context.from_defaults.return_value = mock_storage_context_instance + mock_index = MagicMock() + mock_index_class.from_documents.return_value = mock_index + + # Initialize and add chunks + self.backend.initialize("test_source", self.config) + + chunks = [ + { + "text": "Test chunk 1", + "input_id": "input_1", + "input_type": "text", + "chunk_id": "chunk_1", + "source_title": "Test Source", + } + ] + + count = self.backend.add_chunks(chunks) + + # Should return number of chunks added + self.assertEqual(count, 1) + mock_index_class.from_documents.assert_called_once() + + @unittest.skipUnless(LLAMAINDEX_AVAILABLE, "llama-index-vector-stores-weaviate not installed") + @patch("huf.ai.knowledge.backends.weaviate_backend.WeaviateVectorStore") + @patch("huf.ai.knowledge.backends.weaviate_backend.StorageContext") + @patch("huf.ai.knowledge.backends.weaviate_backend.VectorStoreIndex") + def test_search(self, mock_index_class, mock_storage_context, mock_vector_store): + """Test searching Weaviate.""" + # Setup mocks + mock_vector_store_instance = MagicMock() + mock_vector_store.return_value = mock_vector_store_instance + mock_storage_context_instance = MagicMock() + mock_storage_context.from_defaults.return_value = mock_storage_context_instance + + # Mock index and retriever + mock_index = MagicMock() + mock_retriever = MagicMock() + mock_index.as_retriever.return_value = mock_retriever + mock_index_class.from_vector_store.return_value = mock_index + + # Mock search results + mock_node = MagicMock() + mock_node.text = "Test result" + mock_node.metadata = { + "chunk_id": "chunk_1", + "source_title": "Test Source", + "knowledge_source": "test_source", + } + mock_node.score = 0.95 + mock_retriever.retrieve.return_value = [mock_node] + + # Initialize and search + self.backend.initialize("test_source", self.config) + self.backend.index = mock_index + + results = self.backend.search("test query", top_k=5) + + # Should return results + self.assertEqual(len(results), 1) + self.assertEqual(results[0].chunk_id, "chunk_1") + self.assertEqual(results[0].text, "Test result") + self.assertEqual(results[0].score, 0.95) + + def test_empty_chunks(self): + """Test adding empty chunks list.""" + # Should return 0 without error + count = self.backend.add_chunks([]) + self.assertEqual(count, 0) + + @unittest.skipUnless(LLAMAINDEX_AVAILABLE, "llama-index-vector-stores-weaviate not installed") + @patch("huf.ai.knowledge.backends.weaviate_backend.WeaviateVectorStore") + @patch("huf.ai.knowledge.backends.weaviate_backend.StorageContext") + def test_health_check(self, mock_storage_context, mock_vector_store): + """Test health check.""" + # Setup mocks + mock_vector_store_instance = MagicMock() + mock_vector_store.return_value = mock_vector_store_instance + mock_client = MagicMock() + mock_vector_store_instance._client = mock_client + + # Initialize + self.backend.initialize("test_source", self.config) + + # Health check should pass + is_healthy, message = self.backend.health_check() + self.assertTrue(is_healthy) + self.assertEqual(message, "Healthy") + mock_client.get_meta.assert_called_once() + + @unittest.skipUnless(LLAMAINDEX_AVAILABLE, "llama-index-vector-stores-weaviate not installed") + @patch("huf.ai.knowledge.backends.weaviate_backend.WeaviateVectorStore") + @patch("huf.ai.knowledge.backends.weaviate_backend.StorageContext") + def test_health_check_failure(self, mock_storage_context, mock_vector_store): + """Test health check with failure.""" + # Setup mocks + mock_vector_store_instance = MagicMock() + mock_vector_store.return_value = mock_vector_store_instance + mock_client = MagicMock() + mock_client.get_meta.side_effect = Exception("Connection failed") + mock_vector_store_instance._client = mock_client + + # Initialize + self.backend.initialize("test_source", self.config) + + # Health check should fail + is_healthy, message = self.backend.health_check() + self.assertFalse(is_healthy) + self.assertIn("Connection failed", message) + + def test_supports_filters(self): + """Test that Weaviate supports filters.""" + self.assertTrue(self.backend.supports_filters()) + + def test_supports_hybrid_search(self): + """Test that Weaviate supports hybrid search.""" + self.assertTrue(self.backend.supports_hybrid_search()) + + @unittest.skipUnless(LLAMAINDEX_AVAILABLE, "llama-index-vector-stores-weaviate not installed") + @patch("huf.ai.knowledge.backends.weaviate_backend.WeaviateVectorStore") + @patch("huf.ai.knowledge.backends.weaviate_backend.StorageContext") + def test_get_stats(self, mock_storage_context, mock_vector_store): + """Test getting backend statistics.""" + # Setup mocks + mock_vector_store_instance = MagicMock() + mock_vector_store.return_value = mock_vector_store_instance + mock_collection = MagicMock() + mock_vector_store_instance._collection = mock_collection + mock_aggregate = MagicMock() + mock_aggregate.total_count = 42 + mock_collection.aggregate.over_all.return_value = mock_aggregate + + # Initialize + self.backend.initialize("test_source", self.config) + + # Get stats + stats = self.backend.get_stats() + + self.assertEqual(stats["backend_type"], "weaviate") + self.assertEqual(stats["knowledge_source"], "test_source") + self.assertTrue(stats["initialized"]) + self.assertEqual(stats["object_count"], 42) + + +class TestWeaviateBackendImport(unittest.TestCase): + """Test import handling when llama-index is not available.""" + + @patch("huf.ai.knowledge.backends.weaviate_backend.LLAMAINDEX_AVAILABLE", False) + def test_initialize_without_llamaindex(self): + """Test that initialization raises ImportError when llama-index is not available.""" + backend = WeaviateBackend() + with self.assertRaises(ImportError) as context: + backend.initialize("test", {}) + + self.assertIn("llama-index-vector-stores-weaviate", str(context.exception)) + + +if __name__ == "__main__": + frappe.connect(site="test_site") + unittest.main()