hindsight-api 0.2.1__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.
- hindsight_api/admin/__init__.py +1 -0
- hindsight_api/admin/cli.py +311 -0
- hindsight_api/alembic/versions/f1a2b3c4d5e6_add_memory_links_composite_index.py +44 -0
- hindsight_api/alembic/versions/g2a3b4c5d6e7_add_tags_column.py +48 -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 +1406 -118
- hindsight_api/api/mcp.py +11 -196
- hindsight_api/config.py +359 -27
- hindsight_api/engine/consolidation/__init__.py +5 -0
- hindsight_api/engine/consolidation/consolidator.py +859 -0
- hindsight_api/engine/consolidation/prompts.py +69 -0
- hindsight_api/engine/cross_encoder.py +706 -88
- hindsight_api/engine/db_budget.py +284 -0
- hindsight_api/engine/db_utils.py +11 -0
- hindsight_api/engine/directives/__init__.py +5 -0
- hindsight_api/engine/directives/models.py +37 -0
- hindsight_api/engine/embeddings.py +553 -29
- hindsight_api/engine/entity_resolver.py +8 -5
- hindsight_api/engine/interface.py +40 -17
- hindsight_api/engine/llm_wrapper.py +744 -68
- hindsight_api/engine/memory_engine.py +2505 -1017
- hindsight_api/engine/mental_models/__init__.py +14 -0
- hindsight_api/engine/mental_models/models.py +53 -0
- hindsight_api/engine/query_analyzer.py +4 -3
- 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 +168 -4
- hindsight_api/engine/retain/bank_utils.py +79 -201
- hindsight_api/engine/retain/fact_extraction.py +424 -195
- hindsight_api/engine/retain/fact_storage.py +35 -12
- hindsight_api/engine/retain/link_utils.py +29 -24
- hindsight_api/engine/retain/orchestrator.py +24 -43
- hindsight_api/engine/retain/types.py +11 -2
- hindsight_api/engine/search/graph_retrieval.py +43 -14
- hindsight_api/engine/search/link_expansion_retrieval.py +391 -0
- hindsight_api/engine/search/mpfp_retrieval.py +362 -117
- hindsight_api/engine/search/reranking.py +2 -2
- hindsight_api/engine/search/retrieval.py +848 -201
- hindsight_api/engine/search/tags.py +172 -0
- hindsight_api/engine/search/think_utils.py +42 -141
- hindsight_api/engine/search/trace.py +12 -1
- hindsight_api/engine/search/tracer.py +26 -6
- hindsight_api/engine/search/types.py +21 -3
- hindsight_api/engine/task_backend.py +113 -106
- hindsight_api/engine/utils.py +1 -152
- hindsight_api/extensions/__init__.py +10 -1
- hindsight_api/extensions/builtin/tenant.py +5 -1
- hindsight_api/extensions/context.py +10 -1
- hindsight_api/extensions/operation_validator.py +81 -4
- hindsight_api/extensions/tenant.py +26 -0
- hindsight_api/main.py +69 -6
- hindsight_api/mcp_local.py +12 -53
- hindsight_api/mcp_tools.py +494 -0
- hindsight_api/metrics.py +433 -48
- hindsight_api/migrations.py +141 -1
- hindsight_api/models.py +3 -3
- hindsight_api/pg0.py +53 -0
- hindsight_api/server.py +39 -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.2.1.dist-info → hindsight_api-0.4.0.dist-info}/METADATA +16 -6
- hindsight_api-0.4.0.dist-info/RECORD +112 -0
- {hindsight_api-0.2.1.dist-info → hindsight_api-0.4.0.dist-info}/entry_points.txt +2 -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.2.1.dist-info/RECORD +0 -75
- {hindsight_api-0.2.1.dist-info → hindsight_api-0.4.0.dist-info}/WHEEL +0 -0
|
@@ -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
|
@@ -23,7 +23,7 @@ import uvicorn
|
|
|
23
23
|
from . import MemoryEngine
|
|
24
24
|
from .api import create_app
|
|
25
25
|
from .banner import print_banner
|
|
26
|
-
from .config import HindsightConfig, get_config
|
|
26
|
+
from .config import DEFAULT_WORKERS, ENV_WORKERS, HindsightConfig, get_config
|
|
27
27
|
from .daemon import (
|
|
28
28
|
DEFAULT_DAEMON_PORT,
|
|
29
29
|
DEFAULT_IDLE_TIMEOUT,
|
|
@@ -95,7 +95,12 @@ def main():
|
|
|
95
95
|
|
|
96
96
|
# Development options
|
|
97
97
|
parser.add_argument("--reload", action="store_true", help="Enable auto-reload on code changes (development only)")
|
|
98
|
-
parser.add_argument(
|
|
98
|
+
parser.add_argument(
|
|
99
|
+
"--workers",
|
|
100
|
+
type=int,
|
|
101
|
+
default=int(os.getenv(ENV_WORKERS, str(DEFAULT_WORKERS))),
|
|
102
|
+
help=f"Number of worker processes (env: {ENV_WORKERS}, default: {DEFAULT_WORKERS})",
|
|
103
|
+
)
|
|
99
104
|
|
|
100
105
|
# Access log options
|
|
101
106
|
parser.add_argument("--access-log", action="store_true", help="Enable access log")
|
|
@@ -171,21 +176,62 @@ def main():
|
|
|
171
176
|
llm_base_url=config.llm_base_url,
|
|
172
177
|
llm_max_concurrent=config.llm_max_concurrent,
|
|
173
178
|
llm_timeout=config.llm_timeout,
|
|
179
|
+
retain_llm_provider=config.retain_llm_provider,
|
|
180
|
+
retain_llm_api_key=config.retain_llm_api_key,
|
|
181
|
+
retain_llm_model=config.retain_llm_model,
|
|
182
|
+
retain_llm_base_url=config.retain_llm_base_url,
|
|
183
|
+
reflect_llm_provider=config.reflect_llm_provider,
|
|
184
|
+
reflect_llm_api_key=config.reflect_llm_api_key,
|
|
185
|
+
reflect_llm_model=config.reflect_llm_model,
|
|
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,
|
|
174
191
|
embeddings_provider=config.embeddings_provider,
|
|
175
192
|
embeddings_local_model=config.embeddings_local_model,
|
|
176
193
|
embeddings_tei_url=config.embeddings_tei_url,
|
|
194
|
+
embeddings_openai_base_url=config.embeddings_openai_base_url,
|
|
195
|
+
embeddings_cohere_base_url=config.embeddings_cohere_base_url,
|
|
177
196
|
reranker_provider=config.reranker_provider,
|
|
178
197
|
reranker_local_model=config.reranker_local_model,
|
|
179
198
|
reranker_tei_url=config.reranker_tei_url,
|
|
199
|
+
reranker_tei_batch_size=config.reranker_tei_batch_size,
|
|
200
|
+
reranker_tei_max_concurrent=config.reranker_tei_max_concurrent,
|
|
201
|
+
reranker_max_candidates=config.reranker_max_candidates,
|
|
202
|
+
reranker_cohere_base_url=config.reranker_cohere_base_url,
|
|
180
203
|
host=args.host,
|
|
181
204
|
port=args.port,
|
|
182
205
|
log_level=args.log_level,
|
|
206
|
+
log_format=config.log_format,
|
|
183
207
|
mcp_enabled=config.mcp_enabled,
|
|
184
208
|
graph_retriever=config.graph_retriever,
|
|
185
|
-
|
|
186
|
-
|
|
209
|
+
mpfp_top_k_neighbors=config.mpfp_top_k_neighbors,
|
|
210
|
+
recall_max_concurrent=config.recall_max_concurrent,
|
|
211
|
+
recall_connection_budget=config.recall_connection_budget,
|
|
212
|
+
retain_max_completion_tokens=config.retain_max_completion_tokens,
|
|
213
|
+
retain_chunk_size=config.retain_chunk_size,
|
|
214
|
+
retain_extract_causal_links=config.retain_extract_causal_links,
|
|
215
|
+
retain_extraction_mode=config.retain_extraction_mode,
|
|
216
|
+
retain_custom_instructions=config.retain_custom_instructions,
|
|
217
|
+
retain_observations_async=config.retain_observations_async,
|
|
218
|
+
enable_observations=config.enable_observations,
|
|
219
|
+
consolidation_batch_size=config.consolidation_batch_size,
|
|
187
220
|
skip_llm_verification=config.skip_llm_verification,
|
|
188
221
|
lazy_reranker=config.lazy_reranker,
|
|
222
|
+
run_migrations_on_startup=config.run_migrations_on_startup,
|
|
223
|
+
db_pool_min_size=config.db_pool_min_size,
|
|
224
|
+
db_pool_max_size=config.db_pool_max_size,
|
|
225
|
+
db_command_timeout=config.db_command_timeout,
|
|
226
|
+
db_acquire_timeout=config.db_acquire_timeout,
|
|
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,
|
|
189
235
|
)
|
|
190
236
|
config.configure_logging()
|
|
191
237
|
if not args.daemon:
|
|
@@ -211,7 +257,11 @@ def main():
|
|
|
211
257
|
logging.info(f"Loaded tenant extension: {tenant_extension.__class__.__name__}")
|
|
212
258
|
|
|
213
259
|
# Create MemoryEngine (reads configuration from environment)
|
|
214
|
-
_memory = MemoryEngine(
|
|
260
|
+
_memory = MemoryEngine(
|
|
261
|
+
operation_validator=operation_validator,
|
|
262
|
+
tenant_extension=tenant_extension,
|
|
263
|
+
run_migrations=config.run_migrations_on_startup,
|
|
264
|
+
)
|
|
215
265
|
|
|
216
266
|
# Set extension context on tenant extension (needed for schema provisioning)
|
|
217
267
|
if tenant_extension:
|
|
@@ -238,14 +288,27 @@ def main():
|
|
|
238
288
|
app = idle_middleware
|
|
239
289
|
|
|
240
290
|
# Prepare uvicorn config
|
|
291
|
+
# When using workers or reload, we must use import string so each worker can import the app
|
|
292
|
+
use_import_string = args.workers > 1 or args.reload
|
|
293
|
+
# Check for uvloop availability
|
|
294
|
+
try:
|
|
295
|
+
import uvloop # noqa: F401
|
|
296
|
+
|
|
297
|
+
loop_impl = "uvloop"
|
|
298
|
+
print("uvloop available, will use for event loop")
|
|
299
|
+
except ImportError:
|
|
300
|
+
loop_impl = "asyncio"
|
|
301
|
+
print("uvloop not installed, using default asyncio event loop")
|
|
302
|
+
|
|
241
303
|
uvicorn_config = {
|
|
242
|
-
"app": app,
|
|
304
|
+
"app": "hindsight_api.server:app" if use_import_string else app,
|
|
243
305
|
"host": args.host,
|
|
244
306
|
"port": args.port,
|
|
245
307
|
"log_level": args.log_level,
|
|
246
308
|
"access_log": args.access_log,
|
|
247
309
|
"proxy_headers": args.proxy_headers,
|
|
248
310
|
"ws": "wsproto", # Use wsproto instead of websockets to avoid deprecation warnings
|
|
311
|
+
"loop": loop_impl, # Explicitly set event loop implementation
|
|
249
312
|
}
|
|
250
313
|
|
|
251
314
|
# Add optional parameters if provided
|
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
|
|