* fix: misc perf improvements
* more tests
* fix test
* fix: update test files for new extract_facts_from_text signature
- Replace test_fact_extraction_token_analysis with test_fact_extraction_basic_analysis
using inline sample content instead of external file
- Update test_fact_extraction_output_ratio.py to unpack 3 return values
(facts, chunks, usage) instead of 2
* fix: make temporal tests more flexible for LLM variation
- test_temporal_absolute_conversion: check occurred_start field instead of
requiring specific text in facts
- test_date_field_calculation_yesterday: make assertions conditional on
having temporal data, add more content for better extraction
- test_temporal_ordering: reduce minimum required facts from 3 to 2
Call ensure_embedding_dimension after running migrations for tenant
schemas. This ensures the embedding column dimension matches the
model's dimension, which may differ from the default 384 dimensions
used in the initial migration.
Without this fix, using embedding providers with different dimensions
(e.g., Cohere's embed-english-v3.0 with 1024 dims) would fail with
"expected 384 dimensions, not 1024" errors on tenant schemas.
The /v1/default/banks/{bank_id}/stats endpoint was missing the
request_context parameter and tenant authentication call, causing
it to query the public schema instead of the tenant's schema.
This resulted in stats always returning zeros for multi-tenant
deployments since the data lives in tenant-specific schemas.
Added request_context dependency and _authenticate_tenant() call
to properly set the tenant schema before querying stats.
* expose the delete API
* add deleteBank
* Add a button and confirmation dialog to delete a memory bank
* commit lint changes
* add CI test for delete bank
* revert alembic lint changes due to version differences
* revert alembic lint changes
* fix the delete bank test
* account for ruff lint third party alembic
* feat(mcp): add async_processing parameter to retain tool
Add async_processing parameter (default: True) to the MCP retain tool
to allow non-blocking memory storage. When True, memories are queued
for background processing and the tool returns immediately. When False,
the tool waits for completion before returning.
This matches the async behavior available in the HTTP API.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(mcp): add list_memories and reflect tools
Add two missing MCP tools to achieve feature parity with HTTP API:
- list_memories: browse memories with pagination and full-text search
(equivalent to GET /memories/list)
- reflect: LLM-based reasoning over memories with disposition awareness
(equivalent to POST /reflect)
Both tools follow the existing pattern with JSON string responses
and proper error handling.
* docs: improve CLAUDE.md with detailed architecture info
- Add memory types explanation (world, experience, opinion, observation)
- Document retain/ and search/ submodule structure
- Add commands for single test run, ruff format, ty type checking
- Note MCP server implementation in API layer
- Add optional environment variables section
- Clarify conventions (no Python files at root, npm workspaces)
* chore: add .mcp.json and .osgrep to gitignore
These are user-specific development tool configs that should not be committed.
* changes
* refactor(mcp): remove list_memories tool
The list_memories endpoint is for debugging/exploration, not agent use.
Agents should use recall for semantic search instead.
Feedback from maintainer: "this tool is misleading for the agent,
it should use recall, the list method is mostly for debugging and
exploration, not for real usage"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor(mcp): remove list_banks and create_bank tools
These admin/orchestration tools are not needed for typical agent usage.
Agents work with a single configured bank via X-Bank-Id header.
MCP now exposes only core memory operations:
- retain: store memories
- recall: semantic search
- reflect: LLM reasoning over memories
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Anton Evseev <a.evseev@xsolla.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Record LLM token metrics via Prometheus
Wire up the existing token metrics infrastructure to actually record
token usage from LLM calls. The MetricsCollector already had
record_tokens() method and Prometheus counters (hindsight.tokens.input,
hindsight.tokens.output), but they were never being populated.
Changes:
- Import get_metrics_collector in llm_wrapper.py
- Call record_tokens() after successful LLM calls for:
- OpenAI/Groq (using response.usage.prompt_tokens, completion_tokens)
- Anthropic (using response.usage.input_tokens, output_tokens)
- Gemini (using response.usage_metadata.prompt_token_count, candidates_token_count)
- Add test file to verify token metrics are recorded
Note: Ollama's native API doesn't return token usage, so metrics
are not recorded for that provider.
The token metrics will now be available via /metrics endpoint:
- hindsight_tokens_input_total
- hindsight_tokens_output_total
* feat: add per-request token usage tracking to retain and reflect endpoints
- Add TokenUsage model with input_tokens, output_tokens, total_tokens
- Return usage metrics in retain response (sync operations only)
- Return usage metrics in reflect response
- Update Python, TypeScript, and Rust clients
- Add API documentation for usage fields
- Add changelog entry
Add automatic .env file loading using python-dotenv. This searches
the current working directory and parent directories for a .env file
and loads environment variables from it.
Uses override=True so .env file values take precedence over existing
shell environment variables, which is the expected behavior when
running from a project directory.
* misc: add mcp integration tests and increase test coverage
* misc: add mcp integration tests and increase test coverage
* misc: add mcp integration tests and increase test coverage
* feat(mcp): Add multi-bank access and new MCP tools
Enables orchestrator agents to access multiple memory banks from a
single MCP connection, with new tools for bank management.
## New MCP Tools
- `reflect` - Thoughtful analysis using bank's personality and memories
- `list_banks` - Discover all available memory banks
- `create_bank` - Create new banks programmatically
## Multi-Bank Access
- Added optional `bank_id` parameter to `retain`, `recall`, `reflect`
- Allows cross-bank operations from a single MCP session
- Defaults to session bank if not specified
## Claude Code Compatibility
- Enabled `stateless_http=True` for proper Claude Code integration
- Responses now include `bank_id` for transparency
## Documentation
- Added docker-compose.example.yml with env var substitution
- Added HINDSIGHT-DOCKER.md setup guide with volume persistence docs
- Updated .gitignore to exclude local docker-compose.yml
## Use Case
Orchestrator agents can now:
- Maintain a private meta-orchestration bank
- Access shared project knowledge banks
- Query across banks for cross-context insights
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address PR review feedback: remove docker files, improve reflect description
- Remove HINDSIGHT-DOCKER.md and docker-compose.example.yml per reviewer request
- Improve reflect tool description with clearer guidance for AI agents:
- Added "WHEN TO USE THIS TOOL" section
- Added "EXAMPLES OF GOOD QUERIES" with concrete use cases
- Added "HOW IT DIFFERS FROM RECALL" to clarify when to use each tool
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Add local LLM improvements for reasoning models and Docker startup
## Reasoning Model Support
- Strip thinking tags from local LLM responses (<think>, <thinking>, <reasoning>, |startthink|/|endthink|)
- Enables Qwen3, DeepSeek, and other reasoning models to work with JSON extraction
- Non-breaking: only affects responses that contain thinking tags
## Docker Retry Start Script
- New retry-start.sh waits for dependencies before starting Hindsight
- Checks LLM Studio availability at /v1/models endpoint
- Checks database connectivity (skipped for embedded pg0)
- Configurable via HINDSIGHT_RETRY_MAX and HINDSIGHT_RETRY_INTERVAL env vars
- Prevents startup failures when LLM Studio isn't ready yet
Tested on Apple Silicon M4 Max with Qwen3 8B via LM Studio.
* refactor: make thinking token stripping opt-in via env var
* refactor: merge retry logic into start-all.sh (opt-in via HINDSIGHT_WAIT_FOR_DEPS)
* fix: resolve pg0 stale instance config in Docker build
- Remove stale pg0 instance data after pre-caching binaries to avoid
port conflicts (was using hardcoded port 5555 from build time)
- Remove unused cache copy logic from start-all.sh
- Add database backup instructions to CLAUDE.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Improve graph visualization on the UI
* Fix double animation when loading the graph visualization
* Fix typescript issues
* CI test changes for temporal scenarios
* Fix typescript errors
* Fix animation issue on opinions and experiences
* Load operation validator extension in main entry point
Enable the operation validator extension to be loaded from environment
configuration and passed to MemoryEngine, allowing pre/post operation
hooks for usage metering, rate limiting, and audit logging.
* Fix reflect background task authentication and add internal flag
- Pass API key to background opinion storage task for proper auth
- Add internal flag to RequestContext for tracking internal operations
- Background opinion storage now authenticates correctly with tenant
* Add api_key_id to RequestContext for usage tracking
- Add api_key_id field to RequestContext to track which API key was used
- Enables per-API-key usage analytics in the metering system
* Fix HTTP error handling for authentication and validation errors
- Add status_code parameter to ValidationResult and OperationValidationError
- Convert OperationValidationError to HTTPException with proper status codes
- Fix authentication errors to return 401 instead of raising internal errors
- Re-raise HTTPException in exception handlers to prevent swallowing errors
* Fix AuthenticationError handling in memory engine
- Raise AuthenticationError from memory_engine._authenticate_tenant instead
of HTTPException so unit tests pass
- Add AuthenticationError handling in HTTP layer to convert to 401 responses
- Fixes failing TestMemoryEngineTenantAuth tests
* Add global exception handler for AuthenticationError
Returns proper 401 status code for all authentication failures
across all endpoints, not just the ones with explicit handlers.
* Simplify exception handling: use global AuthenticationError handler
- Remove redundant individual exception handlers
- Add 'except AuthenticationError: raise' before generic Exception handlers
to let global handler process auth errors uniformly
* Refactor background tasks to use tenant_id instead of api_key
This makes the core more generic - it passes tenant_id (which is
extension-agnostic) rather than api_key (which is cloud-specific).
- Add tenant_id field to RequestContext
- Pass tenant_id instead of api_key to background tasks
- Extensions can check internal=True with tenant_id to bypass normal auth
* Fix exception propagation: include HTTPException in re-raise
After cleanup of redundant exception handlers, 404 errors were
returning 500 because HTTPException was caught by the generic
except Exception handler. Fixed by combining AuthenticationError
and HTTPException in the re-raise pattern.
* feat: Add Anthropic Claude and LM Studio provider support
- Add Anthropic as LLM provider with full async support
- Add LM Studio provider for local model inference
- Fix JSON response format compatibility for local models
- Update .env.example with configuration examples
- Update docstrings with all supported providers
Tested with:
- Claude Sonnet 4 (claude-sonnet-4-20250514)
- Claude Haiku 4.5 (claude-haiku-4-5-20251001)
- Qwen 30B via LM Studio
* feat: Add dynamic timeout for local LLM providers
Add configurable timeout support for LLM API calls:
- Environment variable override via HINDSIGHT_API_LLM_TIMEOUT
- Dynamic heuristic for lmstudio/ollama: 20 mins for large models
(30b, 33b, 34b, 65b, 70b, 72b, 8x7b, 8x22b), 5 mins for others
- Pass timeout to Anthropic, OpenAI, and local model clients
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Address PR review feedback
- Remove CLAUDE.md from .gitignore (should stay in repository)
- Pass max_completion_tokens to _call_anthropic instead of hardcoding 4096
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: Remove deleted AI assistant files from .gitignore
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: Add CLAUDE.md for Claude Code integration
Provides project context and development commands for AI-assisted coding.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: Include local dev files and sync changes
- Add docker-compose.yml for local development
- Add test_internal.py for local testing
- Sync uv.lock and llm_wrapper.py changes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Address PR review feedback for LLM provider support
- Move LLM config to config.py with HINDSIGHT_API_ prefix
- Add HINDSIGHT_API_LLM_MAX_CONCURRENT (default: 32)
- Add HINDSIGHT_API_LLM_TIMEOUT (default: 120s)
- Remove fragile model-size timeout heuristic
- Apply markdown JSON extraction to all providers, not just local
- Fix Anthropic markdown extraction bug (missing split)
- Change LLM request/response logs from info to debug level
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: Remove local dev docker-compose.yml
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: Add local dev docker-compose.yml
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Update LM Studio port to 2222 in docker-compose
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: Remove obsolete version attribute from docker-compose
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Remove test file and docker-compose per PR review
- Remove test_internal.py (debug file)
- Remove docker-compose.yml (to be moved to hindsight-cookbook repo)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The MCP server's lifespan was not being properly chained with the
FastAPI app's lifespan, causing the MCP server to not start/stop
correctly when mounted as a sub-application.
Changes:
- Create MCP app before FastAPI app to access its lifespan
- Chain MCP lifespan context with FastAPI's lifespan context
- Ensures MCP server lifecycle is properly managed
This fix is required for the MCP server to function correctly when
used with Claude Code and other MCP clients.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Allows tuning of entity observation generation via environment variables.
## New Environment Variables
- `HINDSIGHT_API_OBSERVATION_MIN_FACTS` - Minimum facts required to
generate entity observations (default: 5)
- `HINDSIGHT_API_OBSERVATION_TOP_ENTITIES` - Maximum entities to process
per retain batch (default: 5)
## Changes
- Added threshold configuration to HindsightConfig
- Updated memory_engine.py to use config values
- Updated observation_regeneration.py to use config values
## Use Case
Lower thresholds generate more observations (better recall, higher cost).
Higher thresholds are more selective (lower cost, may miss patterns).
Example:
```bash
# Generate more observations
docker run -e HINDSIGHT_API_OBSERVATION_MIN_FACTS=3 \
-e HINDSIGHT_API_OBSERVATION_TOP_ENTITIES=10 ...
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Enable the operation validator extension to be loaded from environment
configuration and passed to MemoryEngine, allowing pre/post operation
hooks for usage metering, rate limiting, and audit logging.
Task handlers were swallowing exceptions, causing operations to be
marked as completed even when they failed. This prevented the retry
logic in execute_task() from working and led to accumulation of
pending operations that never completed.
Fixed handlers:
- _handle_batch_retain: remove try/except wrapper
- _handle_access_count_update: remove try/except wrapper
- _handle_regenerate_observations: remove outer try/except, keep
inner one for individual entity failures