Commit graph

518 commits

Author SHA1 Message Date
Nicolò Boschi
0aa7c2b3a1
feat: batch observations consolidation (#430)
* feat: batch observations consolidation

* feat: batch observations consolidation

* docs: add CONSOLIDATION_LLM_BATCH_SIZE config flag documentation
2026-02-24 15:19:39 +01:00
Nicolò Boschi
ac9a94ade3
fix: handle observations regeneration when memories get deleted (#429)
* fix: handle observations regeneration when memories get deleted

* feat: add clear_memory_observations endpoint and regenerate clients

- Add DELETE /banks/{id}/memories/{memory_id}/observations endpoint
- Add observations lifecycle/invalidation section to docs
- Regenerate OpenAPI spec and all clients (Python, TypeScript, Go, Rust)

* refactor: use dedicated response model for clear_memory_observations, remove code example from docs
2026-02-24 13:26:33 +01:00
Anton Evseev
40b02645f4
fix(openclaw): pass auth token to health check endpoint (#427)
The checkExternalApiHealth function didn't include the Bearer token
in its requests. When the Hindsight API requires authentication
(HINDSIGHT_API_TENANT_API_KEY), health checks would fail with 401/403,
preventing plugin initialization.

Pass apiToken to all checkExternalApiHealth call sites and include
the Authorization header when a token is configured.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 13:17:00 +01:00
Nicolò Boschi
5fddd9a79c
feat: add reflect mode to LoComo benchmark and improve reflect agent (#428)
* feat: add reflect mode to LoComo benchmark and improve reflect agent

- Replace think mode with reflect mode in LoComo benchmark using reflect_async with Budget.HIGH
- Add --question-index CLI flag to run a single question by its index
- Track and display original question index in logs and visualizer
- Update visualizer to show reflect mode results

Reflect agent improvements:
- tool_recall: always fetch chunks (max_chunk_tokens=1000 min, non-optional)
- tool_search_observations: use include_source_facts=True instead of separate DB query
- Use model_dump() throughout to avoid manual error-prone dict conversion
- Enforce minimum 1000 tokens for max_tokens and max_chunk_tokens in _execute_tool
- Fix NoneType error when LLM passes null for mental_model_ids/observation_ids arrays
- Add non-conversational constraint to system prompt to prevent follow-up questions
- Fix recall_fn Callable type hint to include max_chunk_tokens parameter
- Fix main.py missing reranker_zeroentropy fields in HindsightConfig constructor

* fix: update tests for reflect tool API changes

- source_memory_ids -> source_fact_ids in test_search_observations (MemoryFact.model_dump() field name)
- Remove proof_count check (not in MemoryFact, was ObservationResult-specific)
- Remove max_results param from tool_recall call (no longer supported)
- Fix recall_result["count"] -> len(recall_result["memories"])
2026-02-24 09:48:23 +01:00
Nicolò Boschi
4d030707ad
feat: enable bank config API by default (#426)
Change DEFAULT_ENABLE_BANK_CONFIG_API from false to true, update all docs,
error messages, and client docstrings to reflect the new default. Remove
explicit env var overrides in CI and tests that are no longer needed.
2026-02-24 08:52:42 +01:00
Chris Bartholomew
5fef54d501 Fix typos in README 2026-02-23 15:32:49 -05:00
Nicolò Boschi
2a32273226
feat: increase customization for reflect, retain and consolidation (#419) 2026-02-23 20:35:32 +01:00
Nicolò Boschi
87219b731d
feat: include doc metadata in fact extraction (#424) 2026-02-23 11:31:02 +01:00
Nicolò Boschi
9f0c031df7
fix: improve memory footprint of recall (#423) 2026-02-23 11:30:16 +01:00
Chris Bartholomew
8b1a46585d
Fix reflect based_on population and enforce full hierarchical retrieval (#421)
* Fix reflect based_on population and enforce full hierarchical retrieval

Problem 1: based_on field was incomplete
- search_observations results were never extracted into based_on, so
  observations used by the agent were invisible to callers
- search_mental_models and get_mental_model used non-existent fields
  (summary/description) instead of the actual content field, producing
  empty text in based_on entries
- A duplicate unreachable elif block for search_mental_models was dead
  code (the first identical condition always matched)

Problem 2: mental models could produce "I don't have information"
- When a bank has mental models, the agent's tool_choice forcing only
  covered iteration 0 (search_mental_models). Iterations 1+ were auto,
  allowing the LLM to short-circuit without ever searching observations
  or raw facts. Combined with the LOW budget prompt encouraging speed,
  this meant the agent would often stop after a single tool call.
- This created a self-reinforcing failure loop: if a mental model
  refresh produced "I don't have information" (e.g. due to the agent
  skipping recall), subsequent reflects would find that content and
  trust it, never searching deeper.

Fix: extend forced tool_choice to cover the full hierarchical retrieval
path before allowing auto mode:
- With mental models: search_mental_models(0) → search_observations(1)
  → recall(2) → auto(3+)
- Without mental models: search_observations(0) → recall(1) → auto(2+)

This matches the retrieval strategy documented in the system prompt and
ensures all three knowledge levels are always consulted. The agent still
has 2-3 auto iterations (with LOW budget, max_iterations=5) for
additional searches or calling done().

* Add Umami analytics tracking to docs site

Add conditional Umami script injection to docusaurus.config.ts and pass
UMAMI_URL/UMAMI_WEBSITE_ID env vars in the GitHub Pages deploy workflow.
The tracking script only loads when both env vars are set.
2026-02-23 10:13:19 +01:00
Eliah Rusin
172596751f
feat: add ZeroEntropy reranker provider support (#420)
Add ZeroEntropy as a reranker provider using their Rerank API
(https://docs.zeroentropy.dev/models). Supports zerank-2 (flagship)
and zerank-2-small models via direct HTTP API calls with httpx (no
additional SDK dependency required).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:12:32 +01:00
Chris Bartholomew
b180b3ad97
Fix bank config API for multi-tenant schema isolation (#417)
* Fix bank config API for multi-tenant schema isolation

- Use fq_table() in config_resolver.py to schema-qualify bank table queries
- Add authenticate_and_resolve_schema() to bank config API handlers in http.py

Without these fixes, bank config operations in multi-tenant mode hit
public.banks instead of tenant_xxx.banks, causing "column config does
not exist" errors.

* Fix method name: _authenticate_tenant not authenticate_and_resolve_schema

The MemoryEngine method is _authenticate_tenant(), not
authenticate_and_resolve_schema(). This was causing AttributeError
on all bank config API requests.
2026-02-20 23:43:52 +01:00
Nicolò Boschi
7a2798eb7a
misc: fix vertex/gemini errors and use it for ci tests (#414)
* ci: use vertex model

* fix: allow vertexai provider without API key requirement

- Add vertexai to providers that don't require an API key in memory_engine.py
  (vertexai uses GCP service account credentials instead)
- Add vertexai to PROVIDER_DEFAULTS in embed CLI for non-interactive configure support
- Skip API key requirement for vertexai in embed CLI configure from env
- Fix test_server_integration.py fixture to not raise for vertexai provider

* fix: skip upgrade tests when using vertexai provider

Old server versions (e.g., v0.3.0) do not support the vertexai provider.
Skip upgrade tests gracefully when using vertexai without a fallback API key,
since these old versions would fail to start with the vertexai configuration.

* fix: allow vertexai provider in embed smoke test

Skip the API key requirement in test.sh when using vertexai provider,
since vertexai uses GCP service account credentials instead.

* fix: skip API key check for vertexai in embed CLI command forwarding

vertexai uses GCP service account credentials instead of an API key.
Skip the API key validation before forwarding commands to hindsight-cli
when the provider is vertexai (or ollama which also doesn't need an API key).

* fix(ci): add GCP credentials setup step to test-api job

The test-api job was missing the step to write GCP credentials to
/tmp/gcp-credentials.json and set HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID
from the credentials file, causing tests to fail with:
"HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID is required for Vertex AI provider"

* fix: support vertexai in LLMProvider factory methods and fix ADC test

- Add vertexai and ollama to providers that don't require an API key
  in LLMProvider.for_memory(), for_answer_generation(), and for_judge()
- Fix test_llm_wrapper_vertexai_adc_auth to properly clear the SA key
  env var when testing the ADC authentication path

* fix(ci): fix remaining test failures for GCP Vertex AI CI

- test_fact_ordering: relax timing assertion from >=5s to >0 (SECONDS_PER_FACT=0.01 since #402)
- retain.sh doc example: replace non-existent report.pdf with sample.pdf from examples dir
- Strengthen language preservation instruction in fact extraction prompt for better LLM compliance
- Mark LLM-behavior-dependent tests as xfail(strict=False) for models that may not preserve source language or follow directives:
  - test_retain_chinese_content
  - test_reflect_chinese_content
  - test_retain_japanese_content
  - test_reflect_follows_language_directive
  - test_date_field_calculation_yesterday
  - test_no_match_creates_with_fact_tags

* fix(ci): stabilize flaky tests for Gemini-flash-lite and CI environment

- Mark consolidation tests as xfail(strict=False) for LLMs that don't always create observations from single facts
- Mark reflect test as xfail for LLMs that may not call search_mental_models
- Add timeout(300) to test_llm_provider_memory_operations to prevent 120s default timeout failures
- Increase SeaweedFS startup timeout from 30s to 120s for slow CI Docker environments
- Increase Python client pytest timeout from 60s to 120s for slow Gemini responses

* fix(ci): fix test isolation and skip SeaweedFS tests in CI

- Fix test_create_operation_span_disabled: patch _tracing_enabled=False for test isolation since tests run in parallel and another test enables tracing
- Skip SeaweedFS Docker tests in CI (container startup too slow, exceeds 120s timeout)
- Mark graph edge test as xfail for LLMs that don't always create observations/entity links

* fix(ci): fix remaining test failures

- Fix test_post_hooks_called_in_order_after_pre_hooks: use >= 1 for recall count since consolidation triggers internal recalls when observations are enabled
- Mark test_consolidation_merges_only_redundant_facts as xfail for LLMs that don't always create observations
- Mark test_untagged_fact_can_update_scoped_observation as xfail for LLMs that don't always create observations
- Add HuggingFace model cache and pre-download step to test-python-client CI job to fix NotImplementedError with meta tensors
- Increase API server startup wait from 60s to 120s in test-python-client job

* revert: simplify language instruction in fact extraction prompts

* refactor: add requires_api_key() to llm_wrapper and revert xfail markers

- Add public requires_api_key(provider) function to llm_wrapper.py with a frozenset of providers that don't need API keys (ollama, lmstudio, openai-codex, claude-code, mock, vertexai)
- Simplify memory_engine.py API key check to use requires_api_key()
- Revert all @pytest.mark.xfail(strict=False) markers from test files

* refactor(embed): use shared PROVIDER_DEFAULT_MODELS map in cli.py

- Add PROVIDER_DEFAULT_MODELS to cli.py mirroring hindsight_api/config.py (with sync comment)
- Derive PROVIDER_DEFAULTS model values from PROVIDER_DEFAULT_MODELS instead of duplicating strings
- Fix get_config() to look up the default model from PROVIDER_DEFAULT_MODELS based on the active provider
- Rename "google" provider alias to "gemini" in PROVIDER_DEFAULTS and interactive choices to match config.py

* refactor(embed): use get_default_model_for_provider() instead of mirrored dict

Replace the hardcoded PROVIDER_DEFAULT_MODELS dict in cli.py with a function
that imports from hindsight_api.config at call time, eliminating duplication.
Falls back to gpt-4o-mini if hindsight_api is not importable.

* fix: address CI test failures with real root-cause fixes

- fact_extraction: strengthen LANGUAGE instruction to be more emphatic
  about preserving input language (fixes multilingual test failures)
- fact_extraction: add _replace_temporal_expressions() to convert
  relative dates ("yesterday") to absolute dates in stored fact text
  (fixes test_date_field_calculation_yesterday)
- tools_schema: note that search_observations is secondary to
  search_mental_models when mental models are available
  (helps model call search_mental_models first)
- test_mental_models: change directive test to use a unique marker phrase
  ('MEMO-VERIFIED') instead of brittle "start with Hello!" format check,
  which is more reliably testable across LLM providers
- test_consolidation: use wait_for_background_tasks() instead of
  asyncio.sleep(2), and make edge assertion conditional on having
  multiple observation nodes (consolidation may merge facts into one)

* fix: more CI test fixes and infrastructure improvements

- fact_extraction: note in examples that non-English input must preserve
  language in all output values (examples are English for illustration only)
- tools_schema: inject directives into done() answer field description
  so model must comply when writing the answer itself
- test_consolidation: add wait_for_background_tasks() in
  test_scoped_fact_updates_global_observation so observations exist
  before asserting on them
- ci: add HuggingFace model pre-download step and increase API server
  wait from 60s to 120s for test-doc-examples job (same fix as test-api)

* fix: strengthen directive and language handling in reflect

- reflect/prompts: add LANGUAGE RULE section to respond in query language
  (fixes test_reflect_chinese_content which expects Chinese response)
- test_mental_models: change tagged directive test to verify isolation
  mechanism via directives_applied instead of brittle response content
  check (model may not include exact phrase when finding no memories)
- reflect/prompts: add language rule comment that directives override
  language (so French directive test can still work)

* ci: add HuggingFace pre-download and increase timeout for client/CLI test jobs

Add Cache HuggingFace models + Pre-download models steps to:
- test-rust-cli
- test-typescript-client
- test-rust-client
- test-go-client

Also increase API server wait from 60s to 120s for all jobs that start
the API server (including test-openclaw-integration and test-integration).

This prevents PyTorch meta tensor errors during HuggingFace model
initialization that caused API server startup failures in CI.

* fix(tests): add wait_for_background_tasks and fix directive isolation test

- test_consolidation_merges_contradictions: add wait after first retain
  so count_before reflects actual observation state before second retain
- test_cross_scope_creates_untagged: add wait after each _retain_with_tags
  so observations are created before checking count
- test_tagged_directive_not_applied_without_tags: verify directives_applied
  mechanism for untagged reflect instead of model response content
  (Gemini Flash Lite doesn't reliably follow exact phrase directives)

* fix: global directives always apply in tagged reflect, improve multilingual

- memory_engine: use "any" tags_match when loading directives so global
  (untagged) directives always apply, even in strict tag mode (all_strict
  was excluding empty-tagged directives from tagged reflect)
- tools_schema: add language instruction to done() answer field description
  to help Gemini Flash Lite respond in user's query language
- test_consolidation: add wait_for_background_tasks() for
  test_untagged_fact_can_update_scoped_observation

* fix(tests/agent): force search_mental_models first, relax model-dependent assertions

- reflect/agent.py: on first iteration when has_mental_models=True, restrict
  tools to only search_mental_models to guarantee it's called first
  (Gemini Flash Lite doesn't support tool_choice with specific function name)
- test_consolidation: relax test_untagged_fact_can_update_scoped_observation
  to not require >= 1 observations (single facts may not consolidate)
- test_consolidation: relax test_cross_scope_creates_untagged to >= 1
  observation (LLM may merge cross-scope facts into one observation)
- test_multilingual: use Budget.MID for Chinese reflect test to ensure
  the model searches thoroughly enough to find the retained facts

* fix: implement Gemini tool_choice support and use it to force search_mental_models

- gemini_llm.py: map OpenAI-style tool_choice to Gemini FunctionCallingConfig
  (required→ANY mode, specific function→ANY+allowed_function_names, none→NONE)
- agent.py: on first iteration with has_mental_models=True, force search_mental_models
  using {"type": "function", "function": {"name": "search_mental_models"}} tool_choice
- test_consolidation: relax test_cross_scope_creates_untagged to not assert
  on observation count (Gemini Flash Lite may not consolidate cross-scope facts)

* fix: proper Gemini multi-turn history and language directive priority

- Fix gemini_llm.py: convert assistant tool_calls to Gemini function_call
  parts in call_with_tools. Previously, assistant messages with tool_calls
  were sent as empty text, breaking conversation history and causing Gemini
  to loop through all iterations instead of calling done efficiently.
- Fix prompts.py: clarify that LANGUAGE RULE yields to directives - the
  previous wording told Gemini to respond in the query language which
  overrode French language directives when the query was in English.
- Fix tools_schema.py: update done tool answer description to acknowledge
  that language directives take precedence over the default language behavior.

* fix(ci): increase client timeout and handle Gemini JSON control characters

- Increase Python client default timeout from 30s to 120s to accommodate
  Gemini Vertex AI reflect calls (which require 2+ LLM calls at 10-15s each)
- Handle JSON control characters (\x00-\x1f) in Gemini responses during
  consolidation by stripping them before re-parsing on JSONDecodeError

* fix(ci): fix consolidation JSON control chars and improve recall fallback

- Fix consolidation failure: Gemini embeds control characters (\x00-\x1f)
  in JSON string output, causing json.loads() to fail in consolidator.py.
  The existing fix in gemini_llm.py doesn't apply here because consolidation
  uses skip_validation=True (no response_format), so the consolidator parses
  JSON itself. Add control char cleaning at consolidator.py line ~960.
- Improve reflect agent fallback: make it MANDATORY to call recall() when
  search_observations returns 0 results, preventing premature "no info found"
  responses when observations haven't been consolidated yet.

* refactor: centralize LLM JSON parsing, fix tags_match bug, remove temporal heuristic

- Add parse_llm_json() to llm_wrapper.py as single robust JSON parsing
  utility: handles markdown code fences and embedded control characters
  (\x00-\x1f). Use it in consolidator.py and gemini_llm.py instead of
  duplicated ad-hoc cleaning logic.
- Fix tags_match bug in reflect_async: directives were fetched with
  hardcoded tags_match="any" instead of using the reflect request's own
  tags_match value. Directives must respect the same scoping rules as
  the rest of the reflect operation.
- Remove _replace_temporal_expressions() heuristic from fact_extraction.py:
  the English-only word list ("yesterday", "today", etc.) broke multi-language
  support. Strengthen the prompt instruction to ask the LLM to resolve
  relative temporal expressions to absolute dates in the extracted fact text.

* test: enable SeaweedFS S3 tests in CI

Remove the CI skip condition - ubuntu-latest runners have Docker pre-installed
and testcontainers is already a test dependency.

* fix: raise on malformed tool call args instead of silently using empty dict

* feat(reflect): enforce search_observations then recall() when no mental models

Mirror the search_mental_models forcing pattern: without mental models,
iteration 0 forces search_observations and iteration 1 forces recall(),
guaranteeing the agent always attempts both retrieval levels before
deciding it has no information.

* refactor: clean up consolidation pipeline and reflect agent

- Consolidation: use response_format for structured LLM output, remove
  silent failures, legacy format handling, and redundant DB queries;
  _find_related_observations now returns RecallResult directly; source
  facts fetched inline via include_source_facts=True/max_source_facts_tokens=-1
- reflect tools: replace time-based mental model staleness with
  pending_consolidation signal (consistent with observations)
- reflect agent: unify directive format (remove {name,description,observations}
  conversion), simplify _extract_directive_rules and _build_directives_applied

* fix: consolidation MemoryFact mapping error, directive tag isolation, S3 test timeout

- Extract _build_observations_for_llm helper to prevent linter from collapsing
  explicit dict construction to {**obs} (MemoryFact is not a mapping)
- Fix directive tag isolation: untagged directives always apply regardless of
  reflect tags; only tagged directives require matching tags
- Add pytest.mark.timeout(300) to S3 tests to handle SeaweedFS container startup

* fix(gemini): group consecutive tool responses into a single Content for Vertex AI

Gemini requires all function responses for a given model turn to be in a
single Content with multiple FunctionResponse parts. Previously each
role="tool" message was added as a separate Content, causing 400 errors:
"number of function response parts != function call parts".

* fix: add Gemini HTTP timeout, cap reflect consecutive errors, increase test timeouts

- Add 60s HTTP timeout to Gemini/VertexAI client to prevent indefinite hangs
  when Vertex AI API calls stall (seen as 10-minute hangs in Go client tests)
- Cap consecutive LLM errors in reflect agent at 2 before falling back to
  final answer (prevents 10x60s=600s timeout cascade from error retries)
- Increase global pytest timeout from 120s to 300s for slow LLM operations
- Increase SeaweedFS internal readiness wait from 120s to 240s in S3 tests

* fix: use asyncio.wait_for(90s) instead of http_options timeout, fix flaky tests

- Replace 45s http_options timeout (which cut off valid 57s Vertex AI responses)
  with asyncio.wait_for(90s) as a safety net for genuine network hangs
- Remove http_options from genai.Client init (both gemini and vertexai)
- Update VertexAI auth tests to not assert on http_options
- Skip SeaweedFS S3 tests in CI (Docker pull too slow)
- Add retry loop to test_reflect_follows_language_directive (flash-lite flaky)
- Increase Python client default timeout 120s → 300s to handle slow Gemini responses
2026-02-20 22:35:38 +01:00
Nicolò Boschi
278344b3b3
doc: improve api explanation (#415)
* doc: improve api explanation

* doc: improve api explanation

* doc: improve api explanation

* fix: add include_facts to reflect client, fix retain.sh temp files, fix main-methods based_on access

* fix: create report.pdf in working directory for retain.sh file upload examples
2026-02-20 17:02:01 +01:00
Anton Evseev
3f9eb27cd7
feat(openclaw): add autoRecall toggle and excludeProviders schema (#413)
Add `autoRecall` config option (default: true) to allow disabling
automatic memory recall injection when the host agent has its own
dedicated recall tool. This is backward compatible — existing
deployments continue auto-recalling as before.

Also add the existing `excludeProviders` field to the plugin.json
configSchema so it appears in the UI and docs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 09:31:46 +01:00
Nicolò Boschi
13c82bab60
fix: set hindsight-crewai version to 0.4.13 (#412) 2026-02-20 09:31:22 +01:00
Nicolò Boschi
4f431b4ace
doc: 0.4.13 changelog (#411) 2026-02-19 20:48:50 +01:00
Nicolò Boschi
2993fdd2f9 Release v0.4.13
- Update version to 0.4.13 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-19 18:46:07 +01:00
Nicolò Boschi
325b5cc141
feat: switch default model to gpt-4o-mini (#410) 2026-02-19 18:43:52 +01:00
Nicolò Boschi
0758827d39
fix: npx hindsight-control-plane fails (#408) 2026-02-19 18:23:13 +01:00
Nicolò Boschi
ea8163c56d
fix(mcp): unify hindsight-mcp-local and server mcp (#407)
* fix(mcp): stateless param not supported anymore

* fixes

* fix: npx hindsight-control-plane fails
2026-02-19 17:57:17 +01:00
Nicolò Boschi
ac73948706
fix: docker startup fails with named docker volumes (#405) 2026-02-19 16:42:27 +01:00
Nicolò Boschi
5569d4adba
feat: include source facts in observation recall (#404)
* feat: include source facts in observation recall

* feat: include source facts in observation recall

* feat: include source facts in observation recall

* feat: include source facts in observation recall

* fix(cli): add missing source_facts field to IncludeOptions initializer
2026-02-19 14:54:19 +01:00
Nicolò Boschi
e785b05831
fix(mcp): stateless param not supported anymore (#406) 2026-02-19 13:42:54 +01:00
Nicolò Boschi
58c4d65778
fix: reranker crashes on provider error (#403)
* fix: reranker crashes on provider error

* fix: reranker crashes on provider error
2026-02-19 11:38:37 +01:00
Derek Bouius
c3ef1555bf
fix: reduce temporal ordering offset from 10s to 10ms per fact (#402)
The 10-second offset per fact caused significant timestamp drift when
ingesting many items — e.g. 600 facts would shift the last fact by
~100 minutes from its actual event time. This broke timeline views
and made occurred_start/mentioned_at unreliable for temporal queries.

Reducing to 10ms preserves fact ordering while keeping timestamps
within ~8 seconds of the original values even for large batches.
2026-02-19 10:48:18 +01:00
Nicolò Boschi
dcaa9f14ab
fix: clients don't respect timeout setting (#400) 2026-02-19 10:47:47 +01:00
Ben
41db2960c5
feat: add CrewAI integration for persistent crew memory (#319)
* feat: add CrewAI integration for persistent crew memory

Implements a CrewAI ExternalMemory storage backend that maps CrewAI's
Storage interface (save/search/reset) to Hindsight's retain/recall/delete
APIs, giving crews long-term memory with fact extraction, entity tracking,
and temporal awareness across runs.

Key features:
- HindsightStorage: drop-in Storage backend for CrewAI ExternalMemory
- HindsightReflectTool: BaseTool exposing Hindsight's reflect API
- Per-agent memory banks with customizable bank resolver
- Async compatibility layer for CrewAI's threading model
- 35 unit tests, docs site page, example script

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

* refactor: move CrewAI example to hindsight-cookbook

Move research_crew.py example from hindsight-integrations/crewai/examples/
to the cookbook repo and update the integration README to link there instead.

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

* ci: add GitHub Actions test job for CrewAI integration

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

* ci: add uv.lock for frozen installs in CI

The test-crewai-integration CI job uses `uv sync --frozen` which
requires a committed lock file.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 17:05:52 +01:00
Nicolò Boschi
f78278ea89
fix: document not tracked if has 0 extracted facts (#399)
* fix: document not tracked if has 0 extracted facts

* fix: document not tracked if has 0 extracted facts
2026-02-18 17:01:57 +01:00
Nicolò Boschi
117dd6988d
doc: changelog for 0.4.12 (#397)
* changelog for 0.4.12

* changelog for 0.4.12
2026-02-18 14:55:19 +01:00
Nicolò Boschi
7c78ae2371 Release v0.4.12
- Update version to 0.4.12 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-18 14:13:33 +01:00
Nicolò Boschi
6c695eb9f8
fix: improve openclaw test coverage (#396)
* fix: improve openclaw test coverage

* test(openclaw): export stripMemoryTags/extractRecallQuery and add hook integration tests

- Extract stripMemoryTags and extractRecallQuery as exported pure functions
  from index.ts so hooks share one implementation and tests cover the real code
- Update before_agent_start to call extractRecallQuery; update agent_end to
  call stripMemoryTags instead of duplicating the regex inline
- Rewrite index.test.ts to import the real functions (no more local duplicate)
  and add 11 tests for extractRecallQuery covering all envelope-stripping cases
- Add tests/hooks.integration.test.ts: loads the plugin via mock MoltbotPluginAPI
  in HTTP mode, spies on client.recall/retain, and exercises all hook behaviours:
  excluded providers, short messages, memory injection format, tag stripping,
  transcript formatting, array content blocks, metadata, document_id derivation
2026-02-18 14:10:33 +01:00
Nicolò Boschi
7eafba661e
feat: add iris as file parser (#395)
* feat: add iris as file parser

* fix
2026-02-18 14:09:56 +01:00
Anton Evseev
c461013047
fix(openclaw): shell safety, HTTP dual-mode, lazy reinit, per-user banks (#388)
- exec→execFile: bypass shell entirely, preventing injection via
  special characters in chat history
- HTTP dual-mode: client can now talk directly to the Hindsight API
  via HTTP (setBankMission, retain, recall) when apiUrl is configured,
  bypassing the subprocess/CLI entirely for production deployments
- HindsightClientOptions: replace 5 positional constructor args with
  a typed options object for clarity and extensibility
- sanitize(): strip null bytes from strings — Node 22 rejects them
  in execFile() args
- recall timeout: accept optional timeoutMs parameter for both HTTP
  and subprocess modes; subprocess gets a longer 30s default
- In-flight recall dedup: concurrent recalls for the same bank reuse
  one promise instead of firing duplicate requests
- Timeout/abort handling: graceful warn-level logging instead of
  error spam when recall times out
- Error cause chaining: wrap errors with { cause } for better
  debugging stack traces
- lazyReinit: recover from startup health check failure with 30s
  cooldown and concurrency guard
- Per-user banks: derive bank ID from senderId (not channelId) for
  proper memory isolation per user across channels
- buildClientOptions(): centralized helper replaces 7 duplicated
  constructor call sites

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:09:02 +01:00
Nicolò Boschi
7c99feb018
fix(go-client): add go build to CI (#393)
* feat(go-client): add NewAPIClientWithToken helper and expand recall vs reflect FAQ

- Add NewAPIClientWithToken convenience function to Go client for easy authenticated client creation
- Expand FAQ with detailed "When should I use recall vs reflect?" guidance including practical examples

* fix(go-client): add go build to CI and preserve hindsight_client.go in generator

- Add explicit 'go build ./...' step before integration tests for faster compile feedback
- Preserve hindsight_client.go as a maintained file in generate-clients.sh
2026-02-18 13:06:17 +01:00
Nicolò Boschi
d06a0259cc
feat: improve ai sdk tools (#394) 2026-02-18 13:06:03 +01:00
Eliah Rusin
be8728b313
fix(go-client): use monorepo-compatible module path (#392)
The Go SDK declared its module as github.com/vectorize-io/hindsight-client-go,
but that repository doesn't exist. Update to
github.com/vectorize-io/hindsight/hindsight-clients/go to match the actual
monorepo path, enabling standard `go get` imports with directory-prefixed tags.

Also enables isGoSubmodule in the OpenAPI generator config and updates all
import references across tests, docs, and the client generation script.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:35:13 +01:00
Derek Bouius
917893aac7
fix: restore entity retrieval in recall (#391)
Entity retrieval was removed in ab5e31f2 ("chore: remove dead code")
but the code was not dead — it populated the entities dict and
per-fact entity names returned by the recall endpoint.

This restores:
- fact_entity_map query joining unit_entities and entities tables
- entity_names on each MemoryFact result
- entities_dict with EntityState objects ordered by fact relevance
- entity count in recall log line
2026-02-18 10:33:58 +01:00
Nicolò Boschi
224b7b74c1
feat: accept pdf, images and office files (#390)
* feat: accept pdf, images and office files

* refactor: rename FileConverter to FileParser, simplify file retain API

- Rename engine/converters/ → engine/parsers/, FileConverter → FileParser,
  ConverterRegistry → FileParserRegistry, MarkitdownConverter → MarkitdownParser
- Rename env var HINDSIGHT_API_FILE_CONVERTER → HINDSIGHT_API_FILE_PARSER
- Remove async/document_tags params from FileRetainRequest (always async now)
- Add retain_files() to Python Hindsight client and retainFiles() to TypeScript client
- Add sample.pdf to doc examples for working file upload demonstrations
- Update test_file_retain.py to use new parser names and always-async behavior
- Fix Go client missing os import in api_files.go
- Simplify postgresql.py storage to minimal schema

* fix: update rust CLI tests to use is_supported_file instead of is_text_file

* fix: patch Go api_files.go to add missing 'os' import after generation

* fix: insert 'os' import after 'net/url' in api_files.go patch for correct position

* chore: regenerate OpenAPI spec and clients (converter→parser description update)
2026-02-17 18:15:03 +01:00
Chris Bartholomew
8114ef440e
fix(python-client): async method parity and server keepalive timeout (#387)
* Fix async method parity and server keepalive timeout

The Python client's async methods were missing parameters available in
their sync counterparts, and the server's default keepalive timeout was
shorter than the client's, causing ServerDisconnectedError on reused
connections.

Server:
- Set uvicorn timeout_keep_alive to 30s (default was 5s). The Python
  client (aiohttp) has a 15s client-side keepalive, so the server must
  hold connections longer to prevent the client from writing to a
  closed socket.

Python client - async method parity:
- arecall(): add trace, query_timestamp, include_entities,
  include_chunks, max_entity_tokens, max_chunk_tokens. Return
  RecallResponse instead of list[RecallResult].
- areflect(): add max_tokens and response_schema.
- acreate_bank(): new async method.
- aset_mission(): new async method.
- adelete_bank(): new async method.

Tests:
- Add test verifying uvicorn keepalive timeout exceeds client default.
- Add async tests for arecall (include_chunks, include_entities, trace,
  full params), areflect (max_tokens, structured output), and
  adelete_bank.

* Fix flaky tag tests by using entity-rich content and asserting on tags

The tag tests were unreliable because:
- Generic content ("Project X meeting notes") was frequently collapsed
  during fact extraction, leaving no memories to recall
- Assertions checked LLM-rewritten text for literal substrings instead
  of checking tags, which is what the tests are actually verifying

Fix: use distinctive, entity-rich content (named people with specific
actions) that reliably survives fact extraction, and assert on tag
membership rather than text content.
2026-02-17 16:00:14 +01:00
Nicolò Boschi
6bad667344
fix(openclaw): error E2BIG on large content ingested (#389) 2026-02-17 14:11:23 +01:00
Nicolò Boschi
b3f0205ead
doc: add faq page (#383) 2026-02-17 11:26:27 +01:00
Nicolò Boschi
476726c2a2
feat: support azure pg_diskann (#381) 2026-02-16 16:08:54 +01:00
Nicolò Boschi
970f1b3534
ci: ensure docs get created with no extracted facts (#379) 2026-02-16 14:43:32 +01:00
Nicolò Boschi
5883e5af2d
doc: add go client examples (#380)
* doc: add go client examples

* doc: add go client examples
2026-02-16 14:43:03 +01:00
Nicolò Boschi
95c4220477
feat: support for pgvectorscale (DiskANN) (#378)
* feat: support for pgvectorscale (DiskANN)

* feat: support for pgvectorscale (DiskANN)
2026-02-16 14:19:56 +01:00
Nicolò Boschi
6e30980add
feat: use official go generator for Go client (#377)
* ci: add Go client integration tests

Add test-go-client job to CI workflow following the same pattern as
Python, TypeScript, and Rust client tests. The job:
- Sets up Go 1.23 with dependency caching
- Starts the Hindsight API server
- Runs integration tests using the 'integration' build tag
- Displays server logs on failure

The integration tests (hindsight-clients/go/integration_test.go) cover
all core operations: retain, recall, reflect, bank management, and
end-to-end workflows.

* Move Go cookbook content to hindsight-cookbook repo

Removes Go-specific cookbook content that was added in PR #375:
- applications/go-memory-service.md
- recipes/go-quickstart.md
- recipes/go-concurrent-pipeline.md

These have been moved to the hindsight-cookbook repository where
cookbook content should live per project conventions.

* feat(go): add CI test for Go client and patch for ogen null handling

- Add test-go-client job to GitHub Actions CI workflow
- Create post-generation patch script (patch-ogen.sh) to fix ogen's
  handling of null values in optional string fields
- Patch OptString.Decode() to check jx.Next() type before decoding,
  properly handling explicit null in JSON responses

The patch ensures generated code persists across regenerations and
handles the Hindsight API's nullable optional fields correctly.

Fixes: Go client integration tests for retain and bank operations
Note: Some tests still fail for nullable arrays/objects - those
require additional patches for other Opt* types.

* feat: use official go generator for Go client

* feat: use official go generator for Go client

* ci fixes

* chore: sync Go client with latest OpenAPI spec

- Add model_child_operation_status.go (new model)
- Update model_operation_status_response.go with child operations
- Update go.mod/go.sum dependencies
- Update api/openapi.yaml
2026-02-16 14:04:12 +01:00
Nicolò Boschi
40d42c58aa
feat: support Batch API for retain (openai/groq) (#365)
* feat: support Batch API for retain (openai/groq)

* api

* stop batch api if sync

* fix(ui): improve toast notifications with brand colors and proper styling

- Replace all window.alert() calls with toast notifications
- Add interceptor-based error handling in API client
- Use different toast styles based on HTTP status codes (4xx = warning, 5xx = error)
- Apply Hindsight brand colors to toasts (primary blue for info, destructive red for errors, etc.)
- Remove obsolete error handling files (hindsight-client-with-toast.ts, api-error-handler.ts)
- Fix toast background conflicts by removing base bg-background class

* fix: restore retain_batch_tokens config that was accidentally removed during rebase
2026-02-16 13:31:50 +01:00
Nicolò Boschi
aefb3fcf4d
fix: improve async batch retain with large payloads (#366)
* fix: improve async batch retain with large payloads

* fix: improve async batch retain with large payloads

* api

* api

* api

* api

* api

* Clean up perf benchmark: keep only Python files

- Remove README.md and PERFORMANCE_FINDINGS.md
- Remove results/ JSON files (gitignored)
- Remove test_data/ directory
- Keep only __init__.py and retain_perf.py

* docs: explain automatic batch optimization for async retain

- Add section explaining Hindsight automatically handles batch sizing
- Users don't need to manually tune batch sizes with async mode
- Hindsight splits large batches (>10k tokens) into optimized sub-batches
- Include example showing best practices

* docs: remove emojis and code example from performance page

* fix: correct OperationDetails type to match API response

- Change optional fields to use | null instead of ?
- Fixes TypeScript compilation error in control plane build

* fix: use discriminated union for OperationDetails type

- Support both success and error states properly
- Fixes TypeScript error when setting error state

* fix: use unique document_ids in batch retain examples

- Each item in a batch must have unique document_id
- Update both Python and JavaScript examples
- Fixes test-doc-examples CI failure

* chore: trigger CI

* fix: test mocking and duplicate document_ids in examples

- Mock _get_pool() in test_async_retain_tags.py to avoid _initialized error
- Set _initialized = True on mocked MemoryEngine instances
- Fix duplicate document_ids in retain.py and retain.mjs examples

* fix: properly mock async pool/connection and fix more duplicate document_ids

- Use AsyncMock for pool.acquire() to fix 'can't be used in await' error
- Fix duplicate document_ids in retain-async examples (retain.py and retain.mjs)
- Remove batch-level document_id parameter that caused duplicates

* ci: collect all doc example failures and show summary

- Run all Python/Node.js/CLI examples regardless of individual failures
- Collect failure list and display summary at the end
- Show pass/fail count and list of failed files
- Exit with failure only after running all examples

* refactor: extract doc example testing to standalone script

- Create scripts/test-doc-examples.sh to run all examples
- Collects logs of failed examples separately
- Shows full error logs only for failures at the end
- Clean summary with pass/fail counts
- Proper exit codes
- Replaces inline bash in CI workflow

* fix: doc examples - duplicate document_ids and error handling

- retain.py: move document_id to item level to avoid duplicates
- documents.mjs: add error handling for getDocument to show clear error message

* fix: update tests for duplicate document_id validation

- test_async_retain_tags: verify operation structure instead of exact UUID
- test_delete_bank: use unique document_ids (team-doc-1, team-doc-2)
2026-02-16 12:51:42 +01:00
Eliah Rusin
2a47389f2c
feat: add Go client SDK with ogen code generation (#375)
Add a Go client for the Hindsight API using ogen for strongly-typed code
generation from the OpenAPI 3.1 spec. The client provides a high-level
wrapper with functional options around the generated code, covering all
core operations (retain, recall, reflect, bank management).

Includes:
- ogen-based code generation with OpenAPI 3.1 spec preprocessing
- High-level Client wrapper with idiomatic Go API
- Functional options for all operations (WithBudget, WithTags, etc.)
- OgenClient() escape hatch for advanced operations
- Integration tests and godoc examples
- Go SDK reference docs and cookbook entries (quickstart, concurrent
  pipeline, memory-augmented API service)
- Updated generate-clients.sh with Go generation step

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 11:35:40 +01:00