hindsight-api 0.3.0__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hindsight_api/__init__.py +1 -1
- hindsight_api/admin/cli.py +59 -0
- hindsight_api/alembic/versions/h3c4d5e6f7g8_mental_models_v4.py +112 -0
- hindsight_api/alembic/versions/i4d5e6f7g8h9_delete_opinions.py +41 -0
- hindsight_api/alembic/versions/j5e6f7g8h9i0_mental_model_versions.py +95 -0
- hindsight_api/alembic/versions/k6f7g8h9i0j1_add_directive_subtype.py +58 -0
- hindsight_api/alembic/versions/l7g8h9i0j1k2_add_worker_columns.py +109 -0
- hindsight_api/alembic/versions/m8h9i0j1k2l3_mental_model_id_to_text.py +41 -0
- hindsight_api/alembic/versions/n9i0j1k2l3m4_learnings_and_pinned_reflections.py +134 -0
- hindsight_api/alembic/versions/o0j1k2l3m4n5_migrate_mental_models_data.py +113 -0
- hindsight_api/alembic/versions/p1k2l3m4n5o6_new_knowledge_architecture.py +194 -0
- hindsight_api/alembic/versions/q2l3m4n5o6p7_fix_mental_model_fact_type.py +50 -0
- hindsight_api/alembic/versions/r3m4n5o6p7q8_add_reflect_response_to_reflections.py +47 -0
- hindsight_api/alembic/versions/s4n5o6p7q8r9_add_consolidated_at_to_memory_units.py +53 -0
- hindsight_api/alembic/versions/t5o6p7q8r9s0_rename_mental_models_to_observations.py +134 -0
- hindsight_api/alembic/versions/u6p7q8r9s0t1_mental_models_text_id.py +41 -0
- hindsight_api/alembic/versions/v7q8r9s0t1u2_add_max_tokens_to_mental_models.py +50 -0
- hindsight_api/api/http.py +1120 -93
- hindsight_api/api/mcp.py +11 -191
- hindsight_api/config.py +174 -46
- hindsight_api/engine/consolidation/__init__.py +5 -0
- hindsight_api/engine/consolidation/consolidator.py +926 -0
- hindsight_api/engine/consolidation/prompts.py +77 -0
- hindsight_api/engine/cross_encoder.py +153 -22
- hindsight_api/engine/directives/__init__.py +5 -0
- hindsight_api/engine/directives/models.py +37 -0
- hindsight_api/engine/embeddings.py +136 -13
- hindsight_api/engine/interface.py +32 -13
- hindsight_api/engine/llm_wrapper.py +505 -43
- hindsight_api/engine/memory_engine.py +2101 -1094
- hindsight_api/engine/mental_models/__init__.py +14 -0
- hindsight_api/engine/mental_models/models.py +53 -0
- hindsight_api/engine/reflect/__init__.py +18 -0
- hindsight_api/engine/reflect/agent.py +933 -0
- hindsight_api/engine/reflect/models.py +109 -0
- hindsight_api/engine/reflect/observations.py +186 -0
- hindsight_api/engine/reflect/prompts.py +483 -0
- hindsight_api/engine/reflect/tools.py +437 -0
- hindsight_api/engine/reflect/tools_schema.py +250 -0
- hindsight_api/engine/response_models.py +130 -4
- hindsight_api/engine/retain/bank_utils.py +79 -201
- hindsight_api/engine/retain/fact_extraction.py +81 -48
- hindsight_api/engine/retain/fact_storage.py +5 -8
- hindsight_api/engine/retain/link_utils.py +5 -8
- hindsight_api/engine/retain/orchestrator.py +1 -55
- hindsight_api/engine/retain/types.py +2 -2
- hindsight_api/engine/search/graph_retrieval.py +2 -2
- hindsight_api/engine/search/link_expansion_retrieval.py +164 -29
- hindsight_api/engine/search/mpfp_retrieval.py +1 -1
- hindsight_api/engine/search/retrieval.py +14 -14
- hindsight_api/engine/search/think_utils.py +41 -140
- hindsight_api/engine/search/trace.py +0 -1
- hindsight_api/engine/search/tracer.py +2 -5
- hindsight_api/engine/search/types.py +0 -3
- hindsight_api/engine/task_backend.py +112 -196
- hindsight_api/engine/utils.py +0 -151
- hindsight_api/extensions/__init__.py +10 -1
- hindsight_api/extensions/builtin/tenant.py +11 -4
- hindsight_api/extensions/operation_validator.py +81 -4
- hindsight_api/extensions/tenant.py +26 -0
- hindsight_api/main.py +28 -5
- hindsight_api/mcp_local.py +12 -53
- hindsight_api/mcp_tools.py +494 -0
- hindsight_api/models.py +0 -2
- hindsight_api/worker/__init__.py +11 -0
- hindsight_api/worker/main.py +296 -0
- hindsight_api/worker/poller.py +486 -0
- {hindsight_api-0.3.0.dist-info → hindsight_api-0.4.1.dist-info}/METADATA +12 -6
- hindsight_api-0.4.1.dist-info/RECORD +112 -0
- {hindsight_api-0.3.0.dist-info → hindsight_api-0.4.1.dist-info}/entry_points.txt +1 -0
- hindsight_api/engine/retain/observation_regeneration.py +0 -254
- hindsight_api/engine/search/observation_utils.py +0 -125
- hindsight_api/engine/search/scoring.py +0 -159
- hindsight_api-0.3.0.dist-info/RECORD +0 -82
- {hindsight_api-0.3.0.dist-info → hindsight_api-0.4.1.dist-info}/WHEEL +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Operation Validator Extension for validating retain/recall/reflect operations."""
|
|
1
|
+
"""Operation Validator Extension for validating retain/recall/reflect/consolidate operations."""
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from dataclasses import dataclass, field
|
|
@@ -97,6 +97,19 @@ class ReflectContext:
|
|
|
97
97
|
context: str | None = None
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
# =============================================================================
|
|
101
|
+
# Consolidation Pre-operation Context
|
|
102
|
+
# =============================================================================
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class ConsolidateContext:
|
|
107
|
+
"""Context for a consolidation operation validation (pre-operation)."""
|
|
108
|
+
|
|
109
|
+
bank_id: str
|
|
110
|
+
request_context: "RequestContext"
|
|
111
|
+
|
|
112
|
+
|
|
100
113
|
# =============================================================================
|
|
101
114
|
# Post-operation Contexts (includes results)
|
|
102
115
|
# =============================================================================
|
|
@@ -164,9 +177,28 @@ class ReflectResultContext:
|
|
|
164
177
|
error: str | None = None
|
|
165
178
|
|
|
166
179
|
|
|
180
|
+
# =============================================================================
|
|
181
|
+
# Consolidation Post-operation Context
|
|
182
|
+
# =============================================================================
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@dataclass
|
|
186
|
+
class ConsolidateResult:
|
|
187
|
+
"""Result context for post-consolidation hook."""
|
|
188
|
+
|
|
189
|
+
bank_id: str
|
|
190
|
+
request_context: "RequestContext"
|
|
191
|
+
# Result
|
|
192
|
+
processed: int = 0
|
|
193
|
+
created: int = 0
|
|
194
|
+
updated: int = 0
|
|
195
|
+
success: bool = True
|
|
196
|
+
error: str | None = None
|
|
197
|
+
|
|
198
|
+
|
|
167
199
|
class OperationValidatorExtension(Extension, ABC):
|
|
168
200
|
"""
|
|
169
|
-
Validates and hooks into retain/recall/reflect operations.
|
|
201
|
+
Validates and hooks into retain/recall/reflect/consolidate operations.
|
|
170
202
|
|
|
171
203
|
This extension allows implementing custom logic such as:
|
|
172
204
|
- Rate limiting (pre-operation)
|
|
@@ -185,9 +217,13 @@ class OperationValidatorExtension(Extension, ABC):
|
|
|
185
217
|
-> config = {"max_requests": "100"}
|
|
186
218
|
|
|
187
219
|
Hook execution order:
|
|
188
|
-
1.
|
|
220
|
+
1. validate_* (pre-operation)
|
|
189
221
|
2. [operation executes]
|
|
190
|
-
3.
|
|
222
|
+
3. on_*_complete (post-operation)
|
|
223
|
+
|
|
224
|
+
Supported operations:
|
|
225
|
+
- retain, recall, reflect (core memory operations)
|
|
226
|
+
- consolidate (mental models consolidation)
|
|
191
227
|
"""
|
|
192
228
|
|
|
193
229
|
# =========================================================================
|
|
@@ -325,3 +361,44 @@ class OperationValidatorExtension(Extension, ABC):
|
|
|
325
361
|
- error: Error message (if failed)
|
|
326
362
|
"""
|
|
327
363
|
pass
|
|
364
|
+
|
|
365
|
+
# =========================================================================
|
|
366
|
+
# Consolidation - Pre-operation validation hook (optional - override to implement)
|
|
367
|
+
# =========================================================================
|
|
368
|
+
|
|
369
|
+
async def validate_consolidate(self, ctx: ConsolidateContext) -> ValidationResult:
|
|
370
|
+
"""
|
|
371
|
+
Validate a consolidation operation before execution.
|
|
372
|
+
|
|
373
|
+
Override to implement custom validation logic for consolidation.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
ctx: Context containing:
|
|
377
|
+
- bank_id: Bank identifier
|
|
378
|
+
- request_context: Request context with auth info
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
ValidationResult indicating whether the operation is allowed.
|
|
382
|
+
"""
|
|
383
|
+
return ValidationResult.accept()
|
|
384
|
+
|
|
385
|
+
# =========================================================================
|
|
386
|
+
# Consolidation - Post-operation hook (optional - override to implement)
|
|
387
|
+
# =========================================================================
|
|
388
|
+
|
|
389
|
+
async def on_consolidate_complete(self, result: ConsolidateResult) -> None:
|
|
390
|
+
"""
|
|
391
|
+
Called after a consolidation operation completes (success or failure).
|
|
392
|
+
|
|
393
|
+
Override to implement post-operation logic such as usage tracking or audit logging.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
result: Result context containing:
|
|
397
|
+
- bank_id: Bank identifier
|
|
398
|
+
- processed: Number of memories processed
|
|
399
|
+
- created: Number of mental models created
|
|
400
|
+
- updated: Number of mental models updated
|
|
401
|
+
- success: Whether the operation succeeded
|
|
402
|
+
- error: Error message (if failed)
|
|
403
|
+
"""
|
|
404
|
+
pass
|
|
@@ -28,6 +28,18 @@ class TenantContext:
|
|
|
28
28
|
schema_name: str
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
@dataclass
|
|
32
|
+
class Tenant:
|
|
33
|
+
"""
|
|
34
|
+
Represents a tenant for worker discovery.
|
|
35
|
+
|
|
36
|
+
Used by list_tenants() to return tenant information including
|
|
37
|
+
the PostgreSQL schema name for database operations.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
schema: str
|
|
41
|
+
|
|
42
|
+
|
|
31
43
|
class TenantExtension(Extension, ABC):
|
|
32
44
|
"""
|
|
33
45
|
Extension for multi-tenancy and API key authentication.
|
|
@@ -61,3 +73,17 @@ class TenantExtension(Extension, ABC):
|
|
|
61
73
|
AuthenticationError: If authentication fails.
|
|
62
74
|
"""
|
|
63
75
|
...
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
async def list_tenants(self) -> list[Tenant]:
|
|
79
|
+
"""
|
|
80
|
+
List all tenants that should be processed by workers.
|
|
81
|
+
|
|
82
|
+
This method is used by the worker to discover all tenants that need
|
|
83
|
+
task polling. Workers will poll for pending tasks in each tenant's schema.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List of Tenant objects containing schema information.
|
|
87
|
+
For single-tenant setups, return [Tenant(schema="public")].
|
|
88
|
+
"""
|
|
89
|
+
...
|
hindsight_api/main.py
CHANGED
|
@@ -140,6 +140,13 @@ def main():
|
|
|
140
140
|
args.port = DEFAULT_DAEMON_PORT
|
|
141
141
|
args.host = "127.0.0.1" # Only bind to localhost for security
|
|
142
142
|
|
|
143
|
+
# Force CPU mode for daemon to avoid macOS MPS/XPC issues
|
|
144
|
+
# MPS (Metal Performance Shaders) has unstable XPC connections in background processes
|
|
145
|
+
# that can cause assertion failures and process crashes at the C++ level
|
|
146
|
+
# (which Python exception handlers cannot catch)
|
|
147
|
+
os.environ["HINDSIGHT_API_EMBEDDINGS_LOCAL_FORCE_CPU"] = "1"
|
|
148
|
+
os.environ["HINDSIGHT_API_RERANKER_LOCAL_FORCE_CPU"] = "1"
|
|
149
|
+
|
|
143
150
|
# Check if another daemon is already running
|
|
144
151
|
daemon_lock = DaemonLock()
|
|
145
152
|
if not daemon_lock.acquire():
|
|
@@ -170,6 +177,7 @@ def main():
|
|
|
170
177
|
if args.log_level != config.log_level:
|
|
171
178
|
config = HindsightConfig(
|
|
172
179
|
database_url=config.database_url,
|
|
180
|
+
database_schema=config.database_schema,
|
|
173
181
|
llm_provider=config.llm_provider,
|
|
174
182
|
llm_api_key=config.llm_api_key,
|
|
175
183
|
llm_model=config.llm_model,
|
|
@@ -184,13 +192,20 @@ def main():
|
|
|
184
192
|
reflect_llm_api_key=config.reflect_llm_api_key,
|
|
185
193
|
reflect_llm_model=config.reflect_llm_model,
|
|
186
194
|
reflect_llm_base_url=config.reflect_llm_base_url,
|
|
195
|
+
consolidation_llm_provider=config.consolidation_llm_provider,
|
|
196
|
+
consolidation_llm_api_key=config.consolidation_llm_api_key,
|
|
197
|
+
consolidation_llm_model=config.consolidation_llm_model,
|
|
198
|
+
consolidation_llm_base_url=config.consolidation_llm_base_url,
|
|
187
199
|
embeddings_provider=config.embeddings_provider,
|
|
188
200
|
embeddings_local_model=config.embeddings_local_model,
|
|
201
|
+
embeddings_local_force_cpu=config.embeddings_local_force_cpu,
|
|
189
202
|
embeddings_tei_url=config.embeddings_tei_url,
|
|
190
203
|
embeddings_openai_base_url=config.embeddings_openai_base_url,
|
|
191
204
|
embeddings_cohere_base_url=config.embeddings_cohere_base_url,
|
|
192
205
|
reranker_provider=config.reranker_provider,
|
|
193
206
|
reranker_local_model=config.reranker_local_model,
|
|
207
|
+
reranker_local_force_cpu=config.reranker_local_force_cpu,
|
|
208
|
+
reranker_local_max_concurrent=config.reranker_local_max_concurrent,
|
|
194
209
|
reranker_tei_url=config.reranker_tei_url,
|
|
195
210
|
reranker_tei_batch_size=config.reranker_tei_batch_size,
|
|
196
211
|
reranker_tei_max_concurrent=config.reranker_tei_max_concurrent,
|
|
@@ -199,18 +214,21 @@ def main():
|
|
|
199
214
|
host=args.host,
|
|
200
215
|
port=args.port,
|
|
201
216
|
log_level=args.log_level,
|
|
217
|
+
log_format=config.log_format,
|
|
202
218
|
mcp_enabled=config.mcp_enabled,
|
|
203
219
|
graph_retriever=config.graph_retriever,
|
|
204
220
|
mpfp_top_k_neighbors=config.mpfp_top_k_neighbors,
|
|
205
221
|
recall_max_concurrent=config.recall_max_concurrent,
|
|
206
222
|
recall_connection_budget=config.recall_connection_budget,
|
|
207
|
-
observation_min_facts=config.observation_min_facts,
|
|
208
|
-
observation_top_entities=config.observation_top_entities,
|
|
209
223
|
retain_max_completion_tokens=config.retain_max_completion_tokens,
|
|
210
224
|
retain_chunk_size=config.retain_chunk_size,
|
|
211
225
|
retain_extract_causal_links=config.retain_extract_causal_links,
|
|
212
226
|
retain_extraction_mode=config.retain_extraction_mode,
|
|
227
|
+
retain_custom_instructions=config.retain_custom_instructions,
|
|
213
228
|
retain_observations_async=config.retain_observations_async,
|
|
229
|
+
enable_observations=config.enable_observations,
|
|
230
|
+
consolidation_batch_size=config.consolidation_batch_size,
|
|
231
|
+
consolidation_max_tokens=config.consolidation_max_tokens,
|
|
214
232
|
skip_llm_verification=config.skip_llm_verification,
|
|
215
233
|
lazy_reranker=config.lazy_reranker,
|
|
216
234
|
run_migrations_on_startup=config.run_migrations_on_startup,
|
|
@@ -218,9 +236,14 @@ def main():
|
|
|
218
236
|
db_pool_max_size=config.db_pool_max_size,
|
|
219
237
|
db_command_timeout=config.db_command_timeout,
|
|
220
238
|
db_acquire_timeout=config.db_acquire_timeout,
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
239
|
+
worker_enabled=config.worker_enabled,
|
|
240
|
+
worker_id=config.worker_id,
|
|
241
|
+
worker_poll_interval_ms=config.worker_poll_interval_ms,
|
|
242
|
+
worker_max_retries=config.worker_max_retries,
|
|
243
|
+
worker_batch_size=config.worker_batch_size,
|
|
244
|
+
worker_http_port=config.worker_http_port,
|
|
245
|
+
reflect_max_iterations=config.reflect_max_iterations,
|
|
246
|
+
mental_model_refresh_concurrency=config.mental_model_refresh_concurrency,
|
|
224
247
|
)
|
|
225
248
|
config.configure_logging()
|
|
226
249
|
if not args.daemon:
|
hindsight_api/mcp_local.py
CHANGED
|
@@ -44,7 +44,6 @@ import os
|
|
|
44
44
|
import sys
|
|
45
45
|
|
|
46
46
|
from mcp.server.fastmcp import FastMCP
|
|
47
|
-
from mcp.types import Icon
|
|
48
47
|
|
|
49
48
|
from hindsight_api.config import (
|
|
50
49
|
DEFAULT_MCP_LOCAL_BANK_ID,
|
|
@@ -53,6 +52,7 @@ from hindsight_api.config import (
|
|
|
53
52
|
ENV_MCP_INSTRUCTIONS,
|
|
54
53
|
ENV_MCP_LOCAL_BANK_ID,
|
|
55
54
|
)
|
|
55
|
+
from hindsight_api.mcp_tools import MCPToolsConfig, register_mcp_tools
|
|
56
56
|
|
|
57
57
|
# Configure logging - default to warning to avoid polluting stderr during MCP init
|
|
58
58
|
# MCP clients interpret stderr output as errors, so we suppress INFO logs by default
|
|
@@ -85,9 +85,6 @@ def create_local_mcp_server(bank_id: str, memory=None) -> FastMCP:
|
|
|
85
85
|
"""
|
|
86
86
|
# Import here to avoid slow startup if just checking --help
|
|
87
87
|
from hindsight_api import MemoryEngine
|
|
88
|
-
from hindsight_api.engine.memory_engine import Budget
|
|
89
|
-
from hindsight_api.engine.response_models import VALID_RECALL_FACT_TYPES
|
|
90
|
-
from hindsight_api.models import RequestContext
|
|
91
88
|
|
|
92
89
|
# Create memory engine with pg0 embedded database if not provided
|
|
93
90
|
if memory is None:
|
|
@@ -105,55 +102,17 @@ def create_local_mcp_server(bank_id: str, memory=None) -> FastMCP:
|
|
|
105
102
|
|
|
106
103
|
mcp = FastMCP("hindsight")
|
|
107
104
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
await memory.retain_batch_async(
|
|
120
|
-
bank_id=bank_id,
|
|
121
|
-
contents=[{"content": content, "context": context}],
|
|
122
|
-
request_context=RequestContext(),
|
|
123
|
-
)
|
|
124
|
-
except Exception as e:
|
|
125
|
-
logger.error(f"Error storing memory: {e}", exc_info=True)
|
|
126
|
-
|
|
127
|
-
# Fire and forget - don't block on memory storage
|
|
128
|
-
asyncio.create_task(_retain())
|
|
129
|
-
return {"status": "accepted", "message": "Memory storage initiated"}
|
|
130
|
-
|
|
131
|
-
@mcp.tool(description=recall_description)
|
|
132
|
-
async def recall(query: str, max_tokens: int = 4096, budget: str = "low") -> dict:
|
|
133
|
-
"""
|
|
134
|
-
Args:
|
|
135
|
-
query: Natural language search query (e.g., "user's food preferences", "what projects is user working on")
|
|
136
|
-
max_tokens: Maximum tokens to return in results (default: 4096)
|
|
137
|
-
budget: Search budget level - "low", "mid", or "high" (default: "low")
|
|
138
|
-
"""
|
|
139
|
-
try:
|
|
140
|
-
# Map string budget to enum
|
|
141
|
-
budget_map = {"low": Budget.LOW, "mid": Budget.MID, "high": Budget.HIGH}
|
|
142
|
-
budget_enum = budget_map.get(budget.lower(), Budget.LOW)
|
|
143
|
-
|
|
144
|
-
search_result = await memory.recall_async(
|
|
145
|
-
bank_id=bank_id,
|
|
146
|
-
query=query,
|
|
147
|
-
fact_type=list(VALID_RECALL_FACT_TYPES),
|
|
148
|
-
budget=budget_enum,
|
|
149
|
-
max_tokens=max_tokens,
|
|
150
|
-
request_context=RequestContext(),
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
return search_result.model_dump()
|
|
154
|
-
except Exception as e:
|
|
155
|
-
logger.error(f"Error searching: {e}", exc_info=True)
|
|
156
|
-
return {"error": str(e), "results": []}
|
|
105
|
+
# Configure and register tools using shared module
|
|
106
|
+
config = MCPToolsConfig(
|
|
107
|
+
bank_id_resolver=lambda: bank_id,
|
|
108
|
+
include_bank_id_param=False, # Local MCP uses fixed bank_id
|
|
109
|
+
tools={"retain", "recall"}, # Local MCP only has retain and recall
|
|
110
|
+
retain_description=retain_description,
|
|
111
|
+
recall_description=recall_description,
|
|
112
|
+
retain_fire_and_forget=True, # Local MCP uses fire-and-forget pattern
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
register_mcp_tools(mcp, memory, config)
|
|
157
116
|
|
|
158
117
|
return mcp
|
|
159
118
|
|