Commit graph

15 commits

Author SHA1 Message Date
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
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
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
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
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
382550690a
fix: custom pg schema is not reliable (#278)
* fix: custom pg schema is not reliable

* fix

* fix

* fix: WorkerPoller now always has tenant extension

Ensures WorkerPoller follows same pattern as MemoryEngine - always
creates a DefaultTenantExtension if none is provided, preventing
NoneType errors when calling list_tenants().

Fixes test failures in test_worker.py

* fix: DefaultTenantExtension honors explicit schema parameter

Allows WorkerPoller's schema parameter to be passed through to
DefaultTenantExtension via config dict, maintaining backward
compatibility for tests that use schema parameter without
providing a tenant extension.

Fixes test_poller_with_custom_schema test failure.
2026-02-02 15:33:50 +01:00
Chris Bartholomew
35127d5f8b
Add MentalModelRefreshContext and pre-operation validation for mental model create/refresh (#271)
Wire up validate_mental_model_refresh hook in the HTTP routes for both
create and refresh mental model endpoints, allowing extensions to reject
operations (e.g. insufficient credits) before queuing async LLM work.
2026-02-01 16:16:20 -05:00
Chris Bartholomew
9c3fda74e2
Add extension hooks for mental model operations (#260)
Add dataclasses and hook methods to OperationValidatorExtension for
tracking mental model operations:

- MentalModelGetContext/Result: context and result for GET operations
- MentalModelRefreshResult: result for refresh operations with token counts
- validate_mental_model_get: pre-operation validation hook
- on_mental_model_get_complete: post-GET completion hook
- on_mental_model_refresh_complete: post-refresh completion hook

Invoke hooks in http.py (GET endpoint) and memory_engine.py (refresh).
Add tests verifying hooks are called with correct parameters.
2026-01-31 09:30:53 -05:00
Nicolò Boschi
2b72e1fd68
feat: support different default pg schema (#222)
* feat: support different default pg schema

* feat: support different default pg schema
2026-01-28 18:14:44 +01:00
Nicolò Boschi
8e39cb7bc8
fix: improve mental model consolidation (#197)
* fix: improve mental model consolidation

* fix skill names

* fixes

* fix: add missing list_tenants to test mocks and update CLI for async refresh
2026-01-26 09:54:25 +01:00
Nicolò Boschi
9db64ecda3
feat: revisit mental models, directives and reflections (#179)
* chore: run benchmarks with reflect mode

* chore: run benchmarks with reflect mode

* fixes

* new mm

* bunch of fixes

* initial commit

* fixes

* fixes

* fixes

* fix: sometimes memories gets extracted in the wrong language
2026-01-22 17:13:16 +01:00
Nicolò Boschi
63a65d0723
feat: improve mental model refresh and add directives (#166)
* feat: improve mental model refresh and add directives

* feat: improve mental model refresh and add directives

* tags

* ui

* fix

* fix

* update

* update
2026-01-19 11:38:35 +01:00
Chris Bartholomew
6fe93140a7
Fix embedding dimension for tenant schemas (#135)
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.
2026-01-08 22:48:25 +01:00
Chris Bartholomew
ce45d301ce
Add operation validator extension support with proper HTTP error handling (#86)
* 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.
2026-01-01 20:19:52 -05:00
Nicolò Boschi
2a0c490c9e
feat: extensions (#54) 2025-12-22 11:05:23 +01:00