hindsight-api 0.3.0__py3-none-any.whl → 0.4.0__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 (74) hide show
  1. hindsight_api/admin/cli.py +59 -0
  2. hindsight_api/alembic/versions/h3c4d5e6f7g8_mental_models_v4.py +112 -0
  3. hindsight_api/alembic/versions/i4d5e6f7g8h9_delete_opinions.py +41 -0
  4. hindsight_api/alembic/versions/j5e6f7g8h9i0_mental_model_versions.py +95 -0
  5. hindsight_api/alembic/versions/k6f7g8h9i0j1_add_directive_subtype.py +58 -0
  6. hindsight_api/alembic/versions/l7g8h9i0j1k2_add_worker_columns.py +109 -0
  7. hindsight_api/alembic/versions/m8h9i0j1k2l3_mental_model_id_to_text.py +41 -0
  8. hindsight_api/alembic/versions/n9i0j1k2l3m4_learnings_and_pinned_reflections.py +134 -0
  9. hindsight_api/alembic/versions/o0j1k2l3m4n5_migrate_mental_models_data.py +113 -0
  10. hindsight_api/alembic/versions/p1k2l3m4n5o6_new_knowledge_architecture.py +194 -0
  11. hindsight_api/alembic/versions/q2l3m4n5o6p7_fix_mental_model_fact_type.py +50 -0
  12. hindsight_api/alembic/versions/r3m4n5o6p7q8_add_reflect_response_to_reflections.py +47 -0
  13. hindsight_api/alembic/versions/s4n5o6p7q8r9_add_consolidated_at_to_memory_units.py +53 -0
  14. hindsight_api/alembic/versions/t5o6p7q8r9s0_rename_mental_models_to_observations.py +134 -0
  15. hindsight_api/alembic/versions/u6p7q8r9s0t1_mental_models_text_id.py +41 -0
  16. hindsight_api/alembic/versions/v7q8r9s0t1u2_add_max_tokens_to_mental_models.py +50 -0
  17. hindsight_api/api/http.py +1119 -93
  18. hindsight_api/api/mcp.py +11 -191
  19. hindsight_api/config.py +145 -45
  20. hindsight_api/engine/consolidation/__init__.py +5 -0
  21. hindsight_api/engine/consolidation/consolidator.py +859 -0
  22. hindsight_api/engine/consolidation/prompts.py +69 -0
  23. hindsight_api/engine/cross_encoder.py +114 -9
  24. hindsight_api/engine/directives/__init__.py +5 -0
  25. hindsight_api/engine/directives/models.py +37 -0
  26. hindsight_api/engine/embeddings.py +102 -5
  27. hindsight_api/engine/interface.py +32 -13
  28. hindsight_api/engine/llm_wrapper.py +505 -43
  29. hindsight_api/engine/memory_engine.py +2090 -1089
  30. hindsight_api/engine/mental_models/__init__.py +14 -0
  31. hindsight_api/engine/mental_models/models.py +53 -0
  32. hindsight_api/engine/reflect/__init__.py +18 -0
  33. hindsight_api/engine/reflect/agent.py +933 -0
  34. hindsight_api/engine/reflect/models.py +109 -0
  35. hindsight_api/engine/reflect/observations.py +186 -0
  36. hindsight_api/engine/reflect/prompts.py +483 -0
  37. hindsight_api/engine/reflect/tools.py +437 -0
  38. hindsight_api/engine/reflect/tools_schema.py +250 -0
  39. hindsight_api/engine/response_models.py +130 -4
  40. hindsight_api/engine/retain/bank_utils.py +79 -201
  41. hindsight_api/engine/retain/fact_extraction.py +81 -48
  42. hindsight_api/engine/retain/fact_storage.py +5 -8
  43. hindsight_api/engine/retain/link_utils.py +5 -8
  44. hindsight_api/engine/retain/orchestrator.py +1 -55
  45. hindsight_api/engine/retain/types.py +2 -2
  46. hindsight_api/engine/search/graph_retrieval.py +2 -2
  47. hindsight_api/engine/search/link_expansion_retrieval.py +164 -29
  48. hindsight_api/engine/search/mpfp_retrieval.py +1 -1
  49. hindsight_api/engine/search/retrieval.py +14 -14
  50. hindsight_api/engine/search/think_utils.py +41 -140
  51. hindsight_api/engine/search/trace.py +0 -1
  52. hindsight_api/engine/search/tracer.py +2 -5
  53. hindsight_api/engine/search/types.py +0 -3
  54. hindsight_api/engine/task_backend.py +112 -196
  55. hindsight_api/engine/utils.py +0 -151
  56. hindsight_api/extensions/__init__.py +10 -1
  57. hindsight_api/extensions/builtin/tenant.py +5 -1
  58. hindsight_api/extensions/operation_validator.py +81 -4
  59. hindsight_api/extensions/tenant.py +26 -0
  60. hindsight_api/main.py +16 -5
  61. hindsight_api/mcp_local.py +12 -53
  62. hindsight_api/mcp_tools.py +494 -0
  63. hindsight_api/models.py +0 -2
  64. hindsight_api/worker/__init__.py +11 -0
  65. hindsight_api/worker/main.py +296 -0
  66. hindsight_api/worker/poller.py +486 -0
  67. {hindsight_api-0.3.0.dist-info → hindsight_api-0.4.0.dist-info}/METADATA +12 -6
  68. hindsight_api-0.4.0.dist-info/RECORD +112 -0
  69. {hindsight_api-0.3.0.dist-info → hindsight_api-0.4.0.dist-info}/entry_points.txt +1 -0
  70. hindsight_api/engine/retain/observation_regeneration.py +0 -254
  71. hindsight_api/engine/search/observation_utils.py +0 -125
  72. hindsight_api/engine/search/scoring.py +0 -159
  73. hindsight_api-0.3.0.dist-info/RECORD +0 -82
  74. {hindsight_api-0.3.0.dist-info → hindsight_api-0.4.0.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
@@ -184,6 +184,10 @@ def main():
184
184
  reflect_llm_api_key=config.reflect_llm_api_key,
185
185
  reflect_llm_model=config.reflect_llm_model,
186
186
  reflect_llm_base_url=config.reflect_llm_base_url,
187
+ consolidation_llm_provider=config.consolidation_llm_provider,
188
+ consolidation_llm_api_key=config.consolidation_llm_api_key,
189
+ consolidation_llm_model=config.consolidation_llm_model,
190
+ consolidation_llm_base_url=config.consolidation_llm_base_url,
187
191
  embeddings_provider=config.embeddings_provider,
188
192
  embeddings_local_model=config.embeddings_local_model,
189
193
  embeddings_tei_url=config.embeddings_tei_url,
@@ -199,18 +203,20 @@ def main():
199
203
  host=args.host,
200
204
  port=args.port,
201
205
  log_level=args.log_level,
206
+ log_format=config.log_format,
202
207
  mcp_enabled=config.mcp_enabled,
203
208
  graph_retriever=config.graph_retriever,
204
209
  mpfp_top_k_neighbors=config.mpfp_top_k_neighbors,
205
210
  recall_max_concurrent=config.recall_max_concurrent,
206
211
  recall_connection_budget=config.recall_connection_budget,
207
- observation_min_facts=config.observation_min_facts,
208
- observation_top_entities=config.observation_top_entities,
209
212
  retain_max_completion_tokens=config.retain_max_completion_tokens,
210
213
  retain_chunk_size=config.retain_chunk_size,
211
214
  retain_extract_causal_links=config.retain_extract_causal_links,
212
215
  retain_extraction_mode=config.retain_extraction_mode,
216
+ retain_custom_instructions=config.retain_custom_instructions,
213
217
  retain_observations_async=config.retain_observations_async,
218
+ enable_observations=config.enable_observations,
219
+ consolidation_batch_size=config.consolidation_batch_size,
214
220
  skip_llm_verification=config.skip_llm_verification,
215
221
  lazy_reranker=config.lazy_reranker,
216
222
  run_migrations_on_startup=config.run_migrations_on_startup,
@@ -218,9 +224,14 @@ def main():
218
224
  db_pool_max_size=config.db_pool_max_size,
219
225
  db_command_timeout=config.db_command_timeout,
220
226
  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,
227
+ worker_enabled=config.worker_enabled,
228
+ worker_id=config.worker_id,
229
+ worker_poll_interval_ms=config.worker_poll_interval_ms,
230
+ worker_max_retries=config.worker_max_retries,
231
+ worker_batch_size=config.worker_batch_size,
232
+ worker_http_port=config.worker_http_port,
233
+ reflect_max_iterations=config.reflect_max_iterations,
234
+ mental_model_refresh_concurrency=config.mental_model_refresh_concurrency,
224
235
  )
225
236
  config.configure_logging()
226
237
  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