Compare commits

...
Author SHA1 Message Date
Nicolò Boschi 504bd5e67b feat: add configs for database connection 2026-01-08 16:25:19 +01:00
4 changed files with 87 additions and 9 deletions
+38
View File
@@ -73,6 +73,16 @@ ENV_LAZY_RERANKER = "HINDSIGHT_API_LAZY_RERANKER"
# Database migrations
ENV_RUN_MIGRATIONS_ON_STARTUP = "HINDSIGHT_API_RUN_MIGRATIONS_ON_STARTUP"
# Database connection pool
ENV_DB_POOL_MIN_SIZE = "HINDSIGHT_API_DB_POOL_MIN_SIZE"
ENV_DB_POOL_MAX_SIZE = "HINDSIGHT_API_DB_POOL_MAX_SIZE"
ENV_DB_COMMAND_TIMEOUT = "HINDSIGHT_API_DB_COMMAND_TIMEOUT"
ENV_DB_ACQUIRE_TIMEOUT = "HINDSIGHT_API_DB_ACQUIRE_TIMEOUT"
# Background task processing
ENV_TASK_BATCH_SIZE = "HINDSIGHT_API_TASK_BATCH_SIZE"
ENV_TASK_BATCH_INTERVAL = "HINDSIGHT_API_TASK_BATCH_INTERVAL"
# Default values
DEFAULT_DATABASE_URL = "pg0"
DEFAULT_LLM_PROVIDER = "openai"
@@ -109,6 +119,16 @@ DEFAULT_RETAIN_CHUNK_SIZE = 3000 # Max chars per chunk for fact extraction
# Database migrations
DEFAULT_RUN_MIGRATIONS_ON_STARTUP = True
# Database connection pool
DEFAULT_DB_POOL_MIN_SIZE = 5
DEFAULT_DB_POOL_MAX_SIZE = 100
DEFAULT_DB_COMMAND_TIMEOUT = 60 # seconds
DEFAULT_DB_ACQUIRE_TIMEOUT = 30 # seconds
# Background task processing
DEFAULT_TASK_BATCH_SIZE = 10
DEFAULT_TASK_BATCH_INTERVAL = 1.0 # seconds
# Default MCP tool descriptions (can be customized via env vars)
DEFAULT_MCP_RETAIN_DESCRIPTION = """Store important information to long-term memory.
@@ -193,6 +213,16 @@ class HindsightConfig:
# Database migrations
run_migrations_on_startup: bool
# Database connection pool
db_pool_min_size: int
db_pool_max_size: int
db_command_timeout: int
db_acquire_timeout: int
# Background task processing
task_batch_size: int
task_batch_interval: float
@classmethod
def from_env(cls) -> "HindsightConfig":
"""Create configuration from environment variables."""
@@ -245,6 +275,14 @@ class HindsightConfig:
retain_chunk_size=int(os.getenv(ENV_RETAIN_CHUNK_SIZE, str(DEFAULT_RETAIN_CHUNK_SIZE))),
# Database migrations
run_migrations_on_startup=os.getenv(ENV_RUN_MIGRATIONS_ON_STARTUP, "true").lower() == "true",
# Database connection pool
db_pool_min_size=int(os.getenv(ENV_DB_POOL_MIN_SIZE, str(DEFAULT_DB_POOL_MIN_SIZE))),
db_pool_max_size=int(os.getenv(ENV_DB_POOL_MAX_SIZE, str(DEFAULT_DB_POOL_MAX_SIZE))),
db_command_timeout=int(os.getenv(ENV_DB_COMMAND_TIMEOUT, str(DEFAULT_DB_COMMAND_TIMEOUT))),
db_acquire_timeout=int(os.getenv(ENV_DB_ACQUIRE_TIMEOUT, str(DEFAULT_DB_ACQUIRE_TIMEOUT))),
# Background task processing
task_batch_size=int(os.getenv(ENV_TASK_BATCH_SIZE, str(DEFAULT_TASK_BATCH_SIZE))),
task_batch_interval=float(os.getenv(ENV_TASK_BATCH_INTERVAL, str(DEFAULT_TASK_BATCH_INTERVAL))),
)
def get_llm_base_url(self) -> str:
@@ -215,9 +215,13 @@ class MemoryEngine(MemoryEngineInterface):
embeddings: Embeddings | None = None,
cross_encoder: CrossEncoderModel | None = None,
query_analyzer: QueryAnalyzer | None = None,
pool_min_size: int = 5,
pool_max_size: int = 100,
pool_min_size: int | None = None,
pool_max_size: int | None = None,
db_command_timeout: int | None = None,
db_acquire_timeout: int | None = None,
task_backend: TaskBackend | None = None,
task_batch_size: int | None = None,
task_batch_interval: float | None = None,
run_migrations: bool = True,
operation_validator: "OperationValidatorExtension | None" = None,
tenant_extension: "TenantExtension | None" = None,
@@ -248,9 +252,13 @@ class MemoryEngine(MemoryEngineInterface):
embeddings: Embeddings implementation. If not provided, created from env vars.
cross_encoder: Cross-encoder model. If not provided, created from env vars.
query_analyzer: Query analyzer implementation. If not provided, uses DateparserQueryAnalyzer.
pool_min_size: Minimum number of connections in the pool (default: 5)
pool_max_size: Maximum number of connections in the pool (default: 100)
pool_min_size: Minimum number of connections in the pool. Defaults to HINDSIGHT_API_DB_POOL_MIN_SIZE.
pool_max_size: Maximum number of connections in the pool. Defaults to HINDSIGHT_API_DB_POOL_MAX_SIZE.
db_command_timeout: PostgreSQL command timeout in seconds. Defaults to HINDSIGHT_API_DB_COMMAND_TIMEOUT.
db_acquire_timeout: Connection acquisition timeout in seconds. Defaults to HINDSIGHT_API_DB_ACQUIRE_TIMEOUT.
task_backend: Custom task backend. If not provided, uses AsyncIOQueueBackend.
task_batch_size: Background task batch size. Defaults to HINDSIGHT_API_TASK_BATCH_SIZE.
task_batch_interval: Background task batch interval in seconds. Defaults to HINDSIGHT_API_TASK_BATCH_INTERVAL.
run_migrations: Whether to run database migrations during initialize(). Default: True
operation_validator: Optional extension to validate operations before execution.
If provided, retain/recall/reflect operations will be validated.
@@ -306,8 +314,10 @@ class MemoryEngine(MemoryEngineInterface):
# Connection pool (will be created in initialize())
self._pool = None
self._initialized = False
self._pool_min_size = pool_min_size
self._pool_max_size = pool_max_size
self._pool_min_size = pool_min_size if pool_min_size is not None else config.db_pool_min_size
self._pool_max_size = pool_max_size if pool_max_size is not None else config.db_pool_max_size
self._db_command_timeout = db_command_timeout if db_command_timeout is not None else config.db_command_timeout
self._db_acquire_timeout = db_acquire_timeout if db_acquire_timeout is not None else config.db_acquire_timeout
self._run_migrations = run_migrations
# Initialize entity resolver (will be created in initialize())
@@ -386,7 +396,11 @@ class MemoryEngine(MemoryEngineInterface):
self._cross_encoder_reranker = CrossEncoderReranker(cross_encoder=cross_encoder)
# Initialize task backend
self._task_backend = task_backend or AsyncIOQueueBackend(batch_size=100, batch_interval=1.0)
_task_batch_size = task_batch_size if task_batch_size is not None else config.task_batch_size
_task_batch_interval = task_batch_interval if task_batch_interval is not None else config.task_batch_interval
self._task_backend = task_backend or AsyncIOQueueBackend(
batch_size=_task_batch_size, batch_interval=_task_batch_interval
)
# Backpressure mechanism: limit concurrent searches to prevent overwhelming the database
# Limit concurrent searches to prevent connection pool exhaustion
@@ -731,9 +745,9 @@ class MemoryEngine(MemoryEngineInterface):
self.db_url,
min_size=self._pool_min_size,
max_size=self._pool_max_size,
command_timeout=60,
command_timeout=self._db_command_timeout,
statement_cache_size=0, # Disable prepared statement cache
timeout=30, # Connection acquisition timeout (seconds)
timeout=self._db_acquire_timeout, # Connection acquisition timeout (seconds)
)
# Initialize entity resolver with pool
+6
View File
@@ -197,6 +197,12 @@ def main():
skip_llm_verification=config.skip_llm_verification,
lazy_reranker=config.lazy_reranker,
run_migrations_on_startup=config.run_migrations_on_startup,
db_pool_min_size=config.db_pool_min_size,
db_pool_max_size=config.db_pool_max_size,
db_command_timeout=config.db_command_timeout,
db_acquire_timeout=config.db_acquire_timeout,
task_batch_size=config.task_batch_size,
task_batch_interval=config.task_batch_interval,
)
config.configure_logging()
if not args.daemon:
@@ -24,6 +24,17 @@ The API service handles all memory operations (retain, recall, reflect).
If not provided, the server uses embedded `pg0` — convenient for development but not recommended for production.
### Database Connection Pool
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_DB_POOL_MIN_SIZE` | Minimum connections in the pool | `5` |
| `HINDSIGHT_API_DB_POOL_MAX_SIZE` | Maximum connections in the pool | `100` |
| `HINDSIGHT_API_DB_COMMAND_TIMEOUT` | PostgreSQL command timeout in seconds | `60` |
| `HINDSIGHT_API_DB_ACQUIRE_TIMEOUT` | Connection acquisition timeout in seconds | `30` |
For high-concurrency workloads, increase `DB_POOL_MAX_SIZE`. Each concurrent recall/think operation can use 2-4 connections.
To run migrations manually (e.g., before starting the API), use the admin CLI:
```bash
@@ -266,6 +277,15 @@ Configuration for the local MCP server (`hindsight-local-mcp` command).
export HINDSIGHT_API_MCP_INSTRUCTIONS="Also store every action you take, including tool calls and decisions made."
```
### Background Tasks
Controls background task processing for async operations like opinion formation and entity observations.
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_TASK_BATCH_SIZE` | Max tasks to process in one batch | `10` |
| `HINDSIGHT_API_TASK_BATCH_INTERVAL` | Interval between batch processing in seconds | `1.0` |
### Performance Optimization
| Variable | Description | Default |