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.
Files changed (75) hide show
  1. hindsight_api/__init__.py +1 -1
  2. hindsight_api/admin/cli.py +59 -0
  3. hindsight_api/alembic/versions/h3c4d5e6f7g8_mental_models_v4.py +112 -0
  4. hindsight_api/alembic/versions/i4d5e6f7g8h9_delete_opinions.py +41 -0
  5. hindsight_api/alembic/versions/j5e6f7g8h9i0_mental_model_versions.py +95 -0
  6. hindsight_api/alembic/versions/k6f7g8h9i0j1_add_directive_subtype.py +58 -0
  7. hindsight_api/alembic/versions/l7g8h9i0j1k2_add_worker_columns.py +109 -0
  8. hindsight_api/alembic/versions/m8h9i0j1k2l3_mental_model_id_to_text.py +41 -0
  9. hindsight_api/alembic/versions/n9i0j1k2l3m4_learnings_and_pinned_reflections.py +134 -0
  10. hindsight_api/alembic/versions/o0j1k2l3m4n5_migrate_mental_models_data.py +113 -0
  11. hindsight_api/alembic/versions/p1k2l3m4n5o6_new_knowledge_architecture.py +194 -0
  12. hindsight_api/alembic/versions/q2l3m4n5o6p7_fix_mental_model_fact_type.py +50 -0
  13. hindsight_api/alembic/versions/r3m4n5o6p7q8_add_reflect_response_to_reflections.py +47 -0
  14. hindsight_api/alembic/versions/s4n5o6p7q8r9_add_consolidated_at_to_memory_units.py +53 -0
  15. hindsight_api/alembic/versions/t5o6p7q8r9s0_rename_mental_models_to_observations.py +134 -0
  16. hindsight_api/alembic/versions/u6p7q8r9s0t1_mental_models_text_id.py +41 -0
  17. hindsight_api/alembic/versions/v7q8r9s0t1u2_add_max_tokens_to_mental_models.py +50 -0
  18. hindsight_api/api/http.py +1120 -93
  19. hindsight_api/api/mcp.py +11 -191
  20. hindsight_api/config.py +174 -46
  21. hindsight_api/engine/consolidation/__init__.py +5 -0
  22. hindsight_api/engine/consolidation/consolidator.py +926 -0
  23. hindsight_api/engine/consolidation/prompts.py +77 -0
  24. hindsight_api/engine/cross_encoder.py +153 -22
  25. hindsight_api/engine/directives/__init__.py +5 -0
  26. hindsight_api/engine/directives/models.py +37 -0
  27. hindsight_api/engine/embeddings.py +136 -13
  28. hindsight_api/engine/interface.py +32 -13
  29. hindsight_api/engine/llm_wrapper.py +505 -43
  30. hindsight_api/engine/memory_engine.py +2101 -1094
  31. hindsight_api/engine/mental_models/__init__.py +14 -0
  32. hindsight_api/engine/mental_models/models.py +53 -0
  33. hindsight_api/engine/reflect/__init__.py +18 -0
  34. hindsight_api/engine/reflect/agent.py +933 -0
  35. hindsight_api/engine/reflect/models.py +109 -0
  36. hindsight_api/engine/reflect/observations.py +186 -0
  37. hindsight_api/engine/reflect/prompts.py +483 -0
  38. hindsight_api/engine/reflect/tools.py +437 -0
  39. hindsight_api/engine/reflect/tools_schema.py +250 -0
  40. hindsight_api/engine/response_models.py +130 -4
  41. hindsight_api/engine/retain/bank_utils.py +79 -201
  42. hindsight_api/engine/retain/fact_extraction.py +81 -48
  43. hindsight_api/engine/retain/fact_storage.py +5 -8
  44. hindsight_api/engine/retain/link_utils.py +5 -8
  45. hindsight_api/engine/retain/orchestrator.py +1 -55
  46. hindsight_api/engine/retain/types.py +2 -2
  47. hindsight_api/engine/search/graph_retrieval.py +2 -2
  48. hindsight_api/engine/search/link_expansion_retrieval.py +164 -29
  49. hindsight_api/engine/search/mpfp_retrieval.py +1 -1
  50. hindsight_api/engine/search/retrieval.py +14 -14
  51. hindsight_api/engine/search/think_utils.py +41 -140
  52. hindsight_api/engine/search/trace.py +0 -1
  53. hindsight_api/engine/search/tracer.py +2 -5
  54. hindsight_api/engine/search/types.py +0 -3
  55. hindsight_api/engine/task_backend.py +112 -196
  56. hindsight_api/engine/utils.py +0 -151
  57. hindsight_api/extensions/__init__.py +10 -1
  58. hindsight_api/extensions/builtin/tenant.py +11 -4
  59. hindsight_api/extensions/operation_validator.py +81 -4
  60. hindsight_api/extensions/tenant.py +26 -0
  61. hindsight_api/main.py +28 -5
  62. hindsight_api/mcp_local.py +12 -53
  63. hindsight_api/mcp_tools.py +494 -0
  64. hindsight_api/models.py +0 -2
  65. hindsight_api/worker/__init__.py +11 -0
  66. hindsight_api/worker/main.py +296 -0
  67. hindsight_api/worker/poller.py +486 -0
  68. {hindsight_api-0.3.0.dist-info → hindsight_api-0.4.1.dist-info}/METADATA +12 -6
  69. hindsight_api-0.4.1.dist-info/RECORD +112 -0
  70. {hindsight_api-0.3.0.dist-info → hindsight_api-0.4.1.dist-info}/entry_points.txt +1 -0
  71. hindsight_api/engine/retain/observation_regeneration.py +0 -254
  72. hindsight_api/engine/search/observation_utils.py +0 -125
  73. hindsight_api/engine/search/scoring.py +0 -159
  74. hindsight_api-0.3.0.dist-info/RECORD +0 -82
  75. {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. validate_retain/validate_recall/validate_reflect (pre-operation)
220
+ 1. validate_* (pre-operation)
189
221
  2. [operation executes]
190
- 3. on_retain_complete/on_recall_complete/on_reflect_complete (post-operation)
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
- task_backend=config.task_backend,
222
- task_backend_memory_batch_size=config.task_backend_memory_batch_size,
223
- task_backend_memory_batch_interval=config.task_backend_memory_batch_interval,
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:
@@ -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
- @mcp.tool(description=retain_description)
109
- async def retain(content: str, context: str = "general") -> dict:
110
- """
111
- Args:
112
- content: The fact/memory to store (be specific and include relevant details)
113
- context: Category for the memory (e.g., 'preferences', 'work', 'hobbies', 'family'). Default: 'general'
114
- """
115
- import asyncio
116
-
117
- async def _retain():
118
- try:
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