mirror of
https://github.com/vectorize-io/hindsight.git
synced 2026-09-14 19:31:49 +08:00
feat/coding-agents-usage-stats
25 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bce43b8e14 |
perf(tokenizer): move token counting from quicktok to toktok-rs (#4022)
Swaps `quicktok-v1` for `toktok-rs` (vectorize-io/toktok), a Rust BPE tokenizer whose ids are byte-identical to tiktoken's, then collapses the module's interface onto the two operations the engine actually performs. The swap itself is behaviour-neutral: compared side by side over 258 texts (~250 real files from `hindsight_api/` plus the special-token and mixed-script edge cases), quicktok and toktok produce identical counts AND identical ids on all three shared encodings. No token budget, chunk boundary or truncation point moves. Interface. `_SafeEncoding` existed to force `disallowed_special=()` onto `encode()`. Every caller of the object it returned was doing either a plain `count` or `decode(encode(x)[:n])` open-coded — which is `truncate_to_tokens`. So the module's whole public surface is now `count_tokens`, `truncate_to_tokens` / `truncate_many_to_tokens`, and `BUNDLED_ENCODINGS`; the tokenizer itself is private. That keeps #1883 fixed by construction rather than by convention: every route to the raising `encode()` went through the accessor that is now `_load_encoding`. Character-boundary truncation (toktok 0.1.3). Truncation was `decode(encode(text)[:n])`, cutting on a *token* boundary. Byte-level BPE splits one character across several tokens (under o200k_base "🧠" is three), so a cut could land mid-character and decode to U+FFFD: truncate_to_tokens("hello 🧠", 2).text -> 'hello �' (before) -> 'hello ' (now) The native call also never builds ids, never decodes, and returns the original string object untouched when nothing needs cutting. `batch_truncate` replaces the Python loop in the two callers that truncate a whole list: every reranker document (both LiteLLM cross-encoders) and every embedding input. Two user-visible consequences: * `llama3` and `qwen3` are gone — quicktok bundled five vocabularies, toktok bundles three. `HINDSIGHT_API_TOKENIZER_ENCODING=llama3` now fails at the first token count with the existing "Unknown tokenizer encoding" ValueError. Docs and both env templates updated, and `BUNDLED_ENCODINGS` (which had been lying about those two) now has a test that loads every name it advertises. * A negative budget used to slice a list with a negative index, silently dropping tokens off the end; the native call would raise. It clamps to 0. Wheels: cp311-abi3 covers 3.11-3.14, so 3.14 no longer compiles from source the way quicktok did. No musllinux wheels, which is irrelevant to the shipped images (all Python stages are glibc python:3.11-slim). numpy drops to an optional extra, so the tokenizer pulls in no dependency of its own. Measured on this repo's text: 2-7x faster than tiktoken on cl100k_base, 10-16x on o200k_base; counting an 81k-token document peaks at 1 KiB vs ~3 MB. |
||
|
|
a6f99c995c |
feat(helm): add Prometheus operator ServiceMonitor support (#3847)
* feat(helm): add Prometheus operator ServiceMonitor support The api (port 8888) and worker (port 8889) containers expose Prometheus format metrics at /metrics (verified against app source); the chart had no wiring for them — the worker's scrape annotations are gated behind the unrelated podAnnotations value and the api service had nothing. Add a gated metrics.serviceMonitor block that emits per-component ServiceMonitor resources (api always when enabled, worker only when worker.enabled). Selection labels are configurable for the Prometheus operator's serviceMonitorSelector (e.g. release: kube-prometheus-stack). Also scrape the dedicated worker in the dev LGTM compose stack, which previously only scraped the api on :8888. Verified end-to-end on k3d + kube-prometheus-stack: all three targets (api + 2 worker pods via headless endpoints) discovered and up=1. * fix(helm): address ServiceMonitor review |
||
|
|
9fcb7ca7ac |
perf(tokenizer): replace tiktoken with quicktok and default to o200k_base (#3788)
Token counting is on the hot path of both retain and recall. Recall counts once
per candidate fact, per candidate chunk, per source fact and per reranker
document; retain counts whole documents. All of it went through
`len(encoding.encode(text))` — which builds a full Python list of ids only to
take its length.
Measured on this repo's own text with the microbenchmark added here, against
tiktoken 0.12.0 on a 14-core M-series, both on o200k_base:
workload tiktoken quicktok speedup peak alloc
200 ranked facts 4.39 ms 0.75 ms 5.8x 3 KiB -> 1 KiB
500 source facts 6.92 ms 1.12 ms 6.2x 2 KiB -> 1 KiB
50 candidate chunks 12.02 ms 1.57 ms 7.7x 21 KiB -> 1 KiB
100 reranker documents 10.84 ms 1.55 ms 7.0x 12 KiB -> 1 KiB
one 77k-token document 36.08 ms 3.00 ms 12.0x 2.8 MB -> 1 KiB
Summed across the four counting stages one recall runs: 34.2 ms -> 5.0 ms.
Three things make this worth a dependency change rather than a micro-opt:
* `count()` returns an int without materialising the ids, so counting a large
document allocates nothing. tiktoken has no count-only API — `encode_to_numpy`
reaches the same 1 KiB but none of the speed, and is measured here too.
* ids are byte-identical to tiktoken's; the benchmark asserts that on adversarial
inputs before it times anything.
* the vocabularies ship inside the wheel, so nothing is downloaded at runtime.
That removes the tiktoken pre-download from the Docker build (both stages) and
from scripts/dev/setup.sh — air-gapped deployments no longer need it baked in.
The dependency risk is maintenance, not correctness, so it is contained:
engine/token_encoding.py is the only module that imports quicktok, every call
site routes through get_token_encoding() / count_tokens(), and its one
dependency (numpy) was already in the tree. Replacing it means rewriting that
file and nothing else. This also removes the last direct tokenizer import that
had escaped the seam (`__import__("tiktoken")` in reflect/prompts.py).
Removes #3756's workaround. count_tokens_windowed existed to bound the memory of
counting a large retain body, encoding a megabyte at a time and accepting an
approximate answer because a fixed character cut can split a token. count()
allocates nothing at any size AND is exact, so the windowing, its helpers and its
six call sites are gone — those callers now get an exact count. The test file
keeps the property that made #3756 worth fixing (allocation does not track the
input), now asserted against count_tokens itself.
Default encoding moves to o200k_base, selectable with
HINDSIGHT_API_TOKENIZER_ENCODING (server-level: budgets are only comparable
between banks if they are all counted the same way). o200k_base is what current
OpenAI models tokenize with. On English and code it counts within a fraction of
a percent of cl100k_base, but on non-Latin scripts it is far closer to what a
model actually charges — a mixed-script line with emoji is 19 tokens under
cl100k_base and 13 under o200k_base. Since these counts back budgets that stand
in for a context window, the closer vocabulary is the more honest one. Set
cl100k_base to reproduce the previous counts exactly.
Call sites that only need a number now call count(); the ones that need ids
(query truncation, chunk truncation, reranker truncation, prompt fitting) still
encode, but only after a count shows the text does not fit. The chunk-budget loop
also stopped encoding each oversized chunk twice.
|
||
|
|
a4650f2da5 |
chore(dev): one-shot dev setup script + fix control-plane production build (#1910)
* fix(control-plane): force NODE_ENV=production for production build A globally-exported NODE_ENV=development (common in dev shells) overrides Next.js's production default during `next build`, bundling React's development build under the production server renderer. Static prerendering then crashes with "Cannot read properties of null (reading 'useContext')" — even on the built-in _global-error page. Pin NODE_ENV=production for the build step so it is robust regardless of the caller's shell. Docker is unaffected (it invokes next build directly in a clean env). * chore(dev): add one-shot dev environment setup script Add scripts/dev/setup.sh: an idempotent bootstrap that installs the required toolchains (uv/Python, Node/npm, Rust/cargo) when missing, creates .env, configures git hooks, installs all Python + Node workspace deps, pre-downloads the local ML models + tokenizer for offline use, and builds the TypeScript SDK and Rust CLI. Flags: --skip-build, --skip-models, --with-docs, --force. Document it in CONTRIBUTING.md as the recommended setup, keeping the manual steps as a fallback. |
||
|
|
d39a2ca618 |
feat(oracle): unify migrations under Alembic with dialect dispatcher (#1330)
* feat(oracle): unify migrations under Alembic with dialect dispatcher
Oracle DDL was a 636-line idempotent file (`migrations_oracle.py`) outside
Alembic, which meant no version tracking, no per-tenant version table, and
schema drift every time a PG migration was added without a corresponding
Oracle change. This unifies both backends behind a single Alembic tree.
- New `alembic/_dialect.py::run_for_dialect(pg=, oracle=)` helper. Each
migration declares `_pg_upgrade` / `_oracle_upgrade` and dispatches based
on the live connection's dialect.
- `alembic/env.py` is dialect-aware: PG keeps the existing search_path /
read-write session setup; Oracle uses `ALTER SESSION SET CURRENT_SCHEMA`
and `DDL_LOCK_TIMEOUT`.
- `alembic/script.py.mako` scaffolds the new pattern by default.
- All 59 existing PG migrations refactored mechanically — bodies moved into
`_pg_upgrade` / `_pg_downgrade`, top-level dispatchers added.
- New `o1a2b3c4d5e6_oracle_baseline` migration brings a fresh Oracle 23ai
database to the current schema in one step (PG = no-op). Drops the legacy
partition-conversion / dedup / `observation_sources` backfill since those
only existed for pre-baseline Oracle installs we explicitly are not
supporting.
- `OracleBackend.run_migrations()` now goes through the unified Alembic
pipeline; `migrations.py` skips the PG-specific advisory lock + pgvector
setup when the URL is Oracle.
- `migrations_oracle.py` deleted; tests updated to use `run_migrations()`.
- New `tests/test_migration_shape.py` lint fails CI if any migration omits
`run_for_dialect` — keeps drift from re-emerging.
- CLAUDE.md updated with the new template and dialect-asymmetry guidance.
* ci: run client integration tests against Oracle on oracle-tests label
Adds test-python-client-oracle and test-typescript-client-oracle. These
mirror the existing test-python-client / test-typescript-client jobs but
spin up Oracle 23ai as a service container and point the API server at it
via HINDSIGHT_API_DATABASE_BACKEND=oracle + DATABASE_URL.
Why a new job instead of matrixing the existing one: Oracle Free's image
takes ~2min to start and is network-heavy, so we don't want to pay that
cost on every PR — only when oracle-tests is opted in via the PR label,
matching the existing test-api-oracle gate.
Why client tests, not unit tests: the unit suite already runs against
both backends via the abstraction layer. Only the client tests exercise
full HTTP round-trips with real serialized payloads, so they catch API
changes that work on PG but break on Oracle (or vice versa) in ways the
abstraction can't see.
* refactor(oracle): tighten feature requirements and dedup is_oracle_url
- Move is_oracle_url to db_url.py and import from there in env.py and
migrations.py — was duplicated in both.
- Type-annotate _configure_pg_session / _configure_oracle_session params
(Engine, Connection); ty checks pass.
- Update the Oracle baseline comment around vector + text index creation
to make the hard requirement explicit: VECTOR + CTXSYS must be
available, the migration fails hard if either is missing. The
swallow-only-ORA-00955 behavior was already correct; the previous
comment misleadingly called it "best-effort".
* chore(openclaw): apply pending prettier reformat to keep verify-generated-files green
Three formatting-only changes prettier wants to make. They've been stale
on main; CI's verify-generated-files runs lint with LINT_ALL=1 (vs the
"only changed integrations" local default), which surfaces them on every
unrelated PR. Folding them in here so this PR can land.
* fix(retain): plumb ops through handle_document_tracking
Line 312 of fact_storage.py references ``ops`` without ``handle_document_tracking``
declaring it as a parameter — straight NameError on every retain that walks
the upsert path. Bug landed on main in
|
||
|
|
576016f5dc |
feat: add @vectorize-io/hindsight-all daemon lifecycle package (#949)
* feat: add @vectorize-io/hindsight-embed daemon lifecycle package
Create a new top-level `hindsight-embed-npm/` package that owns the daemon
lifecycle for the Python `hindsight-embed` CLI: spawning via `uvx`, writing
the profile, waiting for `/health`, and shutting down. Nothing more.
Deliberately does not ship an HTTP client — `@vectorize-io/hindsight-client`
already covers retain / recall / reflect / createBank against the Hindsight
API, and the two packages compose: once `manager.start()` returns, consumers
talk to the daemon via `new HindsightClient({ baseUrl: manager.getBaseUrl() })`.
`HindsightEmbedManagerOptions.env` forwards an arbitrary `Record<string,
string>` to both the daemon process and the profile config via `--env K=V`,
and `extraProfileCreateArgs` / `extraDaemonStartArgs` escape hatches cover
any new CLI flag without waiting for a wrapper release.
Refactor `hindsight-integrations/openclaw` to consume both packages:
`HindsightEmbedManager` for daemon lifecycle in local mode, `HindsightClient`
for all HTTP memory operations. Drop the bespoke subprocess/HTTP client that
used to live in openclaw. The retain queue stays local to openclaw (it's a
client-side reliability workaround with a single consumer today — will move
to the client package or server-side when a second consumer needs it).
Wire the new package into the main release pipeline (versioned alongside
the other core packages, published from `v*` tags) and add a CI build job.
* docs: add Embedded Node.js SDK page for @vectorize-io/hindsight-embed
* refactor: rename hindsight-embed-npm to hindsight-all, restructure docs sidebar
The Node package previously named @vectorize-io/hindsight-embed was
semantically misnamed: hindsight-embed (Python) is a CLI tool, while what
this Node package actually provides is the Node equivalent of hindsight-all
— a programmatic lifecycle manager for a local Hindsight daemon. Rename to
match.
Package rename
- hindsight-embed-npm/ → hindsight-all-npm/ (git mv, history preserved)
- @vectorize-io/hindsight-embed → @vectorize-io/hindsight-all
- class HindsightEmbedManager → HindsightServer (matches Python hindsight-all)
- HindsightEmbedManagerOptions → HindsightServerOptions
- src/manager.ts → src/server.ts, src/manager.test.ts → src/server.test.ts
- openclaw (index.ts, backfill.ts, tests) and the claude-code Python port
updated to reference the new names
Docs restructure
- Split sdks/python.md: now client-only content. New sdks/hindsight-all.md
covers the programmatic hindsight-all Python package (HindsightServer and
HindsightEmbedded).
- Rename sdks/embed-npm.md → sdks/hindsight-all-npm.md with HindsightServer
examples.
- New "Installation" sidebar section, placed after Hosting, containing
Docker / Kubernetes / Bare Metal (anchor links into developer/installation)
plus Programmatic API (Python), Programmatic API (Node.js), and Daemon CLI.
- Add si-docker, si-kubernetes, si-nodedotjs, lu-hard-drive to the sidebar
ICON_MAP.
Docs dev-server fix
- docusaurus.config.ts: drop the flaky NODE_ENV sniff for including the
"Next" version. Use INCLUDE_CURRENT_VERSION exclusively. NODE_ENV was
unreliable across hot-reload paths and caused the Next version to
disappear intermittently when editing files.
- scripts/dev/start-docs.sh: export INCLUDE_CURRENT_VERSION=true so local
dev always shows Next; production builds leave it unset.
Lockfile cleanup
- package-lock.json and hindsight-integrations/openclaw/package-lock.json
had extraneous hindsight-embed-npm blocks left over from the rename.
Removed manually and verified with npm install.
* ci: fix openclaw jobs by pre-building workspace deps; regenerate docs-skill
The build-openclaw-integration and test-openclaw-integration jobs failed
with "Failed to resolve entry for package @vectorize-io/hindsight-all"
because openclaw depends on two monorepo workspaces via `file:` deps
(@vectorize-io/hindsight-client and @vectorize-io/hindsight-all) whose
`dist/` directories are gitignored and never built before openclaw's npm ci.
Both jobs now install the root workspace and build the two deps first,
mirroring the release-control-plane pattern.
Also regenerate skills/hindsight-docs/references/* via
./scripts/generate-docs-skill.sh:
- new skill pages for sdks/hindsight-all{.md,-npm.md}
- updated skill pages for sdks/embed.md and sdks/python.md to match
the new H1s and split content
- incidental refreshes to changelog/index.md, developer/models.md,
openapi.json, and uv.lock that verify-generated-files picked up
* ci: build openclaw before running tests so symlink test can realpath dist
|
||
|
|
576473b6aa |
feat: observation history tracking and diff UI (#513)
* feat: add source facts token limits to consolidation and recall
- Add two new configurable (per-bank) parameters:
- consolidation_source_facts_max_tokens: total token budget for source
facts across all observations in the consolidation prompt (-1 = unlimited)
- consolidation_source_facts_max_tokens_per_observation: per-observation
cap so each observation gets a fair share of source facts (-1 = unlimited,
default 256)
- Both are also exposed as recall API parameters via SourceFactsIncludeOptions
(max_tokens and max_tokens_per_observation)
- Consolidation now uses resolve_full_config to respect bank-level overrides
- Improve consolidation prompt: temporal metadata (occurred_start=, | Involving:)
is now clearly separated from observation text, with a concrete example showing
the expected synthesis style and explicit rules not to copy raw fact lines
- Add tests for recall source facts capping and consolidation config forwarding
- Expose all three new fields in the control plane bank config UI
- Document new env vars in configuration.md
- Regenerate OpenAPI spec and all SDK clients
* fix: reorder observations UI fields and rename Label Groups to Entity Labels
* fix: revert Entities section title (only rename inner label)
* doc: add consolidation source facts and batch size fields to memory-banks docs
* feat: add observation history tracking and UI diff view
- Track observation changes over time in a JSONB history column,
appending each update's previous state (text, tags, dates, sources)
instead of overwriting
- Add HINDSIGHT_API_ENABLE_OBSERVATION_HISTORY config flag (default: true)
to toggle history recording
- Expose history field in get_memory_unit for observations
- Fix observations/[modelId] route that was proxying to wrong endpoint
- Add History tab in observation modal and History section in panel,
showing word-level and tag diffs between each change (newest first)
- Extract shared ObservationHistoryView component used by both modal and panel
- Add --random-port flag to start.sh to run multiple dev instances
- Scope Next.js distDir by port to prevent lock file collisions between instances
- Restyle consolidation pending badge (rounded-md with border) and add
inline refresh button; fix loading flicker on data refresh
* feat: dedicated observation history endpoint with source facts diff
- Add GET /memories/{id}/history endpoint returning enriched history with
resolved source fact texts and is_new flags per change
- Deprecate history field in GET /memories/{id} (always returns empty list)
- Reconstruct cumulative source facts per history entry by working backwards
from current state, marking newly added facts with is_new
- Replace inline history panel with "View History" button opening modal
- History modal fetches from dedicated endpoint lazily on tab switch
- Timeline view now opens MemoryDetailModal instead of side panel
- History view uses prev/next navigation (left = older, right = newer)
- Fix --random-port: pass dynamic API_PORT as HINDSIGHT_CP_DATAPLANE_API_URL
to control plane, preserving caller values over .env
|
||
|
|
3d87ef5cee |
doc: update cookbook (#479)
* doc: update cookbook * fix(cookbook): preserve tag keys during sync, strip local .md links - Fix extract_tags_from_readme/notebook to return dict[str,str] preserving sdk/topic keys instead of bare values, preventing topics like "Customer Service" from being misclassified as SDK - Add strip_local_md_links() to remove relative .md references that would cause broken link errors in Docusaurus build * ci: run test-doc-examples independently without waiting for test-rust-cli Build the CLI directly in the job instead of downloading the artifact, so test-doc-examples can start at the beginning in parallel with all other jobs. |
||
|
|
9b96becc5c |
feat: entity labels — optional, free_values, multi_value, UI polish (#450)
* feat: entity labels * feat: entity labels — optional, free_values, multi_value, UI polish Completes the entity labels system: **Schema & extraction** - Dynamic Pydantic Labels model per fact: each group becomes a typed field (Literal | None, list[Literal], str | None, or list[str]) - `optional: bool` flag per group — non-optional enum fields appear in JSON schema required array so structured-output providers enforce them - `free_values: bool` flag per group — accepts any LLM-generated string instead of a predefined enum; example values shown as hints in prompt - New `is_label_entity()` helper for labels-only mode filtering that handles both enum lookup and free_values key-prefix matching - Sentinel rejection: "None"/"null"/"n/a" strings dropped in post-processing **BM25 / dense retrieval** - `text_signals` column on memory_units: entity names + date tokens for enriched BM25 indexing without polluting stored fact text - Dense embedding includes occurred_end when it differs from occurred_start - Alembic migration z1u2v3w4x5y6 (merge revision fixing two heads) **UI (bank-config-view)** - Shadcn Switch replaces custom Toggle for both entity-labels and observations - Shadcn Checkbox for multi/optional/free_values per group - Input heights bumped to h-8 throughout the editor - "Label Groups" → "Entity Labels", "Free-form entities" → "Entities" - Free-text groups show "Example hints" banner in values section **Tests (45 unit + 3 LLM integration)** - build_labels_model: single, multi, mixed, free_values optional/required/multi - is_label_entity: enum match, free_values prefix match, no false positives - Post-processing: null/absent/string-None/free_values/sentinels/multi-value - Schema: labels in required, structured object, no labels when unconfigured - LLM integration: single-value enum, multi-value enum, free_values retain **Docs** - retain.md: new Entity Labels section covering groups, flags, examples - configuration.md: retain_free_form_entities env var + entity_labels note * fix(tests): update hierarchical fields count for entity_labels additions entity_labels and retain_free_form_entities are hierarchical fields, bumping the expected count from 11 to 13. * fix(migration): rename text_signals revision to avoid collision with main Main branch claimed z1u2v3w4x5y6 for observation_scopes. Rename our text_signals migration to a2b3c4d5e6f7, chaining after z1u2v3w4x5y6. * refactor(entity-labels): simplify free_values — always str|None, no multi - free_values groups always produce str | None (multi_value and optional flags are ignored for free text groups — always optional, never multi) - Prompt section for free_values groups shows only key + description, no values list (users put examples in the description instead) - UI: section title "Entities", toggle "Free Form Entities", replace per-group checkboxes with a type dropdown (Enum / Free text); only show multi checkbox and values list when type is Enum - Update tests to reflect new behaviour * refactor(entity-labels): replace free_values/multi_value booleans with type field - LabelGroup now uses type: "value" | "multi-values" | "text" instead of free_values/multi_value boolean pair - Backward-compat migration converts legacy dicts automatically - Rename retain_free_form_entities → entities_allow_free_form throughout - Update UI dropdown to show Single value / Multi-values / Free text - Remove separate multi checkbox (captured by type selection) - Update docs examples and configuration.md - Update all tests to use new field names * fix(migration): backfill observation_scopes column for DBs with swapped z1u2v3w4x5y6 Local DBs that had z1u2v3w4x5y6 applied when it referred to the old text_signals migration (before it was renamed to a2b3c4d5e6f7) won't have observation_scopes in their memory_units table. This migration adds the column with IF NOT EXISTS so it's a no-op on clean installs. * feat(entity-labels): add tag field to auto-populate memory unit tags from labels When a LabelGroup has tag=True, extracted key:value entities for that group are automatically written to the memory unit's tags array. This lets entity labels double as tags, enabling immediate filtering via the existing tags/tags_match API params with no extra infrastructure. - Add tag: bool = False to LabelGroup - _inject_label_tags() helper called in both sync and batch extraction paths - UI: add Tag checkbox per label group row - Docs: document the new tag field - Tests: 4 new unit tests covering all tag injection paths * style: ruff format migration file * fix(migration): fix multiple alembic heads after rebase — point text_signals after nullable_event_date * fix(clients): update timestamp field to use Timestamp wrapper type after timestamp=unset feature * style: ruff format agent.py * fix(docs): update Go quickstart example to use NullableTimestamp for timestamp field |
||
|
|
2a32273226 | feat: increase customization for reflect, retain and consolidation (#419) | ||
|
|
69dec8ec34 |
feat: add otel traceability (#330)
* feat: add comprehensive OpenTelemetry tracing - Add tool execution spans for reflect operations - Add tool call information (names, params) to spans - Change verification scope from 'test' to 'verification' - Add hindsight.reflect_generation span for done() processing - Implement no-op tracer for improved code readability - Update documentation for OTEL configuration - Resolve merge conflicts from rebase * fix: properly serialize Pydantic models in span recording - Add _serialize_for_span() helper to handle Pydantic models - Update all providers to use the helper function - Fixes test failures with 'Object of type X is not JSON serializable' * feat: add Grafana LGTM stack for unified local observability Add Grafana LGTM (Loki, Grafana, Tempo, Mimir) as the recommended local development observability stack. This provides traces, metrics, and logs in a single Docker container instead of separate tools. Changes: - Add scripts/dev/grafana/ with docker-compose and README - Add scripts/dev/start-grafana.sh startup script - Update .env.example to reference Grafana LGTM - Update configuration docs to emphasize Grafana LGTM as primary option - Reorder OTLP backend list to show Grafana LGTM first Benefits: - Single container vs multiple separate tools (Jaeger, SigNoz, etc.) - ~515MB image with full observability stack - Compatible with existing OTLP configuration - Simpler local development setup * chore: remove SigNoz scripts and references Remove SigNoz observability stack in favor of Grafana LGTM as the sole recommended local development tracing solution. Changes: - Delete scripts/dev/signoz/ directory and all SigNoz configurations - Delete scripts/dev/start-signoz.sh startup script - Remove SigNoz references from .env.example - Remove SigNoz from OTLP backends list in configuration docs Grafana LGTM provides the same capabilities (traces, metrics, logs) in a simpler single-container setup. * feat: add consolidation span hierarchy for tracing Add parent-child span structure for consolidation operations: - hindsight.consolidation: Parent span for each memory being processed - hindsight.consolidation_recall: Child span for finding related observations - LLM call span: Automatically created by LLM provider (scope="consolidation") This enables detailed timing breakdown in Grafana Tempo: - Total consolidation time per memory - Time spent in recall - Time spent in LLM call - Time spent executing actions (create/update) All consolidation tests pass (31/31). * feat: add Prometheus metrics and GenAI dashboard to Grafana stack Add comprehensive metrics and dashboarding to the Grafana LGTM stack: Metrics Collection: - Configure Prometheus to scrape Hindsight API /metrics endpoint - Scrape interval: 10 seconds - Targets hindsight-api on host.docker.internal:8888 GenAI Dashboard: - Pre-configured dashboard with 6 panels: - LLM call rate (by provider/model) - LLM call duration (p50/p95 by scope) - Token usage - input tokens/sec by scope - Token usage - output tokens/sec by scope - Operations rate (retain/recall/reflect/consolidation) - Operation duration p95 by operation type Configuration: - Mount prometheus.yml for metrics scraping - Mount dashboards directory for auto-provisioning - Add host.docker.internal mapping for container->host access - Dashboard provisioning with auto-reload every 10s Documentation: - Updated README with metrics viewing instructions - Added PromQL query examples - Documented dashboard access and navigation This provides full observability: traces (Tempo) + metrics (Prometheus/Mimir) + dashboards (Grafana) * refactor: merge Grafana setup into existing monitoring stack Consolidate the separate scripts/dev/grafana/ setup into the existing scripts/dev/monitoring/ stack, using Grafana LGTM (Loki, Grafana, Tempo, Mimir). Changes: - Remove separate scripts/dev/grafana/ directory and start-grafana.sh - Rewrite scripts/dev/monitoring/start.sh to use Docker + Grafana LGTM (was: download native Prometheus/Grafana binaries) - Add docker-compose.yaml for Grafana LGTM container - Add prometheus.yml for scraping Hindsight API metrics - Mount existing dashboards from monitoring/grafana/dashboards/ - Add comprehensive README.md Benefits: - Single unified monitoring command: ./scripts/dev/start-monitoring.sh - Uses existing dashboard files (hindsight-operations, hindsight-llm, hindsight-api-service) - Simpler setup: Docker-based vs downloading/running native binaries - Full observability: traces + metrics + logs + dashboards in one container - Standard ports: Grafana on 3000, OTLP on 4317/4318 Architecture: - Grafana LGTM container (~515MB) provides all components - Dashboards auto-provisioned from monitoring/grafana/dashboards/ - Prometheus scrapes host.docker.internal:8888/metrics - Shared hindsight-network for future service-to-service tracing * fix: run monitoring stack in foreground for easy Ctrl+C stop Change docker-compose from detached (-d) to foreground mode. Users can now stop the stack with Ctrl+C instead of needing to run docker-compose down separately. * fix: remove invalid home dashboard path and obsolete version field - Remove GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH environment variable (was pointing to wrong path causing 'Failed to load home dashboard' error) - Remove obsolete 'version' field from docker-compose.yaml (docker-compose v2+ doesn't require version field) * fix: load Hindsight dashboards in Grafana LGTM Mount Hindsight dashboard JSON files and custom provisioning config to make dashboards visible in Grafana. Changes: - Mount hindsight-operations.json, hindsight-llm.json, hindsight-api-service.json to /otel-lgtm/ - Create grafana-dashboards.yaml with all dashboard providers (default + Hindsight) - Mount custom provisioning config to override LGTM default All 3 Hindsight dashboards now appear in Grafana UI with metrics from Prometheus scraping the Hindsight API /metrics endpoint. * fix: configure Prometheus to scrape Hindsight API metrics Update prometheus.yml to include both OTLP receiver config (from LGTM) and scrape_configs for pulling metrics from Hindsight API. Changes: - Mount prometheus.yml to /otel-lgtm/prometheus.yaml (where LGTM reads it) - Add scrape_configs section to pull from host.docker.internal:8888/metrics - Keep OTLP receiver configuration for trace metrics - Set scrape_interval to 5s Verified: Prometheus now successfully scrapes hindsight_llm_calls_total and other Hindsight metrics. Dashboards now show live data! * feat: add comprehensive tracing for recall and improve reflect/mental_model_refresh spans - Add recall operation tracing with parent-child span hierarchy - Parent: hindsight.recall with attributes (bank_id, query, fact_types, etc.) - Children: recall_embedding, recall_retrieval, recall_fusion, recall_rerank - Fixed context propagation using start_as_current_span() - Improve reflect tracing spans - Remove reflect_generation spans, use reflect instead - Change done() tool processing to hindsight.reflect_tool_call - Fix mental_model_refresh span nesting - Add _skip_span parameter to reflect_async to avoid duplicate hindsight.reflect spans - Mental model refresh now has clean span hierarchy without nested reflect parent - Add comprehensive tracing verification tests - Test span hierarchy and attributes for all operations - Verify parent-child relationships - 5 passing tests covering recall, reflect, consolidation, and mental_model_refresh * refactor: remove redundant is_tracing_enabled() checks - Remove all is_tracing_enabled() conditional checks before tracing calls - NoOpTracer/NoOpSpan handle disabled tracing automatically - Simplify code by always calling tracer methods directly - Fix NoOpTracer.start_as_current_span() to yield NoOpSpan instead of None Changes: - memory_engine.py: Remove 5 is_tracing_enabled checks in recall spans - agent.py: Remove 2 is_tracing_enabled checks in reflect tool spans - tracing.py: Fix NoOpTracer context manager to yield proper NoOpSpan This eliminates ~50 lines of redundant conditional code while maintaining identical behavior. * docs: simplify distributed tracing section in monitoring.md - Make tracing documentation more concise - Focus on span hierarchy and attributes - Remove verbose troubleshooting and performance sections - Keep configuration.md for env vars only |
||
|
|
4c792400c1 |
feat: new 'worker' service (#176)
* feat: new 'worker' service * doc * docs * tests |
||
|
|
7935b0accd |
fix: improve mpfp retrieval (#146)
* fix: improve mpfp retrieval * fix: improve mpfp retrieval * fix: improve embeddings service performances * fix: improve embeddings service performances * fix: improve embeddings service performances * fix: improve embeddings service performances |
||
|
|
eb2702bcba |
misc: performance improvements (#140)
* misc: performance improvements * misc: performance improvements * misc: performance improvements |
||
|
|
a6798f7e2a |
fix: improve tei client parameters (#137)
* fix: improve tei client parameters * fix: improve tei client parameters * fix: improve tei client parameters |
||
|
|
1c6acc3ba0 | feat: simplify mcp installation + ui standalone (#41) | ||
|
|
f4bc8443b3 | changelog generator | ||
|
|
fcea8afa6c |
Change npm packaging structure and fix contributing info (#16)
* change the package to workspace concept * add provider name and change default model * add the node_modules to git ignore * change the npm runs to use workspace * fix the start scripts to use the workspace * update the uv.lock * updated instructions * update the docker build to use the npm workspace * Update package-lock.json after merge to sync workspace dependencies * fix merge conflict |
||
|
|
fa554b8980 | brandind and misc fixes | ||
|
|
f42476bf94 |
fix: make sure openai provider works + docs updates (#23)
* fix: make sure openai provider works * fix: make sure openai provider works * fix |
||
|
|
943f6e7844 | control plane and api issues | ||
|
|
d099ef870d | rename to new names | ||
|
|
b1ff2e8823 | rename to hindsight (#2) | ||
|
|
49d9b4bb63 | update gh actions | ||
|
|
588065182a | polish + cli + helm + standalone |