Commit graph

465 commits

Author SHA1 Message Date
Nicolò Boschi
ff55283018
doc: changelog and blog post for 0.4.11 (#363)
* doc: changelog and blog post for 0.4.11

* doc: changelog and blog post for 0.4.11
2026-02-13 11:45:41 +01:00
Nicolò Boschi
b3b541fc53 Release v0.4.11
- Update version to 0.4.11 in all components
- Regenerate OpenAPI spec and client SDKs
- Python packages: hindsight-api, hindsight-dev, hindsight-all, hindsight-litellm, hindsight-embed
- Python client: hindsight-clients/python
- TypeScript client: hindsight-clients/typescript
- Rust CLI: hindsight-cli
- Control Plane: hindsight-control-plane
- OpenClaw integration: hindsight-integrations/openclaw
- AI SDK integration: hindsight-integrations/ai-sdk
- Helm chart
- Sync documentation to version-0.4
2026-02-13 10:52:14 +01:00
Nicolò Boschi
4f112101ac
fix(openclaw): avoid memory retain recursion (#362) 2026-02-13 10:47:39 +01:00
Nicolò Boschi
e408b7e072
feat: support litellm-sdk as reranker and embeddings (#357)
* feat: support litellm-sdk for reranker endpoint

* feat: support litellm-sdk for reranker endpoint

* fix: make litellm SDK cohere test fixture async function-scoped

* fix: store litellm module reference during initialization to avoid import issues

* feat: add LiteLLM SDK embeddings support

- Add LiteLLMSDKEmbeddings class for direct API access without proxy
- Support multiple providers: Cohere, OpenAI, Together AI, HuggingFace, Voyage AI
- Automatic dimension detection via test embedding
- Provider-specific API key mapping
- Batch processing support (configurable batch size)
- Comprehensive test coverage (17 unit tests)
- Update documentation with configuration examples

Implements embeddings in same PR as reranker per user request

* fix: correct config mocking in embeddings factory tests

- Mock get_config() from its source module (hindsight_api.config)
- Fixes factory tests that were returning LocalSTEmbeddings instead of LiteLLMSDKEmbeddings
- All 17 unit tests now passing

* fix: skip Cohere integration tests when API key is invalid

- Catch initialization errors and skip tests instead of failing
- Prevents CI failures when COHERE_API_KEY is set but invalid
- Integration tests now properly skip when authentication fails

* fix: skip Cohere reranker integration tests when API key is invalid

- Add same error handling as embeddings tests
- Prevents CI failures when COHERE_API_KEY is set but invalid
- Tests now properly skip when authentication fails

* Revert "fix: skip Cohere reranker integration tests when API key is invalid"

This reverts commit 655dacaffb25851ff48e202b4379fc8332a66df7.

* Revert "fix: skip Cohere integration tests when API key is invalid"

This reverts commit 5d00548e39faa3da6b427ace89589a216816f9e7.

* fix: pass API key directly to litellm SDK functions

- Add api_key parameter to arerank(), rerank(), aembedding(), and embedding() calls
- Prevents authentication issues in multi-process environments (pytest-xdist)
- More reliable than relying solely on environment variables
- Update test assertions to expect api_key parameter

* feat: pass api_base parameter to litellm SDK calls and remove hasattr check

* fix: raise errors instead of silently returning 0.0 scores

* refactor: pass API keys directly in kwargs instead of setting env vars
2026-02-12 23:53:12 +01:00
Nicolò Boschi
d871c3009d
feat: support timescale pg_textsearch as text search extension (#359)
* feat: support timescale pg_textsearch as text search extension

* refactor: deduplicate text search query in retrieve_semantic_bm25_combined

Instead of maintaining 3 complete query copies (native, vchord, pg_textsearch),
now we:
- Build backend-specific parts (score_expr, order_by, where_filter)
- Use a single query template with injected backend-specific parts

This makes maintenance easier - changes to the semantic CTE or overall structure
only need to be made once.
2026-02-12 17:38:13 +01:00
DK09876
d8376ecf6b
Fix incorrect MCP tool parameters in docs (#358)
- Remove phantom `max_results` param from recall (only has query + max_tokens)
- Remove `budget` param from local-mcp recall (only reflect has budget)
- Add missing `name` and `mission` optional params to create_bank
- Add missing `mental_model_id` optional param to create_mental_model

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 08:52:05 -07:00
Nicolò Boschi
71e408c27b
chore: remove dead code (#356)
* chore: remove dead code

* chore: remove dead code

* feat: support litellm-sdk for reranker endpoint
2026-02-12 14:44:00 +01:00
Nicolò Boschi
c029807add
feat: support for other text and vector search pg extensions (#355)
* feat: support for other text and vector search pg extensions

* test: increase timeout for test_batch_chunking_behavior to account for VectorChord BM25 tokenization overhead

* feat: support for other text and vector search pg extensions
2026-02-12 14:13:04 +01:00
Nicolò Boschi
8d731f2e5f
feat: implement hierarchical configuration (system, tenant, bank) (#329)
* feat: implement hierarchical configuration (system, tenant, bank)

* feat: implement hierarchical configuration (system, tenant, bank)

* docs: add instructions for hierarchical config in CLAUDE.md

* feat: add ENABLE_BANK_CONFIG_API flag (disabled by default)

- Add HINDSIGHT_API_ENABLE_BANK_CONFIG_API env var (default: false)
- Return 403 Forbidden from bank config endpoints when disabled
- Update tests to enable the flag
- Update CLAUDE.md documentation

This provides security control over the bank configuration API,
ensuring it's only accessible when explicitly enabled.

* docs: add hierarchical configuration section

* feat(cli): add bank config commands (config, set-config, reset-config)

- Add 'hindsight bank config' to view bank configuration
- Add 'hindsight bank set-config' to update LLM settings per bank
- Add 'hindsight bank reset-config' to reset to defaults
- Implements client API calls to new bank config endpoints

* fix(cli): fix compilation errors in bank config commands

- Fix type signature: use ApiClient instead of api::Client
- Fix confirmation: use ui::prompt_confirmation instead of ui::confirm
- Fix error handling: use anyhow! macro instead of errors::Error
- Fix type conversion: convert HashMap to serde_json::Map for API call

* feat: implement type-safe hierarchical config with bank overrides

Implements a production-ready hierarchical configuration system that prevents
accidentally using global defaults when bank-specific overrides exist.

- Created StaticConfigProxy that wraps HindsightConfig
- get_config() now returns proxy that blocks access to bank-configurable fields
- Raises ConfigFieldAccessError with clear message when accessing configurable fields
- Added _get_raw_config() for internal use only
- Forces developers to use resolve_full_config(bank_id, context) for bank settings

- Added resolve_full_config() method that returns complete HindsightConfig
- Resolves hierarchy: Global (env) → Tenant → Bank
- No caching to support multi-server deployments (always fresh from DB)
- LLM provider pooling handles expensive operations separately

- Updated entire retain pipeline to pass resolved config through call chain
- memory_engine.py: Resolves config at top level where bank_id/context available
- orchestrator.py: Accepts and passes config to fact_extraction
- fact_extraction.py: Uses passed config instead of get_config()
- utils.py: Added optional config param for backward compatibility

- consolidator.py: Uses resolve_full_config() for enable_observations check
- memory_engine.py: Resolves config before triggering consolidation

- Renamed "Memory Bank" to "Bank Configuration" with tabs
- Combined Stats and Operations into "General" tab
- Consolidated Profile and Configuration into "Configuration" tab
- Moved Actions dropdown to page level (outside tabs)

- Created new component for managing bank-specific config
- Displays configurable fields: retain_chunk_size, retain_extraction_mode, etc.
- Edit via dialog with form validation
- Reset to defaults via AlertDialog confirmation
- Shows field IDs in monospace for clarity
- Visual separation with borders and hover effects

- Removed inline edit mode, switched to dialog-based editing
- Separate dialogs for Disposition and Mission editing
- Read-only display with clear edit buttons
- Removed duplicate stats cards and operations

- bank-stats-view.tsx: Overview statistics (memories, links, documents, pending ops)
- bank-operations-view.tsx: Background operations table with filtering

**Problem**: Consolidation always used global enable_observations, ignoring bank overrides
**Root Cause**: consolidator.py called get_config() instead of resolving bank-specific config
**Solution**: Pass resolved config through the entire pipeline

**Problem**: asyncpg returning JSONB as JSON string instead of parsed dict
**Solution**: Explicit JSON parsing in config_resolver.py with type checking

- All 19 API integration tests pass
- All 10 hierarchical config tests pass
- Retain operations work correctly with bank-specific config
- Consolidation respects bank-specific enable_observations setting

- Updated developer/configuration.md with type-safe config access pattern
- Added examples showing correct usage patterns
- Documented ConfigFieldAccessError and resolution methods

- get_config() now returns StaticConfigProxy (blocks configurable field access)
- Code accessing bank-configurable fields must use resolve_full_config()
- Clear migration path with helpful error messages

Fixes hierarchical configuration to be production-ready with proper type safety.

* refactor: remove LLM client pool and simplify config resolver

Since LLM config (provider, model, api_key) is now static and not
bank-configurable, the LLMClientPool is no longer needed.

Changes:
- Remove hindsight_api/llm_client_pool.py (no longer needed)
- Remove memory_engine._get_bank_llm_config() (dead code, never called)
- Simplify config_resolver.py by eliminating duplication between
  resolve_full_config() and get_bank_config()
- get_bank_config() now calls resolve_full_config() and filters results
- Remove outdated "LLM provider pooling" comments from docstrings

All tests pass (10 hierarchical config tests, 19 API integration tests)

* fix: update tests to use _get_raw_config() for configurable fields

Fixed test fixtures that were accessing configurable fields (like
enable_observations) from get_config(), which now raises
ConfigFieldAccessError due to type-safe config access.

Changes:
- test_consolidation.py: Changed enable_observations fixture to use
  _get_raw_config() instead of get_config()
- test_consolidation.py: Updated test_consolidation_returns_disabled_status
  to set bank config instead of mocking get_config()
- test_link_expansion_retrieval.py: Changed fixture to use _get_raw_config()
- test_observations.py: Changed disable_observations fixture to use
  _get_raw_config()
- Regenerated OpenAPI spec and clients

All 39 previously failing tests now pass.

* fix: add missing config parameter to test calls of extract_facts_from_text()

Fixed 45 test failures where tests were calling extract_facts_from_text()
without the new required config parameter.

Changes:
- Added config=_get_raw_config() to all extract_facts_from_text() calls
- Fixed test_main_module.py to patch _get_raw_config instead of get_config
- Updated 6 test files with 37 function call sites

All tests should now pass.

* fix: add missing config parameter to test_skip_podcast_meta_commentary

One more test was missing the config parameter for extract_facts_from_text().
2026-02-12 13:14:57 +01:00
Nicolò Boschi
f9a8a8e01e
fix: resolve based_on schema/serialization issues in reflect API (#348)
* fix: add default values to OpenAPI schema for default_factory fields

This commit fixes the OpenAPI schema to include default values for fields
using default_factory, which improves schema accuracy and client generation.

Changes:
1. Added FieldWithDefault() helper to inject default values into OpenAPI schema
2. Updated 14 fields using default_factory to include defaults in schema:
   - ReflectBasedOn.{memories, mental_models, directives}
   - ReflectTrace.{tool_calls, llm_calls}
   - All tags fields
   - All trigger fields
   - All include fields

3. Regenerated OpenAPI spec with proper defaults

4. Added tests to verify API returns correct format with empty banks

Note: This fixes the schema but doesn't change the v0.3.0 -> v0.4.0 breaking
change where based_on went from list to object. Clients should handle both
formats for backward compatibility.

* fix: remove client imports from API test

The test was failing in CI because it imported the client library
which isn't installed in the API test environment.

Changed to test only API JSON response format, not client parsing.
This is more appropriate for an API test anyway.

* test: add client tests for ReflectResponse parsing

Added comprehensive tests in hindsight-clients/python/tests to verify:
- v0.4.0+ format with empty based_on object
- v0.4.0+ format with null based_on
- v0.4.0+ format with populated facts
- v0.3.0 format (list) correctly fails validation
- Missing based_on field handling

These tests document the v0.3.0 -> v0.4.0 breaking change where
based_on changed from list to object.
2026-02-12 11:26:12 +01:00
Damien
a713b68b1f
Implement the vchord / pgvector support (#350)
* Implement the vchord / pgvector support

* feat(alembic): detect vector extension and create appropriate index
2026-02-12 10:47:07 +01:00
Nicolò Boschi
93ddd41621
feat: add reverse proxy support (#346)
* feat: add reverse proxy support

* improve

* improve

* improve

* improve

* improve

* fix: update integration test to use modern 'docker compose' command

- Replace 'docker-compose' with 'docker compose' (Docker Compose v2+)
- Add fallback to legacy docker-compose command for compatibility
- Fixes test failures on systems using Docker Compose plugin

* ci: trigger test rerun

* fix: make docker-compose detection more robust for CI

- Add get_docker_compose_command() to detect available command
- Use shutil.which() to check command availability
- Dynamically use correct command (docker compose vs docker-compose)
- Should work in both modern and legacy Docker environments

* fix: docker-compose networking in base path integration test

Fix connection refused error in test_reverse_proxy_simple_config by
handling host vs bridge networking modes correctly:

- Linux (host mode): nginx listens on 18080 directly, no port mapping
- Mac/Windows (bridge mode): nginx listens on 80, mapped to 18080

With host networking, port mappings in docker-compose don't work since
the container binds directly to the host's network namespace.
2026-02-12 10:09:53 +01:00
DK09876
7ee229ba23
Fix MCP extra args rejection and bank ID resolution priority (#351)
* Fix MCP extra args rejection and bank ID resolution priority

Two fixes to the MCP middleware:

1. Strip unknown tool arguments: LLMs frequently add extra fields
   like "explanation" to tool calls. FastMCP's Pydantic TypeAdapter
   rejects these with "Unexpected keyword argument". The middleware
   now intercepts tools/call requests and removes unknown fields
   before they reach validation.

2. Bank ID resolution priority: Path now takes priority over header.
   Previously X-Bank-Id header was checked first, meaning /mcp/my-bank/
   with X-Bank-Id: other-bank would silently use other-bank in multi-bank
   mode. Now the URL path is authoritative — single-bank mode connections
   cannot be overridden by headers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update MCP server docs with mental model tools and fixes

- Add all mental model tools (create, list, get, update, delete, refresh)
- Add list_banks and create_bank tool docs
- Document single-bank vs multi-bank modes
- Fix bank selection priority: path > header > default
- Add Accept header to curl example
- Add timestamp param to retain, max_tokens to recall

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 10:09:28 +01:00
Chris Latimer
29c0890f22 Copy page button on docs 2026-02-11 19:21:24 -07:00
Chris Bartholomew
a1f22dabd2
Replace waitlist links with direct Hindsight Cloud signup URL (#349)
The waitlist is no longer needed. Update all references from
vectorize.io/hindsight/cloud to ui.hindsight.vectorize.io/signup
and change "request early access" language to "sign up".
2026-02-11 20:51:32 +01:00
Nicolò Boschi
60574ee08f
fix: add trust_code env config (#347)
* fix: add trust_code env config

* doc
2026-02-11 17:06:59 +01:00
Nicolò Boschi
7d95a002c7
fix: improve model configuration for litellm gateway (#345)
* fix: improve model configuration for litellm gateway

* fix: add missing config imports for Cohere and LiteLLM providers

Add missing DEFAULT_* and ENV_* constants to cross_encoder.py and embeddings.py imports:
- DEFAULT_RERANKER_COHERE_MODEL
- DEFAULT_LITELLM_API_BASE
- DEFAULT_RERANKER_LITELLM_MODEL
- DEFAULT_EMBEDDINGS_COHERE_MODEL
- DEFAULT_EMBEDDINGS_LITELLM_MODEL
- ENV_RERANKER_COHERE_MODEL

This fixes NameError failures in test-api, test-hindsight-all, and test-upgrade CI jobs.
2026-02-11 11:24:26 +01:00
Chris Bartholomew
83ca669011
Add actual LLM token usage fields to RetainResult (#342)
* Add actual LLM token usage fields to RetainResult

RetainResult now carries llm_input_tokens, llm_output_tokens, and
llm_total_tokens populated from the engine's TokenUsage, so downstream
operation validator extensions can access actual LLM token counts.

* Test that RetainResult includes actual LLM token usage
2026-02-11 10:41:41 +01:00
DK09876
e798979733
Harden MCP server: fix routing, validation, and usage metering (#341)
* fix: move mental model usage metering into engine for MCP support

Mental model validation hooks (validate_mental_model_get, validate_mental_model_refresh)
were only called in REST HTTP handlers, not in the engine. MCP tools call engine methods
directly, so usage metering was skipped entirely for MCP mental model operations.

Moved pre-validation and post-completion hooks into memory_engine.py (matching the
retain/recall/reflect pattern) and removed the duplicate code from http.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove double validation from create_mental_model and add internal checks

- Remove pre-validation from create_mental_model since callers always call
  submit_async_refresh_mental_model next (which validates), preventing
  double credit checks
- Add is_internal checks to mental model metering validators (matching
  the existing pattern for recall/reflect) so background worker tasks
  skip billing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent 307 redirect on /mcp that breaks MCP tool discovery

Starlette's Mount class redirects /mcp to /mcp/ with a 307 Temporary
Redirect. Many MCP clients don't follow POST redirects, which causes
tool discovery to fail (0 tools discovered despite successful auth).

Add _MCPPathRewriteMiddleware that rewrites /mcp to /mcp/ at the ASGI
level before routing, preventing the redirect entirely. Both /mcp and
/mcp/ now work identically.

Add regression test test_mcp_no_trailing_slash_works to verify URLs
with and without trailing slashes discover tools correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* harden MCP server for real-world usage

- Remove MCP_ENDPOINTS blocklist so banks named "sse"/"messages" route correctly
- Scope SSE body rewriting to text/event-stream responses only to prevent data corruption
- Add _validate_mental_model_inputs for name, source_query, max_tokens validation in MCP tools
- Improve "not found" error messages to include bank_id context
- Fix fragile tool count assertions (exact → minimum bounds)
- Add integration tests: tool execution, input validation, edge-case bank names
- Add unit tests for validation helper and tool-level validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: replace Mount + rewrite middleware with wrapping middleware

Starlette's Mount class redirects /mcp -> /mcp/ with 307, which MCP clients
don't follow. Previously we patched this with _MCPPathRewriteMiddleware.

Now MCPMiddleware wraps the FastAPI app directly via add_middleware, intercepting
/mcp* requests before they reach Starlette's router. No Mount means no redirect.

- Remove _MCPPathRewriteMiddleware (no longer needed)
- Remove app.mount() call
- Add prefix parameter to MCPMiddleware
- Use app.add_middleware() for proper Starlette integration
- Simplify path stripping (just remove prefix, no mount/root_path handling)
- Update routing test to match current behavior (no MCP_ENDPOINTS blocklist)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update stale docstring referencing removed _MCPPathRewriteMiddleware

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 10:41:20 +01:00
Anton Evseev
43f9a8bec2
feat(helm): TEI reranker and embedding as separate Deployments (#333)
Refactor TEI from sidecar (PR #333) to standalone Deployment+Service
pairs for independent scaling. Adds embedding support alongside reranker.

- New tei-reranker-deployment.yaml and tei-reranker-service.yaml
- New tei-embedding-deployment.yaml and tei-embedding-service.yaml
- Auto-inject RERANKER/EMBEDDINGS provider and URL env vars on API pod
- Config restructured under tei.reranker.* and tei.embedding.* in values
- Both disabled by default, opt-in via tei.reranker.enabled / tei.embedding.enabled

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 10:39:41 +01:00
DK09876
f641b30d83
feat: add mental model CRUD tools to MCP server (#337)
* Add mental model CRUD tools to MCP server

Expose mental models (pinned reflections) as 6 new MCP tools:
- list_mental_models: List with optional tag filtering
- get_mental_model: Get by ID
- create_mental_model: Create with async content generation
- update_mental_model: Update name/source_query/tags
- delete_mental_model: Delete by ID
- refresh_mental_model: Re-run source query to update content

Both multi-bank (bank_id param) and single-bank modes supported,
following the same patterns as existing retain/recall/reflect tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: include mental model tools in single-bank MCP mode and update tests

The single-bank mode tool set was hardcoded to only retain/recall/reflect,
excluding the new mental model tools. Updated all 3 test layers (unit,
routing, HTTP integration) to assert mental model tool exposure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update extension test tool count for mental model tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: move mental model usage metering into engine for MCP support

Mental model validation hooks (validate_mental_model_get, validate_mental_model_refresh)
were only called in REST HTTP handlers, not in the engine. MCP tools call engine methods
directly, so usage metering was skipped entirely for MCP mental model operations.

Moved pre-validation and post-completion hooks into memory_engine.py (matching the
retain/recall/reflect pattern) and removed the duplicate code from http.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove double validation from create_mental_model and add internal checks

- Remove pre-validation from create_mental_model since callers always call
  submit_async_refresh_mental_model next (which validates), preventing
  double credit checks
- Add is_internal checks to mental model metering validators (matching
  the existing pattern for recall/reflect) so background worker tasks
  skip billing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 22:40:43 +01:00
Chris Bartholomew
90be7c6829
Add user_initiated flag to RequestContext for async task attribution (#338)
Async batch retain tasks need internal=True to bypass extension auth
(worker has no API key), but extensions also need to know the operation
originated from a user request. The new user_initiated flag on
RequestContext allows extensions to distinguish user-initiated async
operations from truly internal system operations like consolidation.
2026-02-10 22:37:42 +01:00
Nicolò Boschi
6eec83b20d
fix: include tiktoken in slim image (#336) 2026-02-10 17:26:38 +01:00
Nicolò Boschi
dd1e0986a1
feat: add docs skill (#335)
* feat: add docs skill

* feat: add docs skill
2026-02-10 14:41:51 +01:00
Nicolò Boschi
69dec8ec34
feat: add otel traceability (#330)
* feat: add comprehensive OpenTelemetry tracing

- Add tool execution spans for reflect operations
- Add tool call information (names, params) to spans
- Change verification scope from 'test' to 'verification'
- Add hindsight.reflect_generation span for done() processing
- Implement no-op tracer for improved code readability
- Update documentation for OTEL configuration
- Resolve merge conflicts from rebase

* fix: properly serialize Pydantic models in span recording

- Add _serialize_for_span() helper to handle Pydantic models
- Update all providers to use the helper function
- Fixes test failures with 'Object of type X is not JSON serializable'

* feat: add Grafana LGTM stack for unified local observability

Add Grafana LGTM (Loki, Grafana, Tempo, Mimir) as the recommended
local development observability stack. This provides traces, metrics,
and logs in a single Docker container instead of separate tools.

Changes:
- Add scripts/dev/grafana/ with docker-compose and README
- Add scripts/dev/start-grafana.sh startup script
- Update .env.example to reference Grafana LGTM
- Update configuration docs to emphasize Grafana LGTM as primary option
- Reorder OTLP backend list to show Grafana LGTM first

Benefits:
- Single container vs multiple separate tools (Jaeger, SigNoz, etc.)
- ~515MB image with full observability stack
- Compatible with existing OTLP configuration
- Simpler local development setup

* chore: remove SigNoz scripts and references

Remove SigNoz observability stack in favor of Grafana LGTM as the
sole recommended local development tracing solution.

Changes:
- Delete scripts/dev/signoz/ directory and all SigNoz configurations
- Delete scripts/dev/start-signoz.sh startup script
- Remove SigNoz references from .env.example
- Remove SigNoz from OTLP backends list in configuration docs

Grafana LGTM provides the same capabilities (traces, metrics, logs)
in a simpler single-container setup.

* feat: add consolidation span hierarchy for tracing

Add parent-child span structure for consolidation operations:
- hindsight.consolidation: Parent span for each memory being processed
- hindsight.consolidation_recall: Child span for finding related observations
- LLM call span: Automatically created by LLM provider (scope="consolidation")

This enables detailed timing breakdown in Grafana Tempo:
- Total consolidation time per memory
- Time spent in recall
- Time spent in LLM call
- Time spent executing actions (create/update)

All consolidation tests pass (31/31).

* feat: add Prometheus metrics and GenAI dashboard to Grafana stack

Add comprehensive metrics and dashboarding to the Grafana LGTM stack:

Metrics Collection:
- Configure Prometheus to scrape Hindsight API /metrics endpoint
- Scrape interval: 10 seconds
- Targets hindsight-api on host.docker.internal:8888

GenAI Dashboard:
- Pre-configured dashboard with 6 panels:
  - LLM call rate (by provider/model)
  - LLM call duration (p50/p95 by scope)
  - Token usage - input tokens/sec by scope
  - Token usage - output tokens/sec by scope
  - Operations rate (retain/recall/reflect/consolidation)
  - Operation duration p95 by operation type

Configuration:
- Mount prometheus.yml for metrics scraping
- Mount dashboards directory for auto-provisioning
- Add host.docker.internal mapping for container->host access
- Dashboard provisioning with auto-reload every 10s

Documentation:
- Updated README with metrics viewing instructions
- Added PromQL query examples
- Documented dashboard access and navigation

This provides full observability: traces (Tempo) + metrics (Prometheus/Mimir) + dashboards (Grafana)

* refactor: merge Grafana setup into existing monitoring stack

Consolidate the separate scripts/dev/grafana/ setup into the existing
scripts/dev/monitoring/ stack, using Grafana LGTM (Loki, Grafana, Tempo, Mimir).

Changes:
- Remove separate scripts/dev/grafana/ directory and start-grafana.sh
- Rewrite scripts/dev/monitoring/start.sh to use Docker + Grafana LGTM
  (was: download native Prometheus/Grafana binaries)
- Add docker-compose.yaml for Grafana LGTM container
- Add prometheus.yml for scraping Hindsight API metrics
- Mount existing dashboards from monitoring/grafana/dashboards/
- Add comprehensive README.md

Benefits:
- Single unified monitoring command: ./scripts/dev/start-monitoring.sh
- Uses existing dashboard files (hindsight-operations, hindsight-llm, hindsight-api-service)
- Simpler setup: Docker-based vs downloading/running native binaries
- Full observability: traces + metrics + logs + dashboards in one container
- Standard ports: Grafana on 3000, OTLP on 4317/4318

Architecture:
- Grafana LGTM container (~515MB) provides all components
- Dashboards auto-provisioned from monitoring/grafana/dashboards/
- Prometheus scrapes host.docker.internal:8888/metrics
- Shared hindsight-network for future service-to-service tracing

* fix: run monitoring stack in foreground for easy Ctrl+C stop

Change docker-compose from detached (-d) to foreground mode.
Users can now stop the stack with Ctrl+C instead of needing
to run docker-compose down separately.

* fix: remove invalid home dashboard path and obsolete version field

- Remove GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH environment variable
  (was pointing to wrong path causing 'Failed to load home dashboard' error)
- Remove obsolete 'version' field from docker-compose.yaml
  (docker-compose v2+ doesn't require version field)

* fix: load Hindsight dashboards in Grafana LGTM

Mount Hindsight dashboard JSON files and custom provisioning config
to make dashboards visible in Grafana.

Changes:
- Mount hindsight-operations.json, hindsight-llm.json, hindsight-api-service.json to /otel-lgtm/
- Create grafana-dashboards.yaml with all dashboard providers (default + Hindsight)
- Mount custom provisioning config to override LGTM default

All 3 Hindsight dashboards now appear in Grafana UI with metrics
from Prometheus scraping the Hindsight API /metrics endpoint.

* fix: configure Prometheus to scrape Hindsight API metrics

Update prometheus.yml to include both OTLP receiver config (from LGTM)
and scrape_configs for pulling metrics from Hindsight API.

Changes:
- Mount prometheus.yml to /otel-lgtm/prometheus.yaml (where LGTM reads it)
- Add scrape_configs section to pull from host.docker.internal:8888/metrics
- Keep OTLP receiver configuration for trace metrics
- Set scrape_interval to 5s

Verified: Prometheus now successfully scrapes hindsight_llm_calls_total
and other Hindsight metrics. Dashboards now show live data!

* feat: add comprehensive tracing for recall and improve reflect/mental_model_refresh spans

- Add recall operation tracing with parent-child span hierarchy
  - Parent: hindsight.recall with attributes (bank_id, query, fact_types, etc.)
  - Children: recall_embedding, recall_retrieval, recall_fusion, recall_rerank
  - Fixed context propagation using start_as_current_span()

- Improve reflect tracing spans
  - Remove reflect_generation spans, use reflect instead
  - Change done() tool processing to hindsight.reflect_tool_call

- Fix mental_model_refresh span nesting
  - Add _skip_span parameter to reflect_async to avoid duplicate hindsight.reflect spans
  - Mental model refresh now has clean span hierarchy without nested reflect parent

- Add comprehensive tracing verification tests
  - Test span hierarchy and attributes for all operations
  - Verify parent-child relationships
  - 5 passing tests covering recall, reflect, consolidation, and mental_model_refresh

* refactor: remove redundant is_tracing_enabled() checks

- Remove all is_tracing_enabled() conditional checks before tracing calls
- NoOpTracer/NoOpSpan handle disabled tracing automatically
- Simplify code by always calling tracer methods directly
- Fix NoOpTracer.start_as_current_span() to yield NoOpSpan instead of None

Changes:
- memory_engine.py: Remove 5 is_tracing_enabled checks in recall spans
- agent.py: Remove 2 is_tracing_enabled checks in reflect tool spans
- tracing.py: Fix NoOpTracer context manager to yield proper NoOpSpan

This eliminates ~50 lines of redundant conditional code while maintaining
identical behavior.

* docs: simplify distributed tracing section in monitoring.md

- Make tracing documentation more concise
- Focus on span hierarchy and attributes
- Remove verbose troubleshooting and performance sections
- Keep configuration.md for env vars only
2026-02-10 12:20:48 +01:00
DK09876
888b50de12
Fix MCP operations not tracked for usage metering (#334)
MCP middleware was discarding tenant_id and api_key_id after authentication.
The authenticate_mcp() call mutated a RequestContext with these fields, but
tools later created a fresh RequestContext without them. This caused
UsageMeteringValidator to see tenant_id="unknown" and skip billing entirely.

Propagate tenant_id and api_key_id via ContextVars (same pattern as bank_id
and api_key) so the RequestContext passed to the memory engine has the full
auth context needed for usage tracking.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 09:38:15 +01:00
Dewaldt Huysamen
fb7be3eced
feat(openclaw): add excludeProviders config to skip recall/retain for specific providers (#332)
Adds an `excludeProviders` option to the OpenClaw plugin config that allows
users to specify message providers (e.g. 'telegram', 'discord') to exclude
from Hindsight memory recall and retention.

Closes #331

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:28:29 +01:00
Chris Latimer
4499254f6d Memory conflict blog post 2026-02-09 11:18:30 -07:00
Anatolii Lapytskyi
9943957fb7
feat(helm): add PDB and per-component affinity support (#327)
Add PodDisruptionBudget templates for api, control plane, and worker
(disabled by default). Support per-component affinity overrides with
backward-compatible global affinity fallback.
2026-02-09 18:03:37 +01:00
Nicolò Boschi
03f47e29c8
fix(helm): gke overriding HINDSIGHT_API_PORT (#328) 2026-02-09 17:59:14 +01:00
Nicolò Boschi
1240b82629 0.4.10 changelog 2026-02-09 12:08:47 +01:00
Nicolò Boschi
08f1cda3bf Release v0.4.10
- Update version to 0.4.10 in all components
- Regenerate OpenAPI spec and client SDKs
- Python packages: hindsight-api, hindsight-dev, hindsight-all, hindsight-litellm, hindsight-embed
- Python client: hindsight-clients/python
- TypeScript client: hindsight-clients/typescript
- Rust CLI: hindsight-cli
- Control Plane: hindsight-control-plane
- OpenClaw integration: hindsight-integrations/openclaw
- AI SDK integration: hindsight-integrations/ai-sdk
- Helm chart
- Sync documentation to version-0.4
2026-02-09 11:44:20 +01:00
Nicolò Boschi
a3a9d7b37d
doc: prepare doc for 0.4.10 (#325)
* doc: prepare doc for 0.4.10

* fixe

* ci
2026-02-09 11:42:37 +01:00
Nicolò Boschi
c2607d7699
fix(helm): improve appVersion usage (#326) 2026-02-09 11:35:08 +01:00
Jerry Henley
e99ee0f243
Add Supabase tenant extension as built-in (#267)
Move the Supabase tenant extension into the hindsight-api package so users
can enable it with just an environment variable — no file copying or Docker
image modifications needed.

Key improvements over the original submission:
- JWKS-based local JWT verification (no network call per request) with
  automatic fallback to /auth/v1/user for legacy HS256 projects
- Service key is now optional (only needed for HS256 or health checks)
- UUID validation on user IDs before schema name construction
- Schema prefix validation against Postgres identifier rules
- Key rotation handling with automatic JWKS cache refresh
- Proper logging via Python logging module
- Tenant extension lifecycle hooks (on_startup/on_shutdown) wired into
  the server lifespan
- Public tenant_extension property on MemoryEngine
- 54 unit tests covering both verification modes, cache behavior, error
  paths, and the extension loader
- README updated to reflect JWKS-first architecture

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-09 10:16:47 +01:00
Van Vuong Ngo
c568094b8c
fix: do not log db user/password (#312)
* fix: security vulnerability - exposed sensitve database credentials in logs

* add comment

* fix: mask credentials of the postgeSQL connection string
2026-02-09 10:15:03 +01:00
Van Vuong Ngo
5179d5f77d
feat: add docker-compose example (#313)
* feat: add docker-compose example

* fix T&V

* doc: add how to quick start hindsight with docker-compose

* chore: fix typo
2026-02-09 10:14:21 +01:00
Anton Evseev
981cf6057f
fix(openclaw): prevent memory wipe on every session (#323)
Use unique document_id per conversation (sessionKey + timestamp) instead
of static sessionKey. The backend CASCADE-deletes old memories when the
same document_id is reused, causing all prior facts to be lost.

Also:
- Universal envelope stripping for all channels (was Telegram-only)
- Prefer rawMessage over prompt for cleaner recall queries
- Increase recall max_tokens from 512 to 2048

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:13:19 +01:00
Nicolò Boschi
d90588b3e1
feat: improve mcp tools based on endpoint (#318)
* feat: improve mcp tools based on endpoint

* feat: improve mcp tools based on endpoint

* test: add integration test for MCP endpoint routing

- Add test_mcp_endpoint_routing.py to verify single-bank vs multi-bank tool exposure
- Verifies /mcp/ exposes all tools with bank_id parameters
- Verifies /mcp/{bank_id}/ only exposes scoped tools without bank_id parameters
- Regression test for issue #317

Related: #317, #318

* test: use StreamableHTTP client for MCP endpoint routing test

Replace httpx AsyncClient SSE parsing with proper MCP StreamableHTTP
client. This correctly tests the MCP server using the actual protocol
that clients will use.

Fixes #317
2026-02-08 09:28:59 +01:00
Van Vuong Ngo
d0f67c9f8b
doc: improve Node.js client example (#320)
Fix doc to increase the developer experience...

- if the code is intended to be a CommonJS by using `require` then you have to wrap `await` calls in an async function
- calling `client.recall` with using the results
2026-02-07 10:02:41 +01:00
DK09876
fedfb494ee
feat: add TenantExtension auth to MCP endpoint (#286)
* feat: add TenantExtension auth to MCP endpoint

Replace static MCP_AUTH_TOKEN check with TenantExtension authentication,
making MCP use the same auth path as REST API.

- MCPMiddleware now calls tenant_extension.authenticate()
- Sets _current_schema from TenantContext for multi-tenant isolation
- Returns 401 on AuthenticationError (same as REST API)
- DefaultTenantExtension: no auth (local dev)
- ApiKeyTenantExtension: validates against env var
- CloudTenantExtension: HMAC + DB lookup (production)

Adds tests for middleware auth rejection, acceptance, and schema routing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review: backwards compatibility for MCP auth

- Keep MCP_AUTH_TOKEN env var for legacy MCP servers
- Add authenticate_mcp() method to TenantExtension base class
  - Default implementation calls authenticate()
  - Extensions can override to opt-out of MCP auth
- Add mcp_auth_disabled config option to ApiKeyTenantExtension
  - Set HINDSIGHT_API_TENANT_MCP_AUTH_DISABLED=true to skip MCP auth
- Remove CloudTenantExtension from public docstring
- Add tests for legacy auth token and mcp_auth_disabled flag
- Update MCP docs with new auth configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add search_docs MCP tool for documentation search

Implements a new MCP tool that searches Hindsight documentation using
Vectorize RAG pipelines. The tool supports:
- Searching core (OSS) docs, cloud docs, or both
- Configurable number of results (1-10)
- Returns ranked results with URLs, similarity scores, and text snippets

New environment variables:
- HINDSIGHT_API_VECTORIZE_ORG_ID
- HINDSIGHT_API_VECTORIZE_API_TOKEN
- HINDSIGHT_API_VECTORIZE_CORE_PIPELINE_ID
- HINDSIGHT_API_VECTORIZE_CLOUD_PIPELINE_ID
- HINDSIGHT_API_VECTORIZE_API_BASE_URL

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add documentation for search_docs MCP tool

- Add Vectorize environment variables to configuration.md
- Add search_docs tool to MCP server available tools
- Add reflect tool documentation (was missing)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add tests for search_docs MCP tool

Tests cover:
- DocsSource enum values and parsing
- _clean_text HTML stripping helper
- _search_vectorize_pipeline with mocked httpx
- Tool registration and function execution
- Source filtering (core/cloud/all)
- Result sorting by similarity
- Error handling for pipeline failures
- HTML cleaning in results
- Invalid source defaulting to 'all'

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Move search_docs to hindsight-cloud, add MCPExtension pattern

- Add MCPExtension base class for registering additional MCP tools
- Load MCPExtension in create_mcp_server when configured
- Remove search_docs tool (moved to hindsight-cloud CloudMCPExtension)
- Remove Vectorize config from hindsight-core
- Add tests for MCPExtension pattern
- Update docs to remove search_docs references

The MCPExtension pattern allows cloud (or any extension package) to
register additional MCP tools via:
  HINDSIGHT_API_MCP_EXTENSION=package.module:ExtensionClass

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review feedback

- Remove CloudTenantExtension mention from MCPMiddleware docstring
- Fix docs: clarify that ApiKeyTenantExtension must be explicitly enabled
- Revert changes to versioned docs (0.3 and 0.4) - synced automatically on release

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Format mcp.py line length

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 12:28:05 -07:00
Nicolò Boschi
0430588e32
fix: hindsight-embed profiles are not loaded correctly (#316)
* fix: hindsight-embed profiles are not loaded correctly

* fix: hindsight-embed profiles are not loaded correctly
2026-02-06 17:13:54 +01:00
Nicolò Boschi
2af0e08dba
doc: update claude-code usage terms (#315)
* doc: update claude-code usage terms

* doc: update claude-code usage terms

* doc: update claude-code usage terms
2026-02-06 16:53:12 +01:00
Nicolò Boschi
f64817814a
feat: slim docker distro (#314)
* feat: slim docker distro

* feat: slim docker distro

* push
2026-02-06 15:00:24 +01:00
Nicolò Boschi
fa4cbf7ef2
fix(ci): resolve flaky test failures in api tests (#311)
* fix: resolve flaky test failures in api tests

Fixed 4 critical test failures that revealed real production issues:

1. test_sensory_dimension_preservation: Updated fact extraction prompt to
   clarify that sensory/emotional details ARE important to remember even if
   they seem small. The "6 months" filter was too aggressive and causing LLM
   to skip valid observations.

2. test_llm_provider_api_methods[openai-gpt-5]: Increased max_completion_tokens
   from 200 to 500 for tool calling tests. Non-nano models like gpt-5 were
   hitting token limits before completing tool calls.

3. test_reflect_chinese_content: Added prominent anti-hallucination warnings
   to reflect agent prompts. LLM was making up names (张飞, 张三, 赵信) instead
   of using the actual names from retrieved facts (张伟, 李明). Added explicit
   instructions at the very top of system prompts to NEVER fabricate names and
   to use EXACT names from retrieved data.

4. test_llm_provider_api_methods[groq-openai/gpt-oss-120b]: Skipped this model
   in tests as it consistently times out (>120s) due to slow Groq API responses.

All changes address real production code issues, not test flakiness.

* refactor: simplify anti-hallucination prompts and document groq issue

- Removed verbose anti-hallucination section with emojis/borders
- Moved core anti-hallucination rules to top of system prompts in clean format
- Kept essential rules: NEVER make up names/entities, ONLY use tool results
- Removed language override rule (directives can control language)
- Removed specific example (too prescriptive)

Groq gpt-oss-120b:
- Documented that API hangs on receive_response_body (Groq API bug)
- Skip is justified: headers received successfully but body never arrives
- This is gpt-oss-120b specific, not a general Groq provider issue

* fix: remove groq skip as requested

- Groq gpt-oss-120b may be slow but should not be skipped
- test_extensions.py::test_reflect_pre_hook_receives_all_parameters passes locally (50s)
- CI timeout appears to be from LLM producing malformed tool names (done<|channel|>commentary)
  which triggers retries and slows down the test

* fix: ensure unique timestamps for facts across different documents

The time offset logic was resetting to 0 for each new content_index, causing
all facts from different documents/conversations to have the same base timestamp
even when they should be distinguishable.

Changed to use absolute position (i) instead of relative position (i - content_fact_start)
so that:
- Content 0, Fact 0: offset = 0s
- Content 0, Fact 1: offset = 10s
- Content 1, Fact 0: offset = 20s (now unique!)
- Content 1, Fact 1: offset = 30s

This ensures facts from different batch-retained documents have unique timestamps
for proper temporal ordering in retrieval.

Fixes test_fact_ordering.py::test_multiple_documents_ordering

* fix: increase timeout for test_llm_provider_api_methods to 300s

The groq gpt-oss-120b model can be very slow (API hangs on response body),
taking >120s to complete. Increased timeout to 300s to prevent CI flakiness
while still catching real hangs.

This affects all provider/model combinations in the test, not just Groq,
but most complete in <30s so the increased timeout won't affect them.

* fix: skip structured output for groq gpt-oss-120b, reinforce date extraction

1. Groq gpt-oss-120b doesn't support response_format (structured output)
   - Returns 400 'json_validate_failed' error
   - Retries with exponential backoff caused 300s timeout
   - Skip test #3 (structured output) for this model

2. Reinforce date extraction prompt
   - Add CRITICAL instruction to extract absolute dates like 'March 15, 2024'
   - Helps prevent flaky test_extract_facts_with_absolute_dates failures
2026-02-06 13:56:59 +01:00
Nicolò Boschi
2109397028
ci: ensure python 3.14 compatibility (#310) 2026-02-06 10:50:45 +01:00
Nicolò Boschi
c4ef090a20
feat: support markdown in reflect and mental models (#307)
* feat: support markdown in reflect and mental models

* chore: regenerate clients and OpenAPI spec with markdown field descriptions
2026-02-06 10:49:13 +01:00
Dewaldt Huysamen
96f487213c
fix(openclaw): remove format:uri to fix ajv warning (#309)
Remove `format: "uri"` from hindsightApiUrl schema property.

OpenClaw's schema validator uses Ajv without ajv-formats loaded, causing:
  unknown format "uri" ignored in schema at path "#/properties/hindsightApiUrl"

The URI validation isn't critical since invalid URLs will fail at connection time.
This removes the warning without affecting functionality.
2026-02-06 10:45:43 +01:00
Nicolò Boschi
0d8d805832
ci: ensure backwards/forward compatibility of the API (#306) 2026-02-05 18:43:05 +01:00
Nicolò Boschi
1cd836229b 0.4.9 changelog 2026-02-05 17:11:52 +01:00