forked from vectorize-io/hindsight
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
528 lines (474 loc) · 34.8 KB
/
Copy path.env.example
File metadata and controls
528 lines (474 loc) · 34.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
# Hindsight Environment Variables
# Copy this file to .env and fill in your values
# LLM Configuration (Required)
# Supported providers: openai, openai-responses, groq, ollama, gemini, anthropic, lmstudio, vertexai, minimax, deepseek, zai, atlas, volcano, openai-codex, claude-code, github-copilot
HINDSIGHT_API_LLM_PROVIDER=openai
HINDSIGHT_API_LLM_API_KEY=your-api-key-here
HINDSIGHT_API_LLM_MODEL=gpt-4o-mini
# HINDSIGHT_API_LLM_BASE_URL=https://api.openai.com/v1
# Reasoning effort for providers/models that support it. Examples: none, low, medium, high, xhigh.
# Set it and the value is sent as given, whatever the model is called — use `none` to stop a
# self-hosted reasoning model (vLLM, Ollama, llama.cpp, TGI) emitting thinking blocks. Unset,
# no reasoning parameter is sent at all and each model runs at its own default effort.
# HINDSIGHT_API_LLM_REASONING_EFFORT=low
# Sampling temperature for internal LLM calls. Set a number in [0.0, 2.0], or `none`
# to omit the temperature parameter entirely (required for models that reject explicit
# temperatures, e.g. Azure gpt-5.5). The global override below applies to every operation;
# per-operation overrides (defaults: verification=0.0, retain=0.1, reflect=0.9,
# consolidation=0.0) take precedence.
# HINDSIGHT_API_LLM_TEMPERATURE=none
# HINDSIGHT_API_LLM_TEMPERATURE_VERIFICATION=0.0
# HINDSIGHT_API_LLM_TEMPERATURE_RETAIN=0.1
# HINDSIGHT_API_LLM_TEMPERATURE_REFLECT=0.9
# HINDSIGHT_API_LLM_TEMPERATURE_CONSOLIDATION=0.0
# Grammar-enforce structured output (json_schema strict) instead of the soft
# schema-in-prompt path. Helps weaker self-hosted models that emit prose preambles
# or invalid JSON. The global override below applies to every operation;
# per-operation overrides take precedence, in both directions -- set one to false
# to opt that operation out while the global flag is on.
# HINDSIGHT_API_LLM_STRICT_SCHEMA=false
# HINDSIGHT_API_LLM_STRICT_SCHEMA_RETAIN=true
# HINDSIGHT_API_LLM_STRICT_SCHEMA_REFLECT=true
# HINDSIGHT_API_LLM_STRICT_SCHEMA_CONSOLIDATION=true
# Ceiling on the connect phase (TCP + TLS handshake) of an LLM request, in seconds.
# Read, write and pool keep the full HINDSIGHT_API_LLM_TIMEOUT budget, so an unreachable
# endpoint fails fast instead of burning the whole request timeout. 0 disables the cap.
# HINDSIGHT_API_LLM_CONNECT_TIMEOUT=10
# Log level for the httpx/httpcore loggers. DEBUG traces every LLM request through its
# transport phases (connect_tcp, send_request_headers, receive_response_headers), which
# is how you tell a request that stalled before being sent from one never answered.
# HINDSIGHT_API_LLM_HTTP_LOG_LEVEL=WARNING
# Some backends, including Bedrock Converse, reject JSON Schema maxItems.
# Disable it only for those backends; consolidation still enforces the cap.
# HINDSIGHT_API_LLM_SUPPORTS_MAX_ITEMS=true
# Constrain retain's occurred_start/occurred_end to an ISO timestamp with a JSON
# Schema pattern. Stops grammar-constrained models reasoning inside the timestamp
# string and burning the whole completion budget. Off by default: backends that
# validate schemas against an allowlist (Bedrock) reject the keyword with a 400.
# HINDSIGHT_API_LLM_SUPPORTS_STRING_PATTERN=false
# Pin a conversation to one backend prompt cache (OpenAI-compatible providers only).
# Server-side prompt caches are per backend server, so the same conversation has to
# reach the same one to hit. Values: auto (default), xai_conv_id (sends xAI's
# x-grok-conv-id header), openai_prompt_cache_key (sends OpenAI's prompt_cache_key
# field), none (sends nothing). "auto" picks from the base URL host and is an
# allowlist: x.ai / grok.com get the header, native OpenAI / openai.com / Azure
# OpenAI get the field, and every other backend gets nothing. Per-operation
# overrides take precedence. Set to none to disable entirely.
# HINDSIGHT_API_LLM_CACHE_AFFINITY=auto
# HINDSIGHT_API_RETAIN_LLM_CACHE_AFFINITY=none
# HINDSIGHT_API_REFLECT_LLM_CACHE_AFFINITY=xai_conv_id
# HINDSIGHT_API_CONSOLIDATION_LLM_CACHE_AFFINITY=none
# Ask litellm/litellmrouter/bedrock for structured output via a forced tool call
# instead of response_format. Enable it for backends that reject response_format
# outright -- e.g. Bedrock Claude in ap-southeast-2 ("Extra inputs are not permitted");
# the same model in us-east-1 accepts response_format and needs nothing here.
# HINDSIGHT_API_LLM_STRUCTURED_OUTPUT_FORCED_TOOL=false
# Transport-level output cap for reflect's final synthesis call. Unset = uncapped:
# the model runs to a natural stop and the reflect/mental-model max_tokens governs
# visible length via a prompt directive + a post-hoc rewrite (not by truncating the
# provider call, which on thinking models is eaten by reasoning tokens and cuts pages
# off mid-word). Set an integer only to enforce a hard cost ceiling on the call.
# HINDSIGHT_API_REFLECT_MAX_COMPLETION_TOKENS=16000
# Diagnostic: on any LLM 4xx, log the exact assembled request ([LLM_4XX_DUMP]) --
# serialized request config (message bodies stripped) + capped per-message previews.
# For debugging otherwise-unreproducible rejected calls. Off by default.
# HINDSIGHT_API_LLM_DEBUG_DUMP_4XX=false
# Example: Anthropic Claude configuration
# HINDSIGHT_API_LLM_PROVIDER=anthropic
# HINDSIGHT_API_LLM_API_KEY=your-anthropic-api-key
# HINDSIGHT_API_LLM_MODEL=claude-sonnet-4-20250514
# Example: GitHub Copilot subscription via the official Copilot SDK
# Sign in with Copilot CLI first; no HINDSIGHT_API_LLM_API_KEY is needed.
# HINDSIGHT_API_LLM_PROVIDER=github-copilot
# HINDSIGHT_API_LLM_MODEL=gpt-5.6-terra
# Example: Google Vertex AI configuration
# HINDSIGHT_API_LLM_PROVIDER=vertexai
# HINDSIGHT_API_LLM_MODEL=google/gemini-2.0-flash-001
# HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID=your-gcp-project-id
# HINDSIGHT_API_LLM_VERTEXAI_REGION=us-central1
# HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY=/path/to/service-account-key.json # Optional, uses ADC if not set
# Example: MiniMax configuration (1M context window)
# HINDSIGHT_API_LLM_PROVIDER=minimax
# HINDSIGHT_API_LLM_API_KEY=your-minimax-api-key
# HINDSIGHT_API_LLM_MODEL=MiniMax-M3 # or MiniMax-M2.7 for the previous generation
# Example: OpenAI Responses API (/v1/responses) — reasoning + function tools together
# HINDSIGHT_API_LLM_PROVIDER=openai-responses
# HINDSIGHT_API_LLM_API_KEY=your-openai-api-key
# HINDSIGHT_API_LLM_MODEL=gpt-5.6 # reasoning model (gpt-5.x / o-series); e.g. gpt-5.6-terra
# HINDSIGHT_API_LLM_REASONING_EFFORT=high # sent alongside tools, unlike chat/completions
# Example: DeepSeek configuration (https://api.deepseek.com)
# HINDSIGHT_API_LLM_PROVIDER=deepseek
# HINDSIGHT_API_LLM_API_KEY=your-deepseek-api-key
# HINDSIGHT_API_LLM_MODEL=deepseek-v4-flash # or deepseek-v4-pro / deepseek-chat / deepseek-reasoner
# Example: z.ai configuration (Zhipu GLM series, https://z.ai)
# HINDSIGHT_API_LLM_PROVIDER=zai
# HINDSIGHT_API_LLM_API_KEY=your-zai-api-key
# HINDSIGHT_API_LLM_MODEL=glm-4.5-flash # or glm-4.5-air for the paid tier
# Example: Atlas Cloud configuration (OpenAI-compatible, https://www.atlascloud.ai)
# HINDSIGHT_API_LLM_PROVIDER=atlas
# HINDSIGHT_API_LLM_API_KEY=your-atlascloud-api-key
# HINDSIGHT_API_LLM_MODEL=deepseek-ai/deepseek-v4-pro # reasoning model; also Qwen / GLM / Kimi / MiniMax, etc.
# Example: LM Studio local configuration (Qwen 2.5 32B recommended)
# HINDSIGHT_API_LLM_PROVIDER=lmstudio
# HINDSIGHT_API_LLM_API_KEY=lmstudio
# HINDSIGHT_API_LLM_BASE_URL=http://localhost:1234/v1
# HINDSIGHT_API_LLM_MODEL=qwen2.5-32b-instruct
# Example: Ollama local configuration (native provider)
# HINDSIGHT_API_LLM_PROVIDER=ollama
# HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1
# HINDSIGHT_API_LLM_MODEL=gemma3:12b
# Native Ollama context-window override (num_ctx). Leave unset to let Ollama use
# the model Modelfile / server default; set a positive integer only to force a
# specific context size (e.g. 16384 to keep the previous request behavior).
# Setting it also routes free-form calls through Ollama's native /api/chat API,
# the only endpoint that can carry a context size.
# HINDSIGHT_API_LLM_OLLAMA_NUM_CTX=16384
# Example: OpenAI Codex (ChatGPT Plus/Pro OAuth, no API key)
# HINDSIGHT_API_LLM_PROVIDER=openai-codex
# Credentials directory for this provider (the dir holding auth.json). Overrides the
# process-wide CODEX_HOME. Set it, plus the indexed HINDSIGHT_API_LLM_<n>_CODEX_HOME
# below, to run two independently authorized ChatGPT profiles in one process.
# HINDSIGHT_API_LLM_CODEX_HOME=/var/lib/hindsight/codex-a
# Multi-LLM strategies: configure extra LLMs by index alongside the primary above,
# then pick a routing strategy. Unset = single primary LLM (default). Members are
# numbered from 1; indices must be contiguous. Each operation can override with a
# RETAIN_/REFLECT_/CONSOLIDATION_ prefix (e.g. HINDSIGHT_API_RETAIN_LLM_1_PROVIDER).
# HINDSIGHT_API_LLM_1_PROVIDER=groq
# HINDSIGHT_API_LLM_1_API_KEY=your-groq-api-key
# HINDSIGHT_API_LLM_1_MODEL=openai/gpt-oss-120b
# HINDSIGHT_API_LLM_2_PROVIDER=anthropic
# HINDSIGHT_API_LLM_2_API_KEY=your-anthropic-api-key
# An openai-codex member can point at its own credentials directory, so a chain of
# two Codex members fails over between two ChatGPT accounts instead of retrying one.
# HINDSIGHT_API_LLM_1_PROVIDER=openai-codex
# HINDSIGHT_API_LLM_1_CODEX_HOME=/var/lib/hindsight/codex-b
# Strategy JSON: {"mode": "failover"} or {"mode": "round-robin"}.
# Round-robin accepts optional positive-int "weights" (one per member, primary first).
# HINDSIGHT_API_LLM_STRATEGY={"mode": "failover"}
# API Configuration (Optional)
HINDSIGHT_API_HOST=0.0.0.0
HINDSIGHT_API_PORT=8888
HINDSIGHT_API_LOG_LEVEL=info
# Vocabulary used for every token count and chunk boundary (recall budgets, chunk
# sizes, prompt fitting, embedding truncation). o200k_base matches current OpenAI
# models; set cl100k_base to reproduce the counts Hindsight produced before this
# default changed. Also bundled: o200k_harmony.
# HINDSIGHT_API_TOKENIZER_ENCODING=o200k_base
# Optional retain chunking override for structured logs/transcripts.
# Unset uses HINDSIGHT_API_RETAIN_CHUNK_SIZE as the structured-chunk limit.
# HINDSIGHT_API_RETAIN_STRUCTURED_CHUNK_SIZE=
# When true, a retain operation that hit any fact-extraction errors is marked
# 'failed' (not 'completed'), surfacing silently-dropped facts. Default false.
# HINDSIGHT_API_FAIL_ON_EXTRACTION_ERRORS=false
# Wall-clock ceiling (seconds) for one retain task in the worker. A retain that
# blocks indefinitely is cancelled and marked 'failed' — and so becomes
# retryable — instead of holding its worker slot until the process restarts.
# Set well above your slowest healthy retain; 0 disables. Default 3600.
# HINDSIGHT_API_RETAIN_WALL_TIMEOUT=3600
# Ceiling (seconds) on how long one consolidation task in the worker may run
# WITHOUT making progress. Every batch that commits restarts the clock, so a large
# backlog is never cut short — only a stalled job is. A stalled consolidation is
# cancelled and marked 'failed' — and so becomes retryable, and the reconcile sweep
# can re-schedule the bank — instead of holding its reserved worker slot until the
# process restarts. 0 disables. Default 7200.
# HINDSIGHT_API_CONSOLIDATION_WALL_TIMEOUT=7200
# Megabytes of extracted-but-unwritten state ONE retain operation may hold. The chunk
# batch size bounds how many chunks are in flight, not what they weigh, so this is the
# figure to size a worker against: peak per retain is roughly this, whatever the document.
# Budget for HINDSIGHT_API_WORKER_MAX_SLOTS concurrent retains. Over budget, extraction
# waits for the write path instead of growing. 0 disables. Default 128.
# HINDSIGHT_API_RETAIN_MEMORY_BUDGET_MB=128
# Dry-run extraction preview endpoint (POST /memories/dry-run-extract). Enabled by default; it makes
# a real LLM call but stores nothing. Set to false to remove the endpoint (returns 404).
# HINDSIGHT_API_ENABLE_DRY_RUN_EXTRACT=true
# Base Path / Reverse Proxy Support (Optional)
# Set these when deploying behind a reverse proxy with path-based routing
# Example: To deploy at example.com/hindsight/, set both to "/hindsight"
# HINDSIGHT_API_BASE_PATH=/hindsight
# NEXT_PUBLIC_BASE_PATH=/hindsight
# Database (Optional - uses embedded pg0 by default)
# HINDSIGHT_API_DATABASE_URL=postgresql://user:pass@host:5432/db
# HINDSIGHT_API_READ_DATABASE_URL= # Optional read-replica URL. When set, recall queries (semantic, BM25, graph, temporal) flow through a separate pool against this URL, offloading the primary. Typically points to a read-only endpoint (CNPG's <cluster>-ro service or Aurora reader endpoint).
# HINDSIGHT_API_MIGRATION_DATABASE_URL= # Direct PostgreSQL URL for migrations (bypasses PgBouncer). Falls back to DATABASE_URL.
# HINDSIGHT_API_DATABASE_SCHEMA=public # PostgreSQL schema name (default: public)
# HINDSIGHT_API_DB_MAX_PARALLEL_WORKERS_PER_GATHER= # Optional cap on Postgres planner parallelism for this process's pool connections. Unset leaves the server default; 0 makes background/bulk queries run serially (useful on worker processes sharing a primary with latency-sensitive traffic).
# HINDSIGHT_API_DB_SESSION_SETUP_ON_ACQUIRE=true # Re-apply the per-connection session settings (statement_timeout, planner parallelism, trigram threshold, vector-search tuning, and the vchord search path) every time a connection is taken from the pool, not just when it is opened. Releasing a connection resets it to the server defaults, so turn this off only when the same settings are pinned on the role/database (ALTER ROLE ... SET) — then it is a pure round trip per acquire, worth reclaiming behind a transaction-mode pooler. On the vchord text-search backend the search path is in that set and losing it fails recall outright, so pin it too. application_name is always re-applied regardless.
# HINDSIGHT_API_ENTITY_TRGM_SIMILARITY_THRESHOLD=0.15 # Postgres pg_trgm.similarity_threshold applied on every pool connection, used by entity resolution's % trigram match. Must be in (0, 1]. Lower catches more substring-ish matches at higher CPU cost on large entity sets; higher is stricter and cheaper.
# HINDSIGHT_API_ENTITY_INTRABATCH_MERGE_SIMILARITY=0.5 # Trigram similarity (pg_trgm-equivalent, computed in-memory) at/above which two new names created by the SAME retain are merged into one entity (in-batch dedup of surface-form variants). Must be in (0, 1]. A merge cutoff, stricter than the recall threshold above; raise toward 1.0 to merge only near-identical forms.
# HINDSIGHT_API_ENTITY_MERGE_MIN_SIMILARITY=0.3 # Minimum trigram similarity a name must have with an EXISTING entity before that entity can be reused for it, whatever the other resolution signals (co-occurrence, recency) say. Must be in (0, 1]. Sits between the recall threshold (0.15) and the in-batch cutoff (0.5). Lower it for corpora of very short names; raise it to merge only clear surface variants.
# HINDSIGHT_API_RETAIN_ENTITY_RESOLUTION_MAX_CANDIDATES=200 # Max candidates scored per entity mention during retain. The fuzzy lookup keeps only this many best matches per name (ranked by trigram/Jaro-Winkler similarity) before scoring them one by one. On banks holding thousands of near-identical names an uncapped set turns one retain into minutes of CPU that stall the worker's health checks. Raise only if entities that should merge are being duplicated.
# HINDSIGHT_API_EXTERNALLY_OWNED_ROUTINES= # Comma-separated maintenance discovery routines this deployment installs itself, e.g. mental_models_with_cron,banks_needing_consolidation,schemas_with_expired_rows,schemas_with_expired_operations. Migrations skip anything named here and leave your CREATE OR REPLACE in place; without it the next migration that reinstalls the routine silently overwrites it. Empty (the default) installs every routine as usual. Naming a routine you have not installed leaves it missing, and the maintenance loop then fails loudly on it.
# HINDSIGHT_API_MIGRATION_CONCURRENCY=1 # Tenant schemas to migrate concurrently (PG only, each in its own process; per-schema work stays sequential). Each worker has ~1-2s startup cost + uses ~3 DB connections, so it only pays off with many schemas (tens+) or slow migrations; keep concurrency*3 <= spare max_connections. Default: 1 (sequential).
# HINDSIGHT_API_MIGRATION_ISOLATION=auto # Run migrations in a subprocess instead of in the calling process: auto | true | false. auto isolates only on a free-threaded interpreter, where alembic's psycopg2 would otherwise re-enable the GIL for the life of the server process. Default: auto.
# HINDSIGHT_API_OPERATION_RETENTION_DAYS=30 # Prune terminal operation rows, payloads, and metadata after this many days; 0 (the default) keeps them forever.
# HINDSIGHT_API_OPERATION_CLEANUP_BATCH_SIZE=1000 # Maximum expired terminal rows deleted per tenant schema in each cleanup cycle; must be positive.
# Background maintenance cadences (Optional)
# Each sweep begins with one cross-tenant discovery call that probes every schema holding the relevant
# table, in every API/worker process — so its cost scales with tenant count while the work it finds does
# not. On deployments with thousands of tenants these intervals are the knob to raise.
# HINDSIGHT_API_RETENTION_SWEEP_INTERVAL_SECONDS=3600 # How often expired audit_log / llm_requests rows are deleted across all tenant schemas. Retention is counted in days, so this only sets how promptly they disappear; 0 disables the sweeps.
# HINDSIGHT_API_OPERATION_CLEANUP_INTERVAL_SECONDS=900 # How often expired terminal operation rows are pruned; with the batch size above this sets the drain rate for a backlog. 0 disables the job.
# HINDSIGHT_API_MAINTENANCE_START_JITTER_SECONDS=60 # Upper bound on a random delay before a process runs its FIRST maintenance tick. Every job is due on that tick, so without an offset a fleet started together runs every sweep in every process at once. 0 disables the jitter.
# Vector Extension (Optional - uses pgvector by default)
# Options: "pgvector" (default), "vchord", "pgvectorscale" (DiskANN)
# HINDSIGHT_API_VECTOR_EXTENSION=pgvector
# Let a vector index scan resume until the query's LIMIT is satisfied, instead of
# stopping when its first candidate list drains (pgvector: hnsw.ef_search, 200) — with
# it off, a larger recall budget cannot retrieve more rows. Needs pgvector 0.8.0+;
# older servers reject it and it is dropped automatically. Set false and restart as a
# quick revert to the previous retrieval depth, with no code change.
# HINDSIGHT_API_ANN_ITERATIVE_SCAN=true
# Ceiling on tuples one resumed scan may visit. Bounds the CPU and memory a selective
# query can spend resuming (filters are applied after the scan, so it resumes often).
# Lower it to trade depth back for latency. pgvector's own default is 20000.
# HINDSIGHT_API_ANN_MAX_SCAN_TUPLES=4000
# For Azure PostgreSQL with DiskANN:
# HINDSIGHT_API_VECTOR_EXTENSION=pgvectorscale # Auto-detects pg_diskann on Azure
# Per-bank vector indexes (pgvector / pgvectorscale / vchord only; ScaNN and Oracle use one global index)
# HINDSIGHT_API_VECTOR_INDEX_MIN_ROWS=0 # Memories a bank needs in one fact type before that fact type gets its own vector index. 0 (default) turns the threshold OFF: every bank is indexed when it is created, and no background maintenance runs. Set ~10000 on deployments with thousands of banks: every index lives on the shared memory_units table and is planned against by every OTHER bank's queries, so unconditional per-bank indexes put a ceiling on bank count. Smaller banks then use exact search, which is faster AND exact.
# HINDSIGHT_API_VECTOR_INDEX_MAINTENANCE_MIN_INTERVAL_SECONDS=900 # Shortest gap between two index-maintenance runs for one bank, so a bank hovering at the threshold cannot build and drop the same index repeatedly. Unused while the threshold is off.
# Text Search Extension (Optional - uses native PostgreSQL full-text search by default)
# Unused by a bank with HINDSIGHT_API_ENABLE_TEXT_SEARCH=false (see Recall pipeline stages).
# Backend options: "native" (default), "vchord", "pg_textsearch", "pgroonga", "pg_search"
# HINDSIGHT_API_TEXT_SEARCH_EXTENSION=native
# Native backend dictionary (only used by HINDSIGHT_API_TEXT_SEARCH_EXTENSION=native)
# HINDSIGHT_API_TEXT_SEARCH_EXTENSION_NATIVE_LANGUAGE=english
# ParadeDB pg_search tokenizer (only used when creating pg_search BM25 indexes).
# Empty uses ParadeDB's default tokenizer: unicode_words.
# Supported values: unicode_words, simple, whitespace, literal, literal_normalized,
# chinese_compatible, icu, jieba, source_code,
# chinese_lindera/lindera(chinese), japanese_lindera/lindera(japanese),
# korean_lindera/lindera(korean), ngram(min,max), edge_ngram(min,max)
# HINDSIGHT_API_TEXT_SEARCH_EXTENSION_PG_SEARCH_TOKENIZER=
# ParadeDB pg_search function schema (default: paradedb).
# Certain managed PostgreSQL distributions install pg_search functions under pgsearch.
# HINDSIGHT_API_TEXT_SEARCH_EXTENSION_PG_SEARCH_FUNCTION_SCHEMA=paradedb
# Cap on the number of terms in the native PostgreSQL BM25 tsquery. Long queries
# OR-join every normalized token, and native ranking (no IDF, re-ranks every
# match) can then scan a large fraction of the bank and time out. Over the cap,
# the most selective terms are kept — lowest tenant-wide document frequency, read
# for free from pg_stats (no reindex). 0 restores the uncapped behavior; the cap
# bounds only the native backend (other BM25 backends get the raw query).
# HINDSIGHT_API_BM25_MAX_QUERY_TERMS=16
# When the cap above trims a query, keep the most selective terms (lowest
# document frequency, from pg_stats) instead of the first N. true is strictly
# better for recall at no extra cost when stats exist; set false to opt out of
# the catalog read and cap by position. Ignored when the cap is 0.
# HINDSIGHT_API_BM25_SELECTIVE_TERMS=true
# File Parser (Optional - uses markitdown by default)
# HINDSIGHT_API_FILE_PARSER=markitdown
# Enable image OCR for MarkItDown using an OpenAI-compatible OCR/vision endpoint.
# These OCR settings are independent from HINDSIGHT_API_LLM_* because MarkItDown
# uses the OpenAI SDK directly and requires Chat Completions image input support.
# When OCR is enabled, API_KEY, BASE_URL, and MODEL are required.
# HINDSIGHT_API_FILE_PARSER_MARKITDOWN_OCR_ENABLED=false
# HINDSIGHT_API_FILE_PARSER_MARKITDOWN_OCR_API_KEY=
# HINDSIGHT_API_FILE_PARSER_MARKITDOWN_OCR_BASE_URL=
# HINDSIGHT_API_FILE_PARSER_MARKITDOWN_OCR_MODEL=
# HINDSIGHT_API_FILE_PARSER_MARKITDOWN_OCR_PROMPT=
# Optional JSON dict of custom headers for the OCR OpenAI client (e.g. proxies / request tracing).
# HINDSIGHT_API_FILE_PARSER_MARKITDOWN_OCR_DEFAULT_HEADERS=
# Embeddings Configuration (Optional - uses local by default)
# Provider: "local" (default), "onnx", "tei", "openai", "cohere", "google", "openrouter", "zeroentropy", "litellm", or "litellm-sdk"
# HINDSIGHT_API_EMBEDDINGS_PROVIDER=local
# For local provider:
# HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-small-en-v1.5
# Force CPU if local embeddings hit MPS/XPC instability on macOS:
# HINDSIGHT_API_EMBEDDINGS_LOCAL_FORCE_CPU=false
# Opt in to the Apple Silicon MPS GPU (off by default: MPS leaks memory under
# variable-length workloads). CUDA/XPU still auto-select regardless:
# HINDSIGHT_API_EMBEDDINGS_LOCAL_ALLOW_MPS=false
# For ONNX provider (local CPU embeddings without an Ollama/TEI sidecar):
# HINDSIGHT_API_EMBEDDINGS_PROVIDER=onnx
# HINDSIGHT_API_EMBEDDINGS_ONNX_MODEL_ID=intfloat/multilingual-e5-small
# HINDSIGHT_API_EMBEDDINGS_ONNX_FILE=onnx/model.onnx
# HINDSIGHT_API_EMBEDDINGS_ONNX_DIMENSIONS=384
# HINDSIGHT_API_EMBEDDINGS_ONNX_MAX_TOKENS=512
# HINDSIGHT_API_EMBEDDINGS_ONNX_POOLING=mean
# HINDSIGHT_API_EMBEDDINGS_ONNX_NORMALIZE=true
# HINDSIGHT_API_EMBEDDINGS_ONNX_QUERY_PREFIX="query: "
# HINDSIGHT_API_EMBEDDINGS_ONNX_PASSAGE_PREFIX="passage: "
# HINDSIGHT_API_EMBEDDINGS_ONNX_BATCH_SIZE=32 # Texts per forward pass; bounds peak memory
# HINDSIGHT_API_EMBEDDINGS_ONNX_CPU_MEM_ARENA=false # ONNX CPU memory arena; true lets RSS ratchet up
# Optional for local model paths or pre-downloaded artifacts:
# HINDSIGHT_API_EMBEDDINGS_ONNX_MODEL_PATH=/models/multilingual-e5-small/onnx/model.onnx
# HINDSIGHT_API_EMBEDDINGS_ONNX_TOKENIZER_NAME_OR_PATH=/models/multilingual-e5-small
# Optional for China network / restricted HF access:
# HF_ENDPOINT=https://hf-mirror.com
# Applies to any provider: cap each input at this many tokens before
# embedding, so oversized content is truncated instead of failing the embed call
# permanently (e.g. Bedrock Titan V2's 8192, or a llama.cpp server's context). Off
# by default. (Deprecated alias: HINDSIGHT_API_EMBEDDINGS_LITELLM_SDK_MAX_INPUT_TOKENS)
# HINDSIGHT_API_EMBEDDINGS_MAX_INPUT_TOKENS=8192
# Asymmetric models (E5, google/embeddinggemma-300m, ...) expect a different instruction
# in front of a search than in front of stored text. Providers that only accept plain text
# (tei, openai-compatible, litellm) need it applied client-side; local/zeroentropy handle it
# themselves and ignore these. Unset = text sent as-is.
# HINDSIGHT_API_EMBEDDINGS_QUERY_PREFIX="task: search result | query: "
# HINDSIGHT_API_EMBEDDINGS_PASSAGE_PREFIX="title: none | text: "
# For TEI provider:
# HINDSIGHT_API_EMBEDDINGS_TEI_URL=http://localhost:8080
# Max texts per TEI /embed request; also the batch size retain coalesces a document's per-chunk embeddings up to
# HINDSIGHT_API_EMBEDDINGS_TEI_BATCH_SIZE=32
# For OpenAI-compatible embeddings:
# HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxxx
# HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=text-embedding-3-small
# HINDSIGHT_API_EMBEDDINGS_OPENAI_BASE_URL=https://api.openai.com/v1
# For LiteLLM proxy embeddings:
# HINDSIGHT_API_EMBEDDINGS_PROVIDER=litellm
# HINDSIGHT_API_EMBEDDINGS_LITELLM_API_BASE=http://localhost:4000
# HINDSIGHT_API_EMBEDDINGS_LITELLM_API_KEY=your-litellm-key
# HINDSIGHT_API_EMBEDDINGS_LITELLM_MODEL=text-embedding-3-small
# HINDSIGHT_API_EMBEDDINGS_LITELLM_DIMENSIONS=1536 # declare the vector width to skip the startup probe
# For ZeroEntropy zembed-1:
# HINDSIGHT_API_EMBEDDINGS_PROVIDER=zeroentropy
# HINDSIGHT_API_EMBEDDINGS_ZEROENTROPY_API_KEY=ze-xxxx
# HINDSIGHT_API_EMBEDDINGS_ZEROENTROPY_MODEL=zembed-1
# HINDSIGHT_API_EMBEDDINGS_ZEROENTROPY_DIMENSIONS=1280
# HINDSIGHT_API_EMBEDDINGS_ZEROENTROPY_ENCODING_FORMAT=float
# HINDSIGHT_API_EMBEDDINGS_ZEROENTROPY_LATENCY=fast
#
# IMPORTANT: Embedding keys require provider-specific names:
# HINDSIGHT_API_EMBEDDINGS_{PROVIDER}_{PARAMETER}
# (for example, HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL).
#
# DeepSeek note: DeepSeek is supported for LLM calls, but not for embeddings.
# If using DeepSeek as LLM provider, keep embeddings on local/openai/cohere/google/etc.
# Retry policy for remote embedding calls (litellm / litellm-sdk providers).
# Transient upstream failures (5xx, timeouts, connection errors) are retried with
# jittered exponential backoff; 4xx auth/validation errors are never retried.
# HINDSIGHT_API_EMBEDDINGS_MAX_RETRIES=4 # retries after the first attempt; 0 disables
# HINDSIGHT_API_EMBEDDINGS_INITIAL_BACKOFF=0.5 # seconds; doubles per attempt, with jitter
# HINDSIGHT_API_EMBEDDINGS_MAX_BACKOFF=4.0 # cap on per-retry backoff, seconds
# HINDSIGHT_API_EMBEDDINGS_RETRY_BUDGET=15.0 # wall-clock ceiling per encode() spent retrying
# Embedding similarity thresholds. These defaults preserve the behavior calibrated
# for BAAI/bge-small-en-v1.5. Recalibrate each threshold independently when changing
# embedding models because cosine-similarity distributions are model-dependent.
# HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY=0.3
# HINDSIGHT_API_GRAPH_SEED_MIN_SIMILARITY=0.3
# HINDSIGHT_API_TEMPORAL_SEMANTIC_MIN_SIMILARITY=0.1
# HINDSIGHT_API_SEMANTIC_LINK_MIN_SIMILARITY=0.7
# HINDSIGHT_API_CONSOLIDATION_DEDUP_THRESHOLD=0.97
# Floor on how often an automatic mental-model refresh (after-consolidation or cron)
# may run, in seconds. A trigger that fires sooner is queued and parked until the
# window closes, and further triggers fold into it, so a burst of small retains costs
# one refresh instead of one per retain. 0 = no floor (the historical behaviour).
# Hierarchical, and overridable per model via the trigger's min_refresh_interval_seconds.
# Explicit refreshes always run immediately.
# HINDSIGHT_API_MENTAL_MODEL_MIN_REFRESH_INTERVAL_SECONDS=0
# Recall pipeline stages (all on by default). Each is hierarchical, so a single
# bank can switch a stage off via the config API without changing the server
# default. Turning all four off reduces recall to a single vector query, the
# lowest-latency recall path.
# Keyword (BM25) arm. false leaves pure vector search: the arm is left out of the
# query entirely rather than filtered to nothing, so its SQL, its query tokenization
# and its pg_stats term-selection lookup are all skipped. Also drops the keyword arm
# from knowledge-page search:
# HINDSIGHT_API_ENABLE_TEXT_SEARCH=true
# Temporal retrieval arm, plus the date-aware query analysis that feeds it:
# HINDSIGHT_API_ENABLE_TEMPORAL_RETRIEVAL=true
# Entity/link graph traversal arm:
# HINDSIGHT_API_ENABLE_GRAPH_RETRIEVAL=true
# Cross-encoder rerank of the fused candidates (false = use the RRF order):
# HINDSIGHT_API_ENABLE_RERANKING=true
# Reranker Configuration (Optional - uses local by default)
# Provider: "local" (default) or "tei" (HuggingFace Text Embeddings Inference)
# HINDSIGHT_API_RERANKER_PROVIDER=local
# Trusted gateway attribution (disabled by default). When enabled, remote
# reranker requests include X-Hindsight-Bank-Id with the current bank ID.
# HINDSIGHT_API_RERANKER_SEND_BANK_AS_HEADER=false
# For local provider:
# HINDSIGHT_API_RERANKER_LOCAL_MODEL=cross-encoder/ms-marco-MiniLM-L-6-v2
# Force CPU if the local reranker hits MPS/XPC instability on macOS:
# HINDSIGHT_API_RERANKER_LOCAL_FORCE_CPU=false
# Opt in to the Apple Silicon MPS GPU (off by default: MPS leaks memory under
# variable-length workloads). CUDA/XPU still auto-select regardless:
# HINDSIGHT_API_RERANKER_LOCAL_ALLOW_MPS=false
# For TEI provider:
# HINDSIGHT_API_RERANKER_TEI_URL=http://localhost:8081
# For flashrank provider: passages scored per ONNX forward pass. Each pass
# allocates attention tensors sized batch * heads * seq^2, so raising this
# raises peak memory quadratically in passage length:
# HINDSIGHT_API_RERANKER_FLASHRANK_BATCH_SIZE=32
# Max candidates the cross-encoder reranks per recall (RRF pre-filters the rest):
# HINDSIGHT_API_RERANKER_MAX_CANDIDATES=300
# Optionally scale that cap by the recall budget level (the cross-encoder dominates
# a large recall's latency). 0 = fall back to the flat cap above; fully backwards-compatible.
# HINDSIGHT_API_RERANKER_MAX_CANDIDATES_LOW=0
# HINDSIGHT_API_RERANKER_MAX_CANDIDATES_MID=0
# HINDSIGHT_API_RERANKER_MAX_CANDIDATES_HIGH=0
# Reranker failover chain: extra rerankers tried, in order, when the one above
# fails. Members are numbered from 1 (indices must be contiguous) and every
# setting of member n carries the same index. A member inherits nothing from the
# primary, so spell out everything it needs. Unset = no fallback (default): a
# failing reranker fails the recall. End the chain with "rrf" to fail open and
# keep the retrieval order instead.
# HINDSIGHT_API_RERANKER_1_PROVIDER=cohere
# HINDSIGHT_API_RERANKER_1_COHERE_API_KEY=your-cohere-api-key
# HINDSIGHT_API_RERANKER_2_PROVIDER=rrf
# Observability & Tracing (Optional - disabled by default)
# Enable OpenTelemetry tracing for LLM calls (GenAI semantic conventions)
# HINDSIGHT_API_OTEL_TRACES_ENABLED=true
#
# Local development with Grafana LGTM stack (recommended - see scripts/dev/grafana/README.md)
# HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
#
# Cloud backends (Grafana Cloud, Langfuse, DataDog, etc.)
# HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=https://your-backend-url
# HINDSIGHT_API_OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token"
#
# Custom service name and environment (optional, defaults: hindsight-api, development)
# HINDSIGHT_API_OTEL_SERVICE_NAME=hindsight-production
# HINDSIGHT_API_OTEL_DEPLOYMENT_ENVIRONMENT=production
#
# Expose async-operation queue + consolidation-backlog gauges on /metrics.
# Runs periodic per-schema COUNT queries on a background task (disabled by default).
# HINDSIGHT_API_METRICS_BACKLOG_ENABLED=true
#
# Runtime-stall observability (enabled by default). When a liveness probe fails,
# these tell you WHY: a blocked event loop vs DB connection-pool exhaustion.
# The loop watchdog logs the offending stack when the loop is unresponsive; the
# DB-pool acquire timing logs (and exposes hindsight.db.pool.waiting) when
# callers queue for a connection. Both are cheap; tune or disable if needed.
# HINDSIGHT_API_LOOP_WATCHDOG_ENABLED=false
# HINDSIGHT_API_LOOP_WATCHDOG_STALL_THRESHOLD_MS=1000
# HINDSIGHT_API_LOOP_WATCHDOG_POLL_INTERVAL_MS=250
# HINDSIGHT_API_DB_ACQUIRE_WARN_THRESHOLD_MS=1000
# -----------------------------------------------------------------------------
# Extensions (Optional)
# -----------------------------------------------------------------------------
# Request headers copied into RequestContext.extra_headers so a custom
# TenantExtension / OperationValidatorExtension can read them. Comma-separated,
# matched case-insensitively. Unset by default: extensions see only the
# Authorization header. Use this when the bearer token identifies a proxy rather
# than the caller, and per-caller identity arrives in a separate header. A listed
# header that arrives more than once is dropped, so only list headers the proxy
# in front of Hindsight sets itself (stripping any client-supplied copy).
# HINDSIGHT_API_EXTENSION_PASSTHROUGH_HEADERS=x-user-assertion
# -----------------------------------------------------------------------------
# Webhooks (Optional)
# -----------------------------------------------------------------------------
# Outbound webhook delivery targets caller-supplied URLs. To prevent SSRF, the
# delivery worker blocks private, loopback, and link-local destinations
# (including the cloud metadata address 169.254.169.254) by default. List hosts
# or IP/CIDR ranges here (comma-separated) to re-permit specific internal
# destinations — e.g. 127.0.0.1 for local testing, or an internal receiver.
# HINDSIGHT_API_WEBHOOK_ALLOWED_HOSTS=127.0.0.1,internal-receiver.svc,10.0.0.0/8
# Whether the webhook delivery-history API returns the raw upstream response
# body. Off by default: returning arbitrary response bodies to callers is an
# information-exfiltration primitive. The delivery status code is always
# returned regardless. Enable only if you trust your webhook destinations.
# HINDSIGHT_API_WEBHOOK_EXPOSE_RESPONSE_BODY=false
# -----------------------------------------------------------------------------
# Control Plane (Optional)
# -----------------------------------------------------------------------------
# Dataplane API URL - where the CP proxies requests to
# HINDSIGHT_CP_DATAPLANE_API_URL=http://localhost:8888
# Optional: Bearer token the CP sends as `Authorization: Bearer <key>` to the
# dataplane API. Required when the API service is auth-protected; omit for a
# public/unauthenticated API.
# HINDSIGHT_CP_DATAPLANE_API_KEY=your-dataplane-bearer-token
# Optional: Require a shared access key to view the Control Plane UI.
# When set, visitors see a login page and must enter the key before
# accessing the dashboard or any /api/* routes (except /api/health).
# HINDSIGHT_CP_ACCESS_KEY=your-shared-secret-key