diff --git a/hindsight-dev/hindsight_dev/generate_changelog.py b/hindsight-dev/hindsight_dev/generate_changelog.py index b626552bb4..2587f9fe50 100644 --- a/hindsight-dev/hindsight_dev/generate_changelog.py +++ b/hindsight-dev/hindsight_dev/generate_changelog.py @@ -12,9 +12,11 @@ import re import subprocess import sys +from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass from pathlib import Path +from hindsight_api.engine.token_encoding import count_tokens from openai import OpenAI from pydantic import BaseModel from rich.console import Console @@ -624,16 +626,60 @@ def render_migrations_section(migrations: list[Migration]) -> list[str]: return lines -def analyze_commits_with_llm( - client: OpenAI, - model: str, +# A release's commits are summarized in token-sized batches rather than in one call. +# One call over a whole release makes the model compress: 0.10.0's 220 commits came +# back as 37 entries against 65 for the 145-commit release before it, with whole +# user-facing fixes unlisted. A batch is small enough that every commit in it can be +# considered on its own, and batches are independent, so they run concurrently. +# +# The budget is deliberately far below the model's input limit. Commit subjects are +# short: a 245-commit release is ~8500 tokens in total, so any budget near the context +# window is one batch and changes nothing. What is being bounded here is how much the +# model is asked to hold at once before it starts summarizing away, not what fits. +BATCH_TOKEN_BUDGET = 2000 +MAX_PARALLEL_BATCHES = 8 +# gpt-5.6-terra bills reasoning tokens against the response budget, so this has to +# leave room for the reasoning that precedes the first entry, not just the entries. +RESPONSE_TOKEN_BUDGET = 100000 + + +def _commit_payload(commit: Commit) -> dict[str, str]: + return {"commit_id": commit.hash, "message": commit.message} + + +def batch_commits(commits: list[Commit], token_budget: int | None = None) -> list[list[Commit]]: + """Split commits into batches of at most ``token_budget`` tokens of commit text. + + Batches follow the input order, so each one holds a contiguous run of the + release's history and the merged result stays in commit order. A single commit + over budget gets a batch to itself rather than being dropped or truncated. + """ + # Read the module constant at call time rather than binding it as a default: + # a default is bound at import, so raising BATCH_TOKEN_BUDGET to compare batched + # against unbatched output silently measures two batched runs. + token_budget = BATCH_TOKEN_BUDGET if token_budget is None else token_budget + batches: list[list[Commit]] = [] + current: list[Commit] = [] + current_tokens = 0 + for commit in commits: + tokens = count_tokens(json.dumps(_commit_payload(commit))) + if current and current_tokens + tokens > token_budget: + batches.append(current) + current, current_tokens = [], 0 + current.append(commit) + current_tokens += tokens + if current: + batches.append(current) + return batches + + +def _build_analysis_prompt( version: str, commits: list[Commit], file_diff: str, - integration: str | None = None, -) -> list[ChangelogEntry]: - """Use LLM to analyze commits and return structured changelog entries.""" - commits_json = json.dumps([{"commit_id": c.hash, "message": c.message} for c in commits], indent=2) + integration: str | None, +) -> str: + commits_json = json.dumps([_commit_payload(c) for c in commits], indent=2) subject = f"the {integration} integration for Hindsight" if integration else f"release {version} of Hindsight" @@ -647,7 +693,7 @@ def analyze_commits_with_llm( "its own package) — integrations are versioned and changelogged separately" ) - prompt = f"""Analyze the following git commits for {subject} (an AI memory system). + return f"""Analyze the following git commits for {subject} (an AI memory system). For each meaningful change, create a changelog entry with: - category: one of "feature", "improvement", "bugfix", "breaking", "other" @@ -655,27 +701,116 @@ def analyze_commits_with_llm( - commit_id: the commit hash from the input Rules: -- Group related commits into a single entry if they're part of the same change +- Group related commits into a single entry ONLY when they are literally parts of one + change (a fix and its follow-up, the same feature landed across two commits). Do not + merge distinct fixes into one summary because they touch the same area +- Be complete rather than selective: every commit that changes what a user, operator or + API caller can observe gets its own entry. A bug fix in retain, recall, reflect, + consolidation, embeddings, the CLI, the control plane, a provider integration, the + Docker images or the Helm chart is user-facing even when its title reads as internal - Skip trivial changes (typo fixes, formatting, internal refactoring) - Skip repository-only changes: README updates, CI/GitHub Actions, release scripts, changelog updates, version bumps{skip_integrations_rule} - Focus on user-facing changes that affect the product functionality - Use the exact commit_id from the input (pick the most relevant one if grouping) - If no meaningful changes remain after filtering, return an empty list +This is one batch of the release's commits, so judge each commit on its own; do not +assume a change is absent because its other half is not in this batch. + Commits: {commits_json} Files changed summary: {file_diff[:4000]}""" + +def _parse_entries(client: OpenAI, model: str, prompt: str) -> list[ChangelogEntry]: response = client.beta.chat.completions.parse( model=model, messages=[{"role": "user", "content": prompt}], response_format=ChangelogResponse, - max_completion_tokens=16000, + max_completion_tokens=RESPONSE_TOKEN_BUDGET, ) + parsed = response.choices[0].message.parsed + return parsed.entries if parsed else [] + + +def deduplicate_entries( + client: OpenAI, + model: str, + entries: list[ChangelogEntry], + commit_order: list[str], +) -> list[ChangelogEntry]: + """Merge entries that describe the same change across batch boundaries. + + Batches are contiguous, but a feature and its follow-up fix can still straddle + two of them, and two batches can describe one rollout twice. A deterministic + pass drops repeated commit_ids first — that needs no model — and the LLM pass + only has to catch the same change described in two different ways. + + The result is re-ordered by the release's own commit order and filtered to + commit_ids that were actually in the input, so a dropped or invented commit_id + cannot reorder the changelog or point a reader at a commit that isn't there. + """ + seen: set[str] = set() + unique: list[ChangelogEntry] = [] + for entry in entries: + if entry.commit_id in seen: + continue + seen.add(entry.commit_id) + unique.append(entry) + + if len(unique) > 1: + prompt = f"""These changelog entries were produced independently from separate batches of one +release's commits, so the same change may appear more than once, worded differently. + +Return the entries to keep: +- Drop an entry only when another entry describes the same underlying change. When two + entries describe one change, keep the clearer summary and its commit_id +- Keep everything else exactly as it is: same summary text, same category, same commit_id +- Do not merge distinct changes, do not reword what you keep, and do not invent entries + +Entries: +{json.dumps([e.model_dump() for e in unique], indent=2)}""" + deduped = _parse_entries(client, model, prompt) + by_id = {e.commit_id: e for e in unique} + # Keep the model's text but never its idea of which commits exist. + unique = [e for e in deduped if e.commit_id in by_id] or unique + + position = {commit_id: i for i, commit_id in enumerate(commit_order)} + return sorted(unique, key=lambda e: position.get(e.commit_id, len(position))) + + +def analyze_commits_with_llm( + client: OpenAI, + model: str, + version: str, + commits: list[Commit], + file_diff: str, + integration: str | None = None, +) -> list[ChangelogEntry]: + """Use LLM to analyze commits and return structured changelog entries.""" + batches = batch_commits(commits) + + if len(batches) == 1: + return _parse_entries(client, model, _build_analysis_prompt(version, batches[0], file_diff, integration)) + + console.print(f"[blue]Summarizing {len(commits)} commits in {len(batches)} batches...[/blue]") + with ThreadPoolExecutor(max_workers=MAX_PARALLEL_BATCHES) as pool: + results = list( + pool.map( + lambda batch: _parse_entries( + client, model, _build_analysis_prompt(version, batch, file_diff, integration) + ), + batches, + ) + ) - return response.choices[0].message.parsed.entries + entries = [entry for batch_entries in results for entry in batch_entries] + console.print(f"[blue]{len(entries)} entries before deduplication[/blue]") + deduped = deduplicate_entries(client, model, entries, [c.hash for c in commits]) + console.print(f"[blue]{len(deduped)} entries after deduplication[/blue]") + return deduped def build_changelog_markdown( @@ -773,7 +908,7 @@ def write_changelog(path: Path, header: str, new_entry: str, existing_releases: def generate_changelog_entry( version: str, - llm_model: str = "gpt-5.2", + llm_model: str = "gpt-5.6-terra", ) -> None: """Generate changelog entry for a specific version.""" api_key = os.environ.get("OPENAI_API_KEY") @@ -881,7 +1016,7 @@ def generate_changelog_entry( def generate_integration_changelog_entry( integration: str, version: str, - llm_model: str = "gpt-5.2", + llm_model: str = "gpt-5.6-terra", ) -> None: """Generate changelog entry for a specific integration version.""" if integration not in VALID_INTEGRATIONS: @@ -1004,8 +1139,8 @@ def main(): ) parser.add_argument( "--model", - default="gpt-5.2", - help="OpenAI model to use (default: gpt-5.2)", + default="gpt-5.6-terra", + help="OpenAI model to use (default: gpt-5.6-terra)", ) parser.add_argument( "--integration", diff --git a/hindsight-dev/tests/test_generate_changelog_batching.py b/hindsight-dev/tests/test_generate_changelog_batching.py new file mode 100644 index 0000000000..f990d7cc5c --- /dev/null +++ b/hindsight-dev/tests/test_generate_changelog_batching.py @@ -0,0 +1,135 @@ +"""Tests for token-batched, parallel commit summarization and its dedup pass. + +The LLM calls themselves are stubbed: what these pin is the batching arithmetic +and the guarantees the dedup pass makes about the model's output (order, and that +a commit_id the model invented or dropped can't corrupt the changelog). +""" + +import json + +from hindsight_api.engine.token_encoding import count_tokens + +from hindsight_dev.generate_changelog import ( + BATCH_TOKEN_BUDGET, + ChangelogEntry, + Commit, + analyze_commits_with_llm, + batch_commits, + deduplicate_entries, +) + + +def _max_commit_tokens(batch: list[Commit]) -> int: + """A batch may exceed the budget by its last commit, which is added before the check.""" + return max(count_tokens(json.dumps({"commit_id": c.hash, "message": c.message})) for c in batch) + + +def _commits(n: int, message: str = "fix(api): something user-facing happened here") -> list[Commit]: + return [Commit(hash=f"{i:09x}", message=f"{message} (#{i})") for i in range(n)] + + +class _StubClient: + """Returns one entry per commit named in the prompt, and records the calls.""" + + def __init__(self, dedup_result: list[ChangelogEntry] | None = None): + self.prompts: list[str] = [] + self._dedup_result = dedup_result + parse = self._parse + self.beta = type( + "_Beta", + (), + {"chat": type("_Chat", (), {"completions": type("_C", (), {"parse": staticmethod(parse)})()})()}, + )() + + def _parse(self, *, model, messages, response_format, max_completion_tokens): + prompt = messages[0]["content"] + self.prompts.append(prompt) + if prompt.startswith("These changelog entries were produced independently"): + entries = self._dedup_result if self._dedup_result is not None else [] + else: + payload = json.loads(prompt.split("Commits:\n", 1)[1].split("\n\nFiles changed", 1)[0]) + entries = [ + ChangelogEntry(category="bugfix", summary=c["message"], commit_id=c["commit_id"]) for c in payload + ] + parsed = response_format(entries=entries) + return type("_R", (), {"choices": [type("_C", (), {"message": type("_M", (), {"parsed": parsed})()})()]})() + + +def test_batches_stay_within_the_token_budget_and_preserve_order(): + commits = _commits(200) + batches = batch_commits(commits) + + assert len(batches) > 1 + assert [c for batch in batches for c in batch] == commits + for batch in batches: + assert sum(count_tokens(json.dumps({"commit_id": c.hash, "message": c.message})) for c in batch) <= ( + BATCH_TOKEN_BUDGET + _max_commit_tokens(batch) + ) + + +def test_the_budget_is_read_at_call_time(monkeypatch): + """A default bound at import would make raising the constant a silent no-op.""" + commits = _commits(200) + assert len(batch_commits(commits)) > 1 + + monkeypatch.setattr("hindsight_dev.generate_changelog.BATCH_TOKEN_BUDGET", 10**9) + assert batch_commits(commits) == [commits] + + +def test_a_single_commit_over_budget_gets_its_own_batch(): + huge = Commit(hash="deadbeef1", message="x " * BATCH_TOKEN_BUDGET) + batches = batch_commits([huge, *_commits(2)]) + + assert batches[0] == [huge] + assert len(batches) > 1 + + +def test_a_small_release_is_one_call_with_no_dedup_pass(): + client = _StubClient() + entries = analyze_commits_with_llm(client, "m", "0.1.0", _commits(3), file_diff="") + + assert len(client.prompts) == 1 + assert [e.commit_id for e in entries] == [c.hash for c in _commits(3)] + + +def test_a_large_release_fans_out_and_deduplicates(): + commits = _commits(200) + client = _StubClient(dedup_result=[]) # model drops everything -> fall back to the union + entries = analyze_commits_with_llm(client, "m", "0.1.0", commits, file_diff="") + + analysis_prompts = [p for p in client.prompts if p.startswith("Analyze the following")] + assert len(analysis_prompts) == len(batch_commits(commits)) + assert sum(p.startswith("These changelog entries") for p in client.prompts) == 1 + # Every commit survives, in release order, despite the empty dedup response. + assert [e.commit_id for e in entries] == [c.hash for c in commits] + + +def test_dedup_keeps_release_order_and_rejects_unknown_commit_ids(): + commits = _commits(3) + entries = [ChangelogEntry(category="bugfix", summary=c.message, commit_id=c.hash) for c in reversed(commits)] + kept = [ + ChangelogEntry(category="bugfix", summary="merged", commit_id=commits[2].hash), + ChangelogEntry(category="bugfix", summary="hallucinated", commit_id="ffffffff0"), + ChangelogEntry(category="bugfix", summary="kept", commit_id=commits[0].hash), + ] + client = _StubClient(dedup_result=kept) + + result = deduplicate_entries(client, "m", entries, [c.hash for c in commits]) + + assert [e.commit_id for e in result] == [commits[0].hash, commits[2].hash] + + +def test_dedup_drops_a_repeated_commit_id_before_asking_the_model(): + commits = _commits(2) + duplicated = [ + ChangelogEntry(category="bugfix", summary="first wording", commit_id=commits[0].hash), + ChangelogEntry(category="bugfix", summary="second wording", commit_id=commits[0].hash), + ChangelogEntry(category="bugfix", summary="other", commit_id=commits[1].hash), + ] + client = _StubClient(dedup_result=[]) + + result = deduplicate_entries(client, "m", duplicated, [c.hash for c in commits]) + + assert [e.summary for e in result] == ["first wording", "other"] + sent = json.loads(client.prompts[0].split("Entries:\n", 1)[1]) + assert len(sent) == 2 diff --git a/hindsight-docs/blog/2026-09-09-version-0-10-0.md b/hindsight-docs/blog/2026-09-09-version-0-10-0.md new file mode 100644 index 0000000000..173e685197 --- /dev/null +++ b/hindsight-docs/blog/2026-09-09-version-0-10-0.md @@ -0,0 +1,88 @@ +--- +title: "What's new in Hindsight 0.10.0" +description: Images and files as first-class retain content, a 3.2x faster request path, a prompt preview for every operation, typo-tolerant tag matching, knowledge pages and mental models in document transfers, and the retirement of the bank profile and background endpoints +authors: [nicoloboschi] +date: 2026-09-09 +hide_table_of_contents: true +tags: [release] +--- + +Hindsight 0.10.0 lets you retain a screenshot or a PDF in the same call as the prose around it, makes the request path several times cheaper, and shows you the exact prompt an operation would send before you spend a token on it. It also retires two endpoints that have been deprecated for several releases. + + + +Hindsight 0.10.0 highlights: images and files as first-class retain content, a 3.2x faster request path with concurrent embedding batches and faster token counting, a prompt preview and retain prompt tester, typo-tolerant tag matching and open-vocabulary labels, knowledge pages and mental models in document transfers, and the breaking changes + +- [**Images and files are first-class retain content**](#images-and-files-are-first-class-retain-content): `content` takes an ordered list of text, image and file blocks, and facts cite the attachment they came from. +- [**More throughput out of the same box**](#more-throughput-out-of-the-same-box): A 3.2x faster request path, concurrent embedding batches, and cheaper token counting. +- [**See the prompt before you send it**](#see-the-prompt-before-you-send-it): A preview endpoint for retain, consolidation and reflect, plus a retain prompt tester in the control plane. +- [**Recall: typo-tolerant tags, and labels you don't have to enumerate**](#recall-typo-tolerant-tags-and-labels-you-dont-have-to-enumerate): A `fuzzy` tag resolve mode, and an open-vocabulary `multi-text` entity label. +- [**Take the synthesized layer with you**](#take-the-synthesized-layer-with-you): Knowledge pages and mental models travel in a document transfer. +- [**Breaking changes**](#breaking-changes): The bank profile and background endpoints answer `410 Gone`, and `curl` is gone from the Docker images. + +## Images and files are first-class retain content + +`content` now accepts an ordered list of text, image and file blocks instead of only a string. The extractor reads each attachment in the position it occupies — a chart between two paragraphs is read as the chart between those two paragraphs — and every read surface hands back the attachments behind what it returns. Provenance is per fact, not per chunk: a fact stated in the prose doesn't claim the screenshot next to it as evidence. + +A plain string behaves exactly as before, byte for byte. Idempotency by content hash, `update_mode=append`, chunk-delta re-extraction and `reprocess_document` all keep working, because blocks are flattened at the API boundary into one canonical body and the bytes live in the existing file storage, content-addressed by SHA-256. + +**Recall carries them through.** A recalled fact comes back with the attachments behind it, in the API response and in the control plane, so the screenshot a fact was read from is one click away from the answer instead of something you go looking for in the source document. + +One migration ships with this (`attachments`, `document_attachments`); both tables are new and empty, so it's quick on any size of deployment. + +## More throughput out of the same box + +**A 3.2x faster request path.** Both HTTP middlewares were rewritten as pure ASGI. Starlette's `BaseHTTPMiddleware` spawns a child task per request and pipes the response through a pair of memory-object streams; on cheap routes that machinery cost more than the endpoint. On a 2-CPU container, `/health/live` went from 2476 rps at p99 108ms to 7917 rps at p99 17ms, at lower CPU. Recall is unchanged — it's CPU-bound in the endpoint, nowhere near that ceiling — but every cheap route, health check and poll got a lot cheaper. Nothing was dropped: the HTTP metrics and the unknown-parameter reporting both moved rather than went away. + +**Embedding batches go out concurrently.** Every remote embedding provider walked its batches in a plain loop, so a retain held exactly one embedding request open at a time however much text it had. Batching and a bounded fan-out now live in one place, results are concatenated in input order whatever the completion order, and the same TEI server sustains 903 texts/s at one in-flight request against 2080 at eight. + +**Cheaper token counting.** Counting went through building a full list of token ids only to take its length — once per candidate fact, candidate chunk, source fact and reranker document. Token counting now runs on [toktok](https://github.com/vectorize-io/toktok), the BPE tokenizer we built for this as a drop-in replacement for tiktoken: it returns a count without materialising the ids, which takes the four counting stages of one recall from 34.2 ms to 5.0 ms, and a 77k-token document from 36 ms and 2.8 MB of allocation to 3 ms and effectively none. + +Alongside those, recall stops building trace payloads when nothing asked for a trace, config resolution stops deep-copying the global config on every call, and the mental-model staleness check no longer walks the bank. + +## See the prompt before you send it + +A bank's missions and strategies only mean something once you can see the prompt they land in. `POST /banks/{id}/prompts/preview` returns the messages retain, consolidation or reflect _would_ send, in send order — no LLM call, no writes, runtime data replaced by a fixed placeholder. Messages come back as blocks, and the active ones concatenate to exactly the text that would be sent; a block for a setting you've switched off comes back marked inactive, so an unset mission is still visible in the place it would occupy. + +The control plane adds a **prompt tester for retain** on top of it: edit the input, see the prompt, and run the extraction to see what facts come out. + +## Recall: typo-tolerant tags, and labels you don't have to enumerate + +**Fuzzy tag matching.** Tag filtering is exact array containment, so a caller filtering on `typsecript` loses the memory tagged `typescript` before ranking ever runs — and no amount of better ranking fixes that, because the match itself has to tolerate the misspelling. A `tag_groups` leaf now takes an optional `resolve` field: leave it at `exact` and nothing changes, set it to `fuzzy` and that leaf's tags are matched against the bank's tags by trigram similarity instead of literally. That is the entire API change — no new config, no new response field. + +It resolves `typescropt`→`typescript`, `kubernets`→`kubernetes` and `user:alcie`→`user:alice`, and deliberately doesn't resolve `mango`→`mongo`. One limit is worth knowing up front: similarity is length-sensitive, so a short tag has few trigrams and a single edit destroys most of them — `kakfa`→`kafka` does not resolve. Fuzzy matching is effective on descriptive tags and close to inert on very short ones, and that same property is what keeps unrelated short words apart. + +**Open-vocabulary entity labels.** Entity labels could classify a fact against a fixed vocabulary, or capture one free string. Neither covers the case where you want _several_ values that can't be enumerated when the bank is configured — the names a thing is known by, including abbreviations and alternative spellings; the tickets a fact cites; product codes. + +The new `multi-text` type is a list of strings with no fixed vocabulary, so the extractor writes as many values as the content warrants. Each value becomes its own entity and, with `tag: true`, a tag — so a bank can derive a classification from its own content and then filter on it at recall, without the caller ever supplying the vocabulary. Both client wrappers can express this now; the TypeScript one previously had no way to configure entity labels at all. + +## Take the synthesized layer with you + +A document transfer moved documents and observations — the raw layer. Everything synthesized on top of it, the knowledge pages and mental models a bank had spent real LLM budget building, stayed behind and had to be rebuilt from scratch on the other side. + +Pass `include_knowledge_base` on a whole-bank export (or tick the box in the control plane) and the archive carries the knowledge pages and mental models too, alongside the documents. It's opt-in and off by default, and only available when exporting a whole bank rather than a document list, because a page synthesized from the whole bank means nothing next to an arbitrary subset of it. The manifest reports what came along, so an archive tells you whether it holds a knowledge base before you import it. + +The import side preserves what the export captured: mental-model content survives the round trip intact, and observations keep their freshness rather than arriving as though they'd all been recorded at import time. + +## Also in this release + +- **A registry for extensions shipped outside the server.** `hindsight-extensions/` is now where extensions distributed separately live, with the Supabase tenant extension as its first entry (`hindsight-ext-supabase-tenant`) — which takes a third-party IdP's JWT client out of every Hindsight install's dependency graph. **If you use it, this is an upgrade step:** the built-in path `hindsight_api.extensions.builtin.supabase_tenant` no longer exists, so an install still pointing at it fails at startup with `ModuleNotFoundError`. Add the extension to your image and set `HINDSIGHT_API_TENANT_EXTENSION=hindsight_ext_supabase_tenant:SupabaseTenantExtension`; every `HINDSIGHT_API_TENANT_*` setting and the schema naming are unchanged. New alongside it: `StaticKeysTenantExtension`, env-configured per-user API keys with per-schema isolation, for deployments that want multi-user isolation without an identity provider at all. +- **Meta Model API as a first-class LLM provider.** `HINDSIGHT_API_LLM_PROVIDER=meta` routes extraction, reflection and consolidation to the Muse family. Note that Muse Spark always reasons — `reasoning_effort: "none"` is rejected, and reasoning tokens bill against the output budget, so give the per-operation token limits headroom. +- **Reflect stops inventing numbers for gaps.** Asked about a period the bank holds no data for, reflect could extrapolate a specific figure from neighbouring periods and present it as reliably inferred. It now says the value isn't recorded, while still drawing qualitative conclusions from what is. The same release stops mental-model and knowledge-page delta refreshes from overwriting stored content with what the newest batch alone says — a running count replaced by the batch's count, or an earlier recorded event erased because the batch didn't mention it. +- **Bulk ingest no longer starves other banks.** The worker rotates its slots across banks, so one bank ingesting a large corpus can't hold every slot while everyone else waits. +- **Listing mental models returns metadata by default.** `GET .../mental-models` defaulted to every model's full synthesized content, which bloats a caller's context and pulls a bank's whole synthesized knowledge in one call. It now defaults to `detail=metadata`; pass `detail=content` or `detail=full` to get content back. The MCP `list_mental_models` tool is metadata-only — read a specific model with `get_mental_model`. +- A **banks overview page with bulk delete** in the control plane, a **Business Executive** bank template, **Prometheus ServiceMonitor** support and `extraContainers`/`extraInitContainers` in the Helm chart, and optional **CPU profiling** to the logs, controlled by environment variables. + +## Breaking changes + +**The bank profile and background endpoints are gone.** `GET`/`PUT /v1/default/banks/{bank_id}/profile` and `POST /v1/default/banks/{bank_id}/background` have been deprecated for several releases. They now answer `410 Gone` with the replacement call in the detail. The routes stay in the OpenAPI spec with unchanged signatures, so no generated SDK method disappears from under a caller — only the behaviour changes. + +Disposition traits and the reflect mission are bank configuration, and already were: read and write them through the bank config API. The display name these endpoints also returned is on the bank list. To make the config API a complete replacement, **reading** `GET .../config` is no longer gated on `HINDSIGHT_API_ENABLE_BANK_CONFIG_API` — that flag now gates only writes, because a bank must always be able to read its own resolved settings. + +The control plane and the CLI moved in the same change. One behaviour difference worth knowing if you scripted against it: `hindsight bank background` now _replaces_ the mission rather than LLM-merging into it, and warns that it does. + +**`curl` is no longer in the Docker images.** It had one in-image consumer — the standalone image's own readiness loop, now replaced — and it was the sole reason nine unfixable HIGH vulnerability findings shipped in every image. Hindsight's images define no `HEALTHCHECK`, so nothing breaks by default, but if your Compose `healthcheck` or a Kubernetes `exec` probe runs `curl` inside the Hindsight container, it will now fail with "command not found". Switch it to an HTTP probe (Kubernetes `httpGet` against `/health/live`) or run the check from outside the container. + +--- + +0.10.0 carries a long list of fixes across retain, recall, embeddings, the CLI and the supported providers. See the [changelog](/changelog) for the full list. diff --git a/hindsight-docs/src/pages/changelog/index.md b/hindsight-docs/src/pages/changelog/index.md index bf003ed9f9..cfbfb6dbdd 100644 --- a/hindsight-docs/src/pages/changelog/index.md +++ b/hindsight-docs/src/pages/changelog/index.md @@ -9,6 +9,189 @@ import PageHero from '@site/src/components/PageHero'; This page covers the Hindsight core (API, CLI, control plane). Each integration is released on its own cadence and has its own changelog — find it on the [integration's page](/integrations). +## [0.10.0](https://github.com/vectorize-io/hindsight/releases/tag/v0.10.0) + +**Breaking Changes** + +- Mental-model list responses now return metadata by default rather than full model content.·@cdbartholomew@cdbartholomew·fb94ce034 +- API and control-plane Docker runtime images no longer include curl.·@nicoloboschi@nicoloboschi·5f9bff905 +- Remove the bank profile and background API endpoints.·@nicoloboschi@nicoloboschi·163fbb0ed + +**Features** + +- The bank selector now keeps the currently selected bank at the top.·@nicoloboschi@nicoloboschi·4af316e74 +- Added a control-plane button to pause ambient constellation motion.·@nicoloboschi@nicoloboschi·558017a84 +- Recall results now include the attachments underlying each returned fact.·@nicoloboschi@nicoloboschi·e1310d34f +- Added optional CPU profiling configured through environment variables, with results logged by the service.·@nicoloboschi@nicoloboschi·da0444a72 +- Added control-plane retain prompt testing and prompt previews for all operations.·@nicoloboschi@nicoloboschi·9e6d9e76c +- Route retained items to LLM chain members based on item metadata.·@nicoloboschi@nicoloboschi·864d92692 +- Add a control-plane banks overview with bulk deletion support.·@nicoloboschi@nicoloboschi·7244098c0 +- Support inline images and files as first-class content in retained memories.·@nicoloboschi@nicoloboschi·280f09820 +- Add the Meta Model API as a built-in LLM provider.·@nicoloboschi@nicoloboschi·30ce3b8d1 +- Support fuzzy tag matching for tag-group leaf values during recall.·@nicoloboschi@nicoloboschi·f747d96c3 +- Database migrations can run in a separate subprocess and be enabled or disabled with configuration.·@nicoloboschi@nicoloboschi·a46783815 +- Entity labels now support open-ended multi-value text labels.·@nicoloboschi@nicoloboschi·21c216004 +- Webhook deliveries now include timestamped HMAC SHA-256 signatures for verification.·@nicoloboschi@nicoloboschi·d936d4931 +- Codex providers can use separate credential directories per provider.·@nicoloboschi@nicoloboschi·c42e6323e +- Mental-model delta refreshes now provide the operation schema to the model.·@nicoloboschi@nicoloboschi·f22c36250 +- Add a CUDA-enabled standalone Docker deployment image and recipe.·@Sanderhoff-alt@Sanderhoff-alt·a6a6c8e5a +- Expose the complete mental-model trigger policy, including knowledge pages, through MCP.·@Sanderhoff-alt@Sanderhoff-alt·56ba04204 +- Meter mental-model content according to what is delivered, regardless of the serving endpoint.·@cdbartholomew@cdbartholomew·870d20d6f +- Add a CLI option to choose full or delta trigger mode when updating mental models.·@mdbenito@mdbenito·1cc2f9c10 +- Allow configuring the PostgreSQL schema that provides pg_search extension functions.·@Sanderhoff-alt@Sanderhoff-alt·0b6b5261a +- Add Helm ServiceMonitor support for Prometheus Operator deployments.·@aWN4Y25pa2EK@aWN4Y25pa2EK·a6f99c995 +- Allow extra containers and init containers in Helm API, worker, and Control Plane workloads.·@Adrastopoulos@Adrastopoulos·eb3d1d587 +- Add Control Plane translations for knowledge-base and mental-model refinement workflows.·@Sanderhoff-alt@Sanderhoff-alt·35b98cb94 +- Include knowledge-base content when exporting document transfers.·@nicoloboschi@nicoloboschi·8d8dab346 +- Allow deployments to own and manage their database maintenance routines.·@cdbartholomew@cdbartholomew·7ba5fb23a +- Add a Business Executive template for creating new memory banks.·@benfrank241@benfrank241·b75fa6103 +- Allow per-bank disabling of text search for vector-only recall.·@nicoloboschi@nicoloboschi·cd8d4d399 + +**Improvements** + +- Reduced CPU usage during recall requests.·@nicoloboschi@nicoloboschi·d9df6d2a4 +- Improved recall performance and addressed issues discovered through per-phase timing analysis.·@nicoloboschi@nicoloboschi·5be9ad915 +- Improved database connection performance by avoiding unnecessary reset work when connections are released.·@nicoloboschi@nicoloboschi·d11371c2b +- Improved API throughput and reduced latency on lightweight routes.·@nicoloboschi@nicoloboschi·2544a73c4 +- Recall no longer builds trace data unless tracing is requested, reducing request overhead.·@nicoloboschi@nicoloboschi·f16e515f5 +- Accelerated semantic-link calculation for documents retained in the same batch.·@Sanderhoff-alt@Sanderhoff-alt·b04579481 +- Reduced configuration-resolution overhead.·@nicoloboschi@nicoloboschi·3e1d47fdc +- Improved entity-resolution performance for duplicate entities within a batch.·@Sanderhoff-alt@Sanderhoff-alt·bc06bd051 +- Reduced overhead when checking mental-model freshness.·@nicoloboschi@nicoloboschi·78c10a382 +- Standalone Docker images no longer depend on the NodeSource bootstrap during image builds.·@nicoloboschi@nicoloboschi·798aebd66 +- Docker images now use the Node 24 active-LTS runtime.·@nicoloboschi@nicoloboschi·c57331a94 +- Speed up Docker image builds by preserving file ownership during copy operations.·@SharkyRawr@SharkyRawr·01ed1e15f +- Reduce runtime Docker image footprint by removing unused libxml2 packages.·@rschlek@rschlek·f9e91e21f +- Include operation attributes in exported tracing spans for better observability.·@koriyoshi2041@koriyoshi2041·356aa6cd4 +- Keep llama.cpp prompt caches in RAM to avoid persisting sensitive prompt data.·@cdbartholomew@cdbartholomew·676e2139e +- Process remote embedding-provider batches concurrently for faster indexing.·@nicoloboschi@nicoloboschi·9697c6900 +- Token counting is faster and no longer leaks tokenizer cache memory.·@nicoloboschi@nicoloboschi·53ed37488 +- API startup avoids repeatedly loading optional LLM and document-processing components.·@nicoloboschi@nicoloboschi·e48a0c9a7 +- Date parsing initializes shared locale data once, improving concurrent API request performance.·@nicoloboschi@nicoloboschi·3d0a66602 +- Recall metrics now capture store-served recall phases during normal traffic.·@nicoloboschi@nicoloboschi·b746ad139 +- API traces are named by operation and connect queued retain requests to their workers.·@nicoloboschi@nicoloboschi·ebcd88f97 +- Recall observability now reports timing for individual recall phases.·@nicoloboschi@nicoloboschi·b00cc4047 +- Operation metrics now include time spent in request-validation hooks.·@nicoloboschi@nicoloboschi·97ae1d08c +- Observation graph expansion uses fewer database reads, improving retrieval performance.·@jervaise@jervaise·3bba2c01f +- Docker images remove known high-severity vulnerabilities and are checked for them daily.·@nicoloboschi@nicoloboschi·44f937631 +- Improve retain performance by reducing embedding vector serialization overhead.·@Sanderhoff-alt@Sanderhoff-alt·0478c09c4 +- Avoid repeated database checks for invalid PostgreSQL configuration settings.·@Andreymi@Andreymi·eca881d0b +- Reduce unnecessary garbage collection overhead for local CPU reranker providers.·@Sanderhoff-alt@Sanderhoff-alt·6f9f935e6 +- Improved tokenization performance and updated the default tokenizer encoding.·@nicoloboschi@nicoloboschi·9fcb7ca7a +- Improved retention performance by embedding a document's chunks in batches rather than individually.·@nicoloboschi@nicoloboschi·ca2c983e1 + +**Bug Fixes** + +- Reflect no longer states a specific value for a period the memories don't cover — it says the data isn't recorded instead of extrapolating a number from neighbouring periods.·@nicoloboschi@nicoloboschi·630c3a63e +- Mental-model and knowledge-page delta refreshes no longer overwrite stored content from what the newest batch alone says, such as replacing a running count or erasing an earlier recorded event.·@nicoloboschi@nicoloboschi·630c3a63e +- Fixed two issues uncovered by end-to-end system testing.·@nicoloboschi@nicoloboschi·59e25d74f +- Generated API clients now return downloaded binary content as bytes.·@nicoloboschi@nicoloboschi·88456981f +- Recall now finds attachments correctly for store-owned banks.·@nicoloboschi@nicoloboschi·b05546b57 +- Entity mention counts are now restored correctly when mentions are removed.·@nicoloboschi@nicoloboschi·64a98aa56 +- Clearing an occurrence start date now also clears the legacy event date.·@nicoloboschi@nicoloboschi·203d95c7c +- Reflection tool-call IDs are now unique so turns are accepted by strict provider APIs.·@gwthm-in@gwthm-in·5df6398fa +- Retain chunk IDs no longer collide between banks.·@nicoloboschi@nicoloboschi·179938a65 +- Reflection results now report when structured-output extraction fails.·@nicoloboschi@nicoloboschi·f5b3f76a8 +- Bank template imports now expose their request body correctly to API clients.·@nicoloboschi@nicoloboschi·1081a2ea4 +- Store-owned retain operations no longer fail while processing their own log entries.·@nicoloboschi@nicoloboschi·134207d3c +- API list and graph responses now provide typed rows instead of untyped objects.·@nicoloboschi@nicoloboschi·ef3ccdba3 +- The Python convenience client now includes the missing asynchronous methods.·@nicoloboschi@nicoloboschi·2bf10435d +- Retain completion notifications are now sent only after the store transaction succeeds.·@kyletser@kyletser·511c86e10 +- Split append operations now retain the complete document body rather than only its final portion.·@nicoloboschi@nicoloboschi·fda969777 +- Recall search traces now report the query timestamp supplied by the caller.·@nicoloboschi@nicoloboschi·e366ff407 +- Vertex single-content embedding models now receive each text in a compatible request format.·@nicoloboschi@nicoloboschi·b2a7257eb +- Local ML ARM64 images now run on ARMv8.0 CPUs.·@nicoloboschi@nicoloboschi·aeaf4b1bd +- Search now handles future-dated memories correctly when calculating recency.·@ebarkhordar@ebarkhordar·1ca8c4d60 +- Gemini embedding requests now send each input in the required separate content format.·@Sanderhoff-alt@Sanderhoff-alt·a08ec8ddf +- Bank-scoped read requests now return 404 when the requested bank does not exist.·@nicoloboschi@nicoloboschi·66992496f +- Embedding inputs are now limited to each model's supported context length, including prefixes.·@nicoloboschi@nicoloboschi·a3c5b350d +- Make consolidation reliably identify deletion targets and account for discarded batch responses.·@nicoloboschi@nicoloboschi·05c775c41 +- Serve concurrent cross-event-loop requests fairly in arrival order.·@ebarkhordar@ebarkhordar·310c9f694 +- Apply configured token limits to all reflection synthesis paths.·@nicoloboschi@nicoloboschi·d39170010 +- Prevent importing observations that reference missing source units.·@nicoloboschi@nicoloboschi·4a447c061 +- Honor configured concurrency limits when starting the server from environment settings.·@Sanderhoff-alt@Sanderhoff-alt·f19c424e0 +- Allow cancellation of operations while they are still running.·@nicoloboschi@nicoloboschi·b1de1b941 +- Correctly recognize requests sent to OpenAI-compatible LLM providers.·@SharkyRawr@SharkyRawr·5092f3e11 +- Sanitize complete retain items consistently when they enter the memory engine.·@nicoloboschi@nicoloboschi·bd607ab8b +- Interpret “last weekend” on a weekend as the preceding weekend.·@NgoQuocViet2001@NgoQuocViet2001·cef202e6b +- Prevent the CLI from aborting when its output pipe is closed early.·@2anoubis@2anoubis·a72f9de15 +- Add bounded retries for all remote reranker requests.·@nicoloboschi@nicoloboschi·d69abe7b9 +- Improve reliability of TEI embedding requests when used across threads.·@nicoloboschi@nicoloboschi·e7987ba69 +- Apply the shared retry policy to Cohere and ZeroEntropy embedding providers.·@nicoloboschi@nicoloboschi·c1f70087f +- Apply the shared retry policy to Gemini embedding requests.·@nicoloboschi@nicoloboschi·36f4a061f +- Make split reflection synthesis map calls deterministic by using zero temperature.·@nicoloboschi@nicoloboschi·98e565d3f +- Allow Bedrock inference profile ARNs when using the LiteLLM embedding provider.·@nicoloboschi@nicoloboschi·7a52b0a6b +- Preserve per-memory entity labels when re-retaining documents updates their tags.·@nicoloboschi@nicoloboschi·576cf40a2 +- Install PostgreSQL extensions in the public schema for compatible API deployments.·@nicoloboschi@nicoloboschi·901b5c696 +- Forward all HINDSIGHT_* environment settings to the embed daemon.·@nicoloboschi@nicoloboschi·7eb0fcc61 +- Run tenant-provisioning migrations through the proper isolation boundary.·@nicoloboschi@nicoloboschi·216272814 +- Show server response details when CLI API requests fail.·@nicoloboschi@nicoloboschi·737e5bf42 +- Return complete paginated results from observation-scope and webhook API endpoints.·@nicoloboschi@nicoloboschi·d20893a83 +- Score fuzzy recall tag matches using tag values rather than namespaced tag names.·@nicoloboschi@nicoloboschi·00caa8eab +- Client wrappers now apply bank configuration consistently with the server.·@nicoloboschi@nicoloboschi·e8518c392 +- Creating a mental model with an existing ID now returns a clear conflict response.·@kubaodias@kubaodias·1a8a1f50e +- Stopping the service no longer triggers a false event-loop stall alert.·@nicoloboschi@nicoloboschi·f0cf699cc +- Local models now safely handle misaligned model weights and fail clearly when they cannot be loaded.·@nicoloboschi@nicoloboschi·4e952b4e5 +- Re-retaining a document correctly invalidates observations when its scope changes.·@nicoloboschi@nicoloboschi·7be0f88ef +- LLM timeout diagnostics now identify the request phase in which a stalled call failed.·@nicoloboschi@nicoloboschi·3531e82f9 +- Reflection runs now fail when retrieval tools fail and record refreshes refused by the model.·@nicoloboschi@nicoloboschi·921ae824a +- Prioritized recall results are ranked correctly regardless of score scale.·@nicoloboschi@nicoloboschi·aee42254a +- Reflections now produce the required document sections field.·@nicoloboschi@nicoloboschi·9bfad10a4 +- Worker capacity is rotated across banks so bulk ingestion no longer starves other banks.·@nicoloboschi@nicoloboschi·b75e94191 +- Mental-model reflection skips empty scopes instead of running with nothing to process.·@nicoloboschi@nicoloboschi·c507e70e3 +- Retain extraction no longer derives its narrator from a bank's display name.·@nicoloboschi@nicoloboschi·aabbe8b22 +- Template imports now make authorization-safe write decisions using current bank state.·@Sanderhoff-alt@Sanderhoff-alt·17fbc4f14 +- Strict structured-output schemas now work when referenced schema fields include sibling constraints.·@nicoloboschi@nicoloboschi·db87a7467 +- Consolidation correctly resolves observation scopes before batching tag updates.·@ferrastas@ferrastas·eff3546ec +- Legacy bank mutations now authorize and provision banks before making changes.·@Sanderhoff-alt@Sanderhoff-alt·24adcfce7 +- Knowledge search now returns matches for any query term instead of requiring every term.·@Sanderhoff-alt@Sanderhoff-alt·a373ffab6 +- Editing a memory now updates its vector embedding together with its fields.·@nicoloboschi@nicoloboschi·31db3983a +- Grafana monitoring dashboards can now be imported into older Grafana versions.·@oldnicke@oldnicke·7083fd790 +- Bank configuration changes are read fresh for the affected bank instead of serving stale cached values.·@nicoloboschi@nicoloboschi·689c9b694 +- Reprocess requests now re-extract document content instead of being ignored.·@nicoloboschi@nicoloboschi·9a4be5e05 +- Embedding execution limits concurrent ONNX and import operations to prevent overload.·@nicoloboschi@nicoloboschi·7051c6e3b +- Every LLM provider now enforces a per-request deadline.·@nicoloboschi@nicoloboschi·7729396e1 +- Keyword minimum-score filters are honored across all text-search backends.·@nicoloboschi@nicoloboschi·78d46a718 +- Recall scores coarse dates by their full period rather than only the period's first day.·@nicoloboschi@nicoloboschi·6a796f9f5 +- LiteLLM embedding startup is retried reliably, with configurable embedding dimensions available.·@Sanderhoff-alt@Sanderhoff-alt·dd60a542e +- Avoid unnecessary retagging work when a document tag update makes no actual change.·@nicoloboschi@nicoloboschi·43f545e9b +- Repair malformed JSON handling for OpenAI-compatible LLM providers.·@nicoloboschi@nicoloboschi·98e29817a +- Correctly send consecutive tool results to Anthropic models.·@zlguo1996@zlguo1996·169c6ef58 +- Ensure the complete mental-model document is embedded whenever it is updated.·@nicoloboschi@nicoloboschi·fd2e0c61f +- Allow reflection delta updates to add sections containing explicit IDs.·@nicoloboschi@nicoloboschi·6b8dfe74f +- Accurately determine visible output tokens for OpenAI-compatible providers.·@nicoloboschi@nicoloboschi·72c9c8f9e +- Handle embedding profiles and log files consistently as UTF-8 text.·@koriyoshi2041@koriyoshi2041·ff29e5ad7 +- Apply all writes from a consolidation response atomically to prevent partial updates.·@nicoloboschi@nicoloboschi·ca38687cd +- Report truncated non-streaming LLM completions as output-too-long errors.·@ebarkhordar@ebarkhordar·7ef98859e +- Only use occurred-time constraints when the selected backend supports them.·@nickanderson@nickanderson·5c78b8132 +- Preserve trailing base64 padding when reading API keys from CLI config files.·@2anoubis@2anoubis·ddf57744c +- Require the appropriate bank-read permission before exposing mental-model history.·@Sword-Saint69@Sword-Saint69·ad2ffa499 +- Preserve complete retain items so reprocessing faithfully replays the original input.·@shauneccles@shauneccles·7b6177b46 +- Apply configured LLM temperature settings to reflection requests.·@koriyoshi2041@koriyoshi2041·7874561b0 +- Read embedding profile configuration and Control Center environment files as UTF-8.·@koriyoshi2041@koriyoshi2041·3e8226d1a +- Preserve intended HTTP status codes when webhook validation fails.·@r266-tech@r266-tech·8743b4594 +- Restore database identity sequences after an administrative restore.·@r266-tech@r266-tech·e82f670f7 +- Apply knowledge-base node updates atomically to prevent partial changes.·@nicoloboschi@nicoloboschi·29901d43b +- Sanitize model-generated text consistently at all LLM boundaries.·@BrianMcBrayer@BrianMcBrayer·aab937e5f +- Use the selected output language without retaining conflicting source-language instructions.·@feniix@feniix·ba0be9b30 +- Update only the mental-model trigger settings explicitly supplied by SDK callers.·@nicoloboschi@nicoloboschi·55dafc27b +- Stop stalled consolidation tasks after they exceed an idle wall-clock limit.·@Sanderhoff-alt@Sanderhoff-alt·698620786 +- Accept reflection delta responses provided as a top-level operation array.·@ebarkhordar@ebarkhordar·7fe6a5e79 +- Honor configured LLM request timeouts when using the native Ollama provider.·@MasterST1337@MasterST1337·6e0f044f2 +- Detect worker tasks that are stuck without making stage progress.·@BrianHotopp@BrianHotopp·532aa8698 +- Add bounded retries, backoff, and timeouts for LiteLLM embedding providers.·@icculp@icculp·906dbcdbd +- Retry failed Text Embeddings Inference embedding requests.·@oldnicke@oldnicke·8916986f5 +- Bound retain link-processing work to prevent oversized deltas from exhausting worker memory.·@nicoloboschi@nicoloboschi·8833a7518 +- Honor OpenAI rate-limit reset headers when retrying rate-limited requests.·@romanbsd@romanbsd·20e66093d +- Remap mental-model evidence references correctly during document transfers.·@nicoloboschi@nicoloboschi·c143de2a9 +- Fixed Knowledge view links so page and mental-model IDs are scoped to the active memory bank.·@nicoloboschi@nicoloboschi·3a399343b +- Fixed PostgreSQL text-search scoring so each search result receives its own relevance score.·@mameikagou@mameikagou·23c63e173 +- Fixed retention of oversized items by rebuilding sub-batches from the original content span.·@nicoloboschi@nicoloboschi·d258f7b82 + +**Database Migrations** + +- `e2f4a6c8b0d1` — Add attachments and document_attachments (inline retain attachments).·attachmentsmedium document_attachmentsmedium·#4077 + ## [0.9.2](https://github.com/vectorize-io/hindsight/releases/tag/v0.9.2) **Features** diff --git a/hindsight-docs/static/img/blog/version-0-10-0-release.png b/hindsight-docs/static/img/blog/version-0-10-0-release.png new file mode 100644 index 0000000000..f0cc12b2d8 Binary files /dev/null and b/hindsight-docs/static/img/blog/version-0-10-0-release.png differ diff --git a/skills/hindsight-docs/references/changelog/index.md b/skills/hindsight-docs/references/changelog/index.md index 87d6130ea9..41df1ac136 100644 --- a/skills/hindsight-docs/references/changelog/index.md +++ b/skills/hindsight-docs/references/changelog/index.md @@ -9,6 +9,189 @@ import PageHero from '@site/src/components/PageHero'; This page covers the Hindsight core (API, CLI, control plane). Each integration is released on its own cadence and has its own changelog — find it on the integration's page. +## [0.10.0](https://github.com/vectorize-io/hindsight/releases/tag/v0.10.0) + +**Breaking Changes** + +- Mental-model list responses now return metadata by default rather than full model content.·@cdbartholomew·fb94ce034 +- API and control-plane Docker runtime images no longer include curl.·@nicoloboschi·5f9bff905 +- Remove the bank profile and background API endpoints.·@nicoloboschi·163fbb0ed + +**Features** + +- The bank selector now keeps the currently selected bank at the top.·@nicoloboschi·4af316e74 +- Added a control-plane button to pause ambient constellation motion.·@nicoloboschi·558017a84 +- Recall results now include the attachments underlying each returned fact.·@nicoloboschi·e1310d34f +- Added optional CPU profiling configured through environment variables, with results logged by the service.·@nicoloboschi·da0444a72 +- Added control-plane retain prompt testing and prompt previews for all operations.·@nicoloboschi·9e6d9e76c +- Route retained items to LLM chain members based on item metadata.·@nicoloboschi·864d92692 +- Add a control-plane banks overview with bulk deletion support.·@nicoloboschi·7244098c0 +- Support inline images and files as first-class content in retained memories.·@nicoloboschi·280f09820 +- Add the Meta Model API as a built-in LLM provider.·@nicoloboschi·30ce3b8d1 +- Support fuzzy tag matching for tag-group leaf values during recall.·@nicoloboschi·f747d96c3 +- Database migrations can run in a separate subprocess and be enabled or disabled with configuration.·@nicoloboschi·a46783815 +- Entity labels now support open-ended multi-value text labels.·@nicoloboschi·21c216004 +- Webhook deliveries now include timestamped HMAC SHA-256 signatures for verification.·@nicoloboschi·d936d4931 +- Codex providers can use separate credential directories per provider.·@nicoloboschi·c42e6323e +- Mental-model delta refreshes now provide the operation schema to the model.·@nicoloboschi·f22c36250 +- Add a CUDA-enabled standalone Docker deployment image and recipe.·@Sanderhoff-alt·a6a6c8e5a +- Expose the complete mental-model trigger policy, including knowledge pages, through MCP.·@Sanderhoff-alt·56ba04204 +- Meter mental-model content according to what is delivered, regardless of the serving endpoint.·@cdbartholomew·870d20d6f +- Add a CLI option to choose full or delta trigger mode when updating mental models.·@mdbenito·1cc2f9c10 +- Allow configuring the PostgreSQL schema that provides pg_search extension functions.·@Sanderhoff-alt·0b6b5261a +- Add Helm ServiceMonitor support for Prometheus Operator deployments.·@aWN4Y25pa2EK·a6f99c995 +- Allow extra containers and init containers in Helm API, worker, and Control Plane workloads.·@Adrastopoulos·eb3d1d587 +- Add Control Plane translations for knowledge-base and mental-model refinement workflows.·@Sanderhoff-alt·35b98cb94 +- Include knowledge-base content when exporting document transfers.·@nicoloboschi·8d8dab346 +- Allow deployments to own and manage their database maintenance routines.·@cdbartholomew·7ba5fb23a +- Add a Business Executive template for creating new memory banks.·@benfrank241·b75fa6103 +- Allow per-bank disabling of text search for vector-only recall.·@nicoloboschi·cd8d4d399 + +**Improvements** + +- Reduced CPU usage during recall requests.·@nicoloboschi·d9df6d2a4 +- Improved recall performance and addressed issues discovered through per-phase timing analysis.·@nicoloboschi·5be9ad915 +- Improved database connection performance by avoiding unnecessary reset work when connections are released.·@nicoloboschi·d11371c2b +- Improved API throughput and reduced latency on lightweight routes.·@nicoloboschi·2544a73c4 +- Recall no longer builds trace data unless tracing is requested, reducing request overhead.·@nicoloboschi·f16e515f5 +- Accelerated semantic-link calculation for documents retained in the same batch.·@Sanderhoff-alt·b04579481 +- Reduced configuration-resolution overhead.·@nicoloboschi·3e1d47fdc +- Improved entity-resolution performance for duplicate entities within a batch.·@Sanderhoff-alt·bc06bd051 +- Reduced overhead when checking mental-model freshness.·@nicoloboschi·78c10a382 +- Standalone Docker images no longer depend on the NodeSource bootstrap during image builds.·@nicoloboschi·798aebd66 +- Docker images now use the Node 24 active-LTS runtime.·@nicoloboschi·c57331a94 +- Speed up Docker image builds by preserving file ownership during copy operations.·@SharkyRawr·01ed1e15f +- Reduce runtime Docker image footprint by removing unused libxml2 packages.·@rschlek·f9e91e21f +- Include operation attributes in exported tracing spans for better observability.·@koriyoshi2041·356aa6cd4 +- Keep llama.cpp prompt caches in RAM to avoid persisting sensitive prompt data.·@cdbartholomew·676e2139e +- Process remote embedding-provider batches concurrently for faster indexing.·@nicoloboschi·9697c6900 +- Token counting is faster and no longer leaks tokenizer cache memory.·@nicoloboschi·53ed37488 +- API startup avoids repeatedly loading optional LLM and document-processing components.·@nicoloboschi·e48a0c9a7 +- Date parsing initializes shared locale data once, improving concurrent API request performance.·@nicoloboschi·3d0a66602 +- Recall metrics now capture store-served recall phases during normal traffic.·@nicoloboschi·b746ad139 +- API traces are named by operation and connect queued retain requests to their workers.·@nicoloboschi·ebcd88f97 +- Recall observability now reports timing for individual recall phases.·@nicoloboschi·b00cc4047 +- Operation metrics now include time spent in request-validation hooks.·@nicoloboschi·97ae1d08c +- Observation graph expansion uses fewer database reads, improving retrieval performance.·@jervaise·3bba2c01f +- Docker images remove known high-severity vulnerabilities and are checked for them daily.·@nicoloboschi·44f937631 +- Improve retain performance by reducing embedding vector serialization overhead.·@Sanderhoff-alt·0478c09c4 +- Avoid repeated database checks for invalid PostgreSQL configuration settings.·@Andreymi·eca881d0b +- Reduce unnecessary garbage collection overhead for local CPU reranker providers.·@Sanderhoff-alt·6f9f935e6 +- Improved tokenization performance and updated the default tokenizer encoding.·@nicoloboschi·9fcb7ca7a +- Improved retention performance by embedding a document's chunks in batches rather than individually.·@nicoloboschi·ca2c983e1 + +**Bug Fixes** + +- Reflect no longer states a specific value for a period the memories don't cover — it says the data isn't recorded instead of extrapolating a number from neighbouring periods.·@nicoloboschi·630c3a63e +- Mental-model and knowledge-page delta refreshes no longer overwrite stored content from what the newest batch alone says, such as replacing a running count or erasing an earlier recorded event.·@nicoloboschi·630c3a63e +- Fixed two issues uncovered by end-to-end system testing.·@nicoloboschi·59e25d74f +- Generated API clients now return downloaded binary content as bytes.·@nicoloboschi·88456981f +- Recall now finds attachments correctly for store-owned banks.·@nicoloboschi·b05546b57 +- Entity mention counts are now restored correctly when mentions are removed.·@nicoloboschi·64a98aa56 +- Clearing an occurrence start date now also clears the legacy event date.·@nicoloboschi·203d95c7c +- Reflection tool-call IDs are now unique so turns are accepted by strict provider APIs.·@gwthm-in·5df6398fa +- Retain chunk IDs no longer collide between banks.·@nicoloboschi·179938a65 +- Reflection results now report when structured-output extraction fails.·@nicoloboschi·f5b3f76a8 +- Bank template imports now expose their request body correctly to API clients.·@nicoloboschi·1081a2ea4 +- Store-owned retain operations no longer fail while processing their own log entries.·@nicoloboschi·134207d3c +- API list and graph responses now provide typed rows instead of untyped objects.·@nicoloboschi·ef3ccdba3 +- The Python convenience client now includes the missing asynchronous methods.·@nicoloboschi·2bf10435d +- Retain completion notifications are now sent only after the store transaction succeeds.·@kyletser·511c86e10 +- Split append operations now retain the complete document body rather than only its final portion.·@nicoloboschi·fda969777 +- Recall search traces now report the query timestamp supplied by the caller.·@nicoloboschi·e366ff407 +- Vertex single-content embedding models now receive each text in a compatible request format.·@nicoloboschi·b2a7257eb +- Local ML ARM64 images now run on ARMv8.0 CPUs.·@nicoloboschi·aeaf4b1bd +- Search now handles future-dated memories correctly when calculating recency.·@ebarkhordar·1ca8c4d60 +- Gemini embedding requests now send each input in the required separate content format.·@Sanderhoff-alt·a08ec8ddf +- Bank-scoped read requests now return 404 when the requested bank does not exist.·@nicoloboschi·66992496f +- Embedding inputs are now limited to each model's supported context length, including prefixes.·@nicoloboschi·a3c5b350d +- Make consolidation reliably identify deletion targets and account for discarded batch responses.·@nicoloboschi·05c775c41 +- Serve concurrent cross-event-loop requests fairly in arrival order.·@ebarkhordar·310c9f694 +- Apply configured token limits to all reflection synthesis paths.·@nicoloboschi·d39170010 +- Prevent importing observations that reference missing source units.·@nicoloboschi·4a447c061 +- Honor configured concurrency limits when starting the server from environment settings.·@Sanderhoff-alt·f19c424e0 +- Allow cancellation of operations while they are still running.·@nicoloboschi·b1de1b941 +- Correctly recognize requests sent to OpenAI-compatible LLM providers.·@SharkyRawr·5092f3e11 +- Sanitize complete retain items consistently when they enter the memory engine.·@nicoloboschi·bd607ab8b +- Interpret “last weekend” on a weekend as the preceding weekend.·@NgoQuocViet2001·cef202e6b +- Prevent the CLI from aborting when its output pipe is closed early.·@2anoubis·a72f9de15 +- Add bounded retries for all remote reranker requests.·@nicoloboschi·d69abe7b9 +- Improve reliability of TEI embedding requests when used across threads.·@nicoloboschi·e7987ba69 +- Apply the shared retry policy to Cohere and ZeroEntropy embedding providers.·@nicoloboschi·c1f70087f +- Apply the shared retry policy to Gemini embedding requests.·@nicoloboschi·36f4a061f +- Make split reflection synthesis map calls deterministic by using zero temperature.·@nicoloboschi·98e565d3f +- Allow Bedrock inference profile ARNs when using the LiteLLM embedding provider.·@nicoloboschi·7a52b0a6b +- Preserve per-memory entity labels when re-retaining documents updates their tags.·@nicoloboschi·576cf40a2 +- Install PostgreSQL extensions in the public schema for compatible API deployments.·@nicoloboschi·901b5c696 +- Forward all HINDSIGHT_* environment settings to the embed daemon.·@nicoloboschi·7eb0fcc61 +- Run tenant-provisioning migrations through the proper isolation boundary.·@nicoloboschi·216272814 +- Show server response details when CLI API requests fail.·@nicoloboschi·737e5bf42 +- Return complete paginated results from observation-scope and webhook API endpoints.·@nicoloboschi·d20893a83 +- Score fuzzy recall tag matches using tag values rather than namespaced tag names.·@nicoloboschi·00caa8eab +- Client wrappers now apply bank configuration consistently with the server.·@nicoloboschi·e8518c392 +- Creating a mental model with an existing ID now returns a clear conflict response.·@kubaodias·1a8a1f50e +- Stopping the service no longer triggers a false event-loop stall alert.·@nicoloboschi·f0cf699cc +- Local models now safely handle misaligned model weights and fail clearly when they cannot be loaded.·@nicoloboschi·4e952b4e5 +- Re-retaining a document correctly invalidates observations when its scope changes.·@nicoloboschi·7be0f88ef +- LLM timeout diagnostics now identify the request phase in which a stalled call failed.·@nicoloboschi·3531e82f9 +- Reflection runs now fail when retrieval tools fail and record refreshes refused by the model.·@nicoloboschi·921ae824a +- Prioritized recall results are ranked correctly regardless of score scale.·@nicoloboschi·aee42254a +- Reflections now produce the required document sections field.·@nicoloboschi·9bfad10a4 +- Worker capacity is rotated across banks so bulk ingestion no longer starves other banks.·@nicoloboschi·b75e94191 +- Mental-model reflection skips empty scopes instead of running with nothing to process.·@nicoloboschi·c507e70e3 +- Retain extraction no longer derives its narrator from a bank's display name.·@nicoloboschi·aabbe8b22 +- Template imports now make authorization-safe write decisions using current bank state.·@Sanderhoff-alt·17fbc4f14 +- Strict structured-output schemas now work when referenced schema fields include sibling constraints.·@nicoloboschi·db87a7467 +- Consolidation correctly resolves observation scopes before batching tag updates.·@ferrastas·eff3546ec +- Legacy bank mutations now authorize and provision banks before making changes.·@Sanderhoff-alt·24adcfce7 +- Knowledge search now returns matches for any query term instead of requiring every term.·@Sanderhoff-alt·a373ffab6 +- Editing a memory now updates its vector embedding together with its fields.·@nicoloboschi·31db3983a +- Grafana monitoring dashboards can now be imported into older Grafana versions.·@oldnicke·7083fd790 +- Bank configuration changes are read fresh for the affected bank instead of serving stale cached values.·@nicoloboschi·689c9b694 +- Reprocess requests now re-extract document content instead of being ignored.·@nicoloboschi·9a4be5e05 +- Embedding execution limits concurrent ONNX and import operations to prevent overload.·@nicoloboschi·7051c6e3b +- Every LLM provider now enforces a per-request deadline.·@nicoloboschi·7729396e1 +- Keyword minimum-score filters are honored across all text-search backends.·@nicoloboschi·78d46a718 +- Recall scores coarse dates by their full period rather than only the period's first day.·@nicoloboschi·6a796f9f5 +- LiteLLM embedding startup is retried reliably, with configurable embedding dimensions available.·@Sanderhoff-alt·dd60a542e +- Avoid unnecessary retagging work when a document tag update makes no actual change.·@nicoloboschi·43f545e9b +- Repair malformed JSON handling for OpenAI-compatible LLM providers.·@nicoloboschi·98e29817a +- Correctly send consecutive tool results to Anthropic models.·@zlguo1996·169c6ef58 +- Ensure the complete mental-model document is embedded whenever it is updated.·@nicoloboschi·fd2e0c61f +- Allow reflection delta updates to add sections containing explicit IDs.·@nicoloboschi·6b8dfe74f +- Accurately determine visible output tokens for OpenAI-compatible providers.·@nicoloboschi·72c9c8f9e +- Handle embedding profiles and log files consistently as UTF-8 text.·@koriyoshi2041·ff29e5ad7 +- Apply all writes from a consolidation response atomically to prevent partial updates.·@nicoloboschi·ca38687cd +- Report truncated non-streaming LLM completions as output-too-long errors.·@ebarkhordar·7ef98859e +- Only use occurred-time constraints when the selected backend supports them.·@nickanderson·5c78b8132 +- Preserve trailing base64 padding when reading API keys from CLI config files.·@2anoubis·ddf57744c +- Require the appropriate bank-read permission before exposing mental-model history.·@Sword-Saint69·ad2ffa499 +- Preserve complete retain items so reprocessing faithfully replays the original input.·@shauneccles·7b6177b46 +- Apply configured LLM temperature settings to reflection requests.·@koriyoshi2041·7874561b0 +- Read embedding profile configuration and Control Center environment files as UTF-8.·@koriyoshi2041·3e8226d1a +- Preserve intended HTTP status codes when webhook validation fails.·@r266-tech·8743b4594 +- Restore database identity sequences after an administrative restore.·@r266-tech·e82f670f7 +- Apply knowledge-base node updates atomically to prevent partial changes.·@nicoloboschi·29901d43b +- Sanitize model-generated text consistently at all LLM boundaries.·@BrianMcBrayer·aab937e5f +- Use the selected output language without retaining conflicting source-language instructions.·@feniix·ba0be9b30 +- Update only the mental-model trigger settings explicitly supplied by SDK callers.·@nicoloboschi·55dafc27b +- Stop stalled consolidation tasks after they exceed an idle wall-clock limit.·@Sanderhoff-alt·698620786 +- Accept reflection delta responses provided as a top-level operation array.·@ebarkhordar·7fe6a5e79 +- Honor configured LLM request timeouts when using the native Ollama provider.·@MasterST1337·6e0f044f2 +- Detect worker tasks that are stuck without making stage progress.·@BrianHotopp·532aa8698 +- Add bounded retries, backoff, and timeouts for LiteLLM embedding providers.·@icculp·906dbcdbd +- Retry failed Text Embeddings Inference embedding requests.·@oldnicke·8916986f5 +- Bound retain link-processing work to prevent oversized deltas from exhausting worker memory.·@nicoloboschi·8833a7518 +- Honor OpenAI rate-limit reset headers when retrying rate-limited requests.·@romanbsd·20e66093d +- Remap mental-model evidence references correctly during document transfers.·@nicoloboschi·c143de2a9 +- Fixed Knowledge view links so page and mental-model IDs are scoped to the active memory bank.·@nicoloboschi·3a399343b +- Fixed PostgreSQL text-search scoring so each search result receives its own relevance score.·@mameikagou·23c63e173 +- Fixed retention of oversized items by rebuilding sub-batches from the original content span.·@nicoloboschi·d258f7b82 + +**Database Migrations** + +- `e2f4a6c8b0d1` — Add attachments and document_attachments (inline retain attachments).·attachmentsmedium document_attachmentsmedium·#4077 + ## [0.9.2](https://github.com/vectorize-io/hindsight/releases/tag/v0.9.2) **Features**