hindsight-api 0.1.0__py3-none-any.whl → 0.1.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 +10 -2
- hindsight_api/alembic/README +1 -0
- hindsight_api/alembic/env.py +146 -0
- hindsight_api/alembic/script.py.mako +28 -0
- hindsight_api/alembic/versions/5a366d414dce_initial_schema.py +274 -0
- hindsight_api/alembic/versions/b7c4d8e9f1a2_add_chunks_table.py +70 -0
- hindsight_api/alembic/versions/c8e5f2a3b4d1_add_retain_params_to_documents.py +39 -0
- hindsight_api/alembic/versions/d9f6a3b4c5e2_rename_bank_to_interactions.py +48 -0
- hindsight_api/alembic/versions/e0a1b2c3d4e5_disposition_to_3_traits.py +62 -0
- hindsight_api/alembic/versions/rename_personality_to_disposition.py +65 -0
- hindsight_api/api/http.py +84 -86
- hindsight_api/config.py +154 -0
- hindsight_api/engine/__init__.py +7 -2
- hindsight_api/engine/cross_encoder.py +219 -15
- hindsight_api/engine/embeddings.py +192 -18
- hindsight_api/engine/llm_wrapper.py +88 -139
- hindsight_api/engine/memory_engine.py +71 -51
- hindsight_api/engine/retain/bank_utils.py +2 -2
- hindsight_api/engine/retain/fact_extraction.py +1 -1
- hindsight_api/engine/search/reranking.py +6 -10
- hindsight_api/engine/search/tracer.py +1 -1
- hindsight_api/main.py +201 -0
- hindsight_api/migrations.py +7 -7
- hindsight_api/server.py +43 -0
- {hindsight_api-0.1.0.dist-info → hindsight_api-0.1.1.dist-info}/METADATA +1 -1
- {hindsight_api-0.1.0.dist-info → hindsight_api-0.1.1.dist-info}/RECORD +28 -19
- hindsight_api-0.1.1.dist-info/entry_points.txt +2 -0
- hindsight_api/cli.py +0 -127
- hindsight_api/web/__init__.py +0 -12
- hindsight_api/web/server.py +0 -109
- hindsight_api-0.1.0.dist-info/entry_points.txt +0 -2
- {hindsight_api-0.1.0.dist-info → hindsight_api-0.1.1.dist-info}/WHEEL +0 -0
|
@@ -11,17 +11,20 @@ This implements a sophisticated memory architecture that combines:
|
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
13
|
from datetime import datetime, timedelta, timezone
|
|
14
|
-
from typing import Any, Dict, List, Optional, Tuple, Union, TypedDict
|
|
14
|
+
from typing import Any, Dict, List, Optional, Tuple, Union, TypedDict, TYPE_CHECKING
|
|
15
15
|
import asyncpg
|
|
16
16
|
import asyncio
|
|
17
|
-
from .embeddings import Embeddings,
|
|
18
|
-
from .cross_encoder import CrossEncoderModel
|
|
17
|
+
from .embeddings import Embeddings, create_embeddings_from_env
|
|
18
|
+
from .cross_encoder import CrossEncoderModel, create_cross_encoder_from_env
|
|
19
19
|
import time
|
|
20
20
|
import numpy as np
|
|
21
21
|
import uuid
|
|
22
22
|
import logging
|
|
23
23
|
from pydantic import BaseModel, Field
|
|
24
24
|
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from ..config import HindsightConfig
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
class RetainContentDict(TypedDict, total=False):
|
|
27
30
|
"""Type definition for content items in retain_batch_async.
|
|
@@ -99,10 +102,10 @@ class MemoryEngine:
|
|
|
99
102
|
|
|
100
103
|
def __init__(
|
|
101
104
|
self,
|
|
102
|
-
db_url: str,
|
|
103
|
-
memory_llm_provider: str,
|
|
104
|
-
memory_llm_api_key: str,
|
|
105
|
-
memory_llm_model: str,
|
|
105
|
+
db_url: Optional[str] = None,
|
|
106
|
+
memory_llm_provider: Optional[str] = None,
|
|
107
|
+
memory_llm_api_key: Optional[str] = None,
|
|
108
|
+
memory_llm_model: Optional[str] = None,
|
|
106
109
|
memory_llm_base_url: Optional[str] = None,
|
|
107
110
|
embeddings: Optional[Embeddings] = None,
|
|
108
111
|
cross_encoder: Optional[CrossEncoderModel] = None,
|
|
@@ -115,26 +118,34 @@ class MemoryEngine:
|
|
|
115
118
|
"""
|
|
116
119
|
Initialize the temporal + semantic memory system.
|
|
117
120
|
|
|
121
|
+
All parameters are optional and will be read from environment variables if not provided.
|
|
122
|
+
See hindsight_api.config for environment variable names and defaults.
|
|
123
|
+
|
|
118
124
|
Args:
|
|
119
|
-
db_url: PostgreSQL connection URL
|
|
125
|
+
db_url: PostgreSQL connection URL. Defaults to HINDSIGHT_API_DATABASE_URL env var or "pg0".
|
|
120
126
|
Also supports pg0 URLs: "pg0" or "pg0://instance-name" or "pg0://instance-name:port"
|
|
121
|
-
memory_llm_provider: LLM provider
|
|
122
|
-
memory_llm_api_key: API key for the LLM provider.
|
|
123
|
-
memory_llm_model: Model name to
|
|
124
|
-
memory_llm_base_url: Base URL for the LLM API.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
cross_encoder: Cross-encoder model for reranking. If not provided, uses default when cross-encoder reranker is selected
|
|
129
|
-
query_analyzer: Query analyzer implementation to use. If not provided, uses TransformerQueryAnalyzer
|
|
127
|
+
memory_llm_provider: LLM provider. Defaults to HINDSIGHT_API_LLM_PROVIDER env var or "groq".
|
|
128
|
+
memory_llm_api_key: API key for the LLM provider. Defaults to HINDSIGHT_API_LLM_API_KEY env var.
|
|
129
|
+
memory_llm_model: Model name. Defaults to HINDSIGHT_API_LLM_MODEL env var.
|
|
130
|
+
memory_llm_base_url: Base URL for the LLM API. Defaults based on provider.
|
|
131
|
+
embeddings: Embeddings implementation. If not provided, created from env vars.
|
|
132
|
+
cross_encoder: Cross-encoder model. If not provided, created from env vars.
|
|
133
|
+
query_analyzer: Query analyzer implementation. If not provided, uses DateparserQueryAnalyzer.
|
|
130
134
|
pool_min_size: Minimum number of connections in the pool (default: 5)
|
|
131
135
|
pool_max_size: Maximum number of connections in the pool (default: 100)
|
|
132
|
-
|
|
133
|
-
task_backend: Custom task backend for async task execution. If not provided, uses AsyncIOQueueBackend
|
|
136
|
+
task_backend: Custom task backend. If not provided, uses AsyncIOQueueBackend.
|
|
134
137
|
run_migrations: Whether to run database migrations during initialize(). Default: True
|
|
135
138
|
"""
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
# Load config from environment for any missing parameters
|
|
140
|
+
from ..config import get_config
|
|
141
|
+
config = get_config()
|
|
142
|
+
|
|
143
|
+
# Apply defaults from config
|
|
144
|
+
db_url = db_url or config.database_url
|
|
145
|
+
memory_llm_provider = memory_llm_provider or config.llm_provider
|
|
146
|
+
memory_llm_api_key = memory_llm_api_key or config.llm_api_key
|
|
147
|
+
memory_llm_model = memory_llm_model or config.llm_model
|
|
148
|
+
memory_llm_base_url = memory_llm_base_url or config.get_llm_base_url() or None
|
|
138
149
|
# Track pg0 instance (if used)
|
|
139
150
|
self._pg0: Optional[EmbeddedPostgres] = None
|
|
140
151
|
self._pg0_instance_name: Optional[str] = None
|
|
@@ -184,11 +195,11 @@ class MemoryEngine:
|
|
|
184
195
|
# Initialize entity resolver (will be created in initialize())
|
|
185
196
|
self.entity_resolver = None
|
|
186
197
|
|
|
187
|
-
# Initialize embeddings
|
|
198
|
+
# Initialize embeddings (from env vars if not provided)
|
|
188
199
|
if embeddings is not None:
|
|
189
200
|
self.embeddings = embeddings
|
|
190
201
|
else:
|
|
191
|
-
self.embeddings =
|
|
202
|
+
self.embeddings = create_embeddings_from_env()
|
|
192
203
|
|
|
193
204
|
# Initialize query analyzer
|
|
194
205
|
if query_analyzer is not None:
|
|
@@ -319,7 +330,7 @@ class MemoryEngine:
|
|
|
319
330
|
await self._handle_reinforce_opinion(task_dict)
|
|
320
331
|
elif task_type == 'form_opinion':
|
|
321
332
|
await self._handle_form_opinion(task_dict)
|
|
322
|
-
elif task_type == '
|
|
333
|
+
elif task_type == 'batch_retain':
|
|
323
334
|
await self._handle_batch_retain(task_dict)
|
|
324
335
|
elif task_type == 'regenerate_observations':
|
|
325
336
|
await self._handle_regenerate_observations(task_dict)
|
|
@@ -414,32 +425,41 @@ class MemoryEngine:
|
|
|
414
425
|
if not was_already_running:
|
|
415
426
|
self._pg0 = pg0
|
|
416
427
|
|
|
417
|
-
def
|
|
418
|
-
"""
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
428
|
+
async def init_embeddings():
|
|
429
|
+
"""Initialize embedding model."""
|
|
430
|
+
# For local providers, run in thread pool to avoid blocking event loop
|
|
431
|
+
if self.embeddings.provider_name == "local":
|
|
432
|
+
await loop.run_in_executor(
|
|
433
|
+
None,
|
|
434
|
+
lambda: asyncio.run(self.embeddings.initialize())
|
|
435
|
+
)
|
|
436
|
+
else:
|
|
437
|
+
await self.embeddings.initialize()
|
|
438
|
+
|
|
439
|
+
async def init_cross_encoder():
|
|
440
|
+
"""Initialize cross-encoder model."""
|
|
441
|
+
cross_encoder = self._cross_encoder_reranker.cross_encoder
|
|
442
|
+
# For local providers, run in thread pool to avoid blocking event loop
|
|
443
|
+
if cross_encoder.provider_name == "local":
|
|
444
|
+
await loop.run_in_executor(
|
|
445
|
+
None,
|
|
446
|
+
lambda: asyncio.run(cross_encoder.initialize())
|
|
447
|
+
)
|
|
448
|
+
else:
|
|
449
|
+
await cross_encoder.initialize()
|
|
450
|
+
|
|
451
|
+
async def init_query_analyzer():
|
|
452
|
+
"""Initialize query analyzer model."""
|
|
453
|
+
# Query analyzer load is sync and CPU-bound
|
|
454
|
+
await loop.run_in_executor(None, self.query_analyzer.load)
|
|
455
|
+
|
|
456
|
+
# Run pg0 and all model initializations in parallel
|
|
457
|
+
await asyncio.gather(
|
|
458
|
+
start_pg0(),
|
|
459
|
+
init_embeddings(),
|
|
460
|
+
init_cross_encoder(),
|
|
461
|
+
init_query_analyzer(),
|
|
462
|
+
)
|
|
443
463
|
|
|
444
464
|
# Run database migrations if enabled
|
|
445
465
|
if self._run_migrations:
|
|
@@ -2692,7 +2712,7 @@ Guidelines:
|
|
|
2692
2712
|
],
|
|
2693
2713
|
scope="memory_think",
|
|
2694
2714
|
temperature=0.9,
|
|
2695
|
-
|
|
2715
|
+
max_completion_tokens=1000
|
|
2696
2716
|
)
|
|
2697
2717
|
llm_time = time.time() - llm_start
|
|
2698
2718
|
|
|
@@ -273,7 +273,7 @@ Merged background:"""
|
|
|
273
273
|
response_format=BackgroundMergeResponse,
|
|
274
274
|
scope="bank_background",
|
|
275
275
|
temperature=0.3,
|
|
276
|
-
|
|
276
|
+
max_completion_tokens=8192
|
|
277
277
|
)
|
|
278
278
|
logger.info(f"Successfully got structured response: background={parsed.background[:100]}")
|
|
279
279
|
|
|
@@ -291,7 +291,7 @@ Merged background:"""
|
|
|
291
291
|
messages=messages,
|
|
292
292
|
scope="bank_background",
|
|
293
293
|
temperature=0.3,
|
|
294
|
-
|
|
294
|
+
max_completion_tokens=8192
|
|
295
295
|
)
|
|
296
296
|
|
|
297
297
|
logger.info(f"LLM response for background merge (first 500 chars): {content[:500]}")
|
|
@@ -10,10 +10,8 @@ class CrossEncoderReranker:
|
|
|
10
10
|
"""
|
|
11
11
|
Neural reranking using a cross-encoder model.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- Small model (80MB)
|
|
16
|
-
- Trained for passage re-ranking
|
|
13
|
+
Configured via environment variables (see cross_encoder.py).
|
|
14
|
+
Default local model is cross-encoder/ms-marco-MiniLM-L-6-v2.
|
|
17
15
|
"""
|
|
18
16
|
|
|
19
17
|
def __init__(self, cross_encoder=None):
|
|
@@ -21,14 +19,12 @@ class CrossEncoderReranker:
|
|
|
21
19
|
Initialize cross-encoder reranker.
|
|
22
20
|
|
|
23
21
|
Args:
|
|
24
|
-
cross_encoder:
|
|
25
|
-
|
|
26
|
-
(loaded lazily for faster startup)
|
|
22
|
+
cross_encoder: CrossEncoderModel instance. If None, creates one from
|
|
23
|
+
environment variables (defaults to local provider)
|
|
27
24
|
"""
|
|
28
25
|
if cross_encoder is None:
|
|
29
|
-
from hindsight_api.engine.cross_encoder import
|
|
30
|
-
|
|
31
|
-
cross_encoder = SentenceTransformersCrossEncoder()
|
|
26
|
+
from hindsight_api.engine.cross_encoder import create_cross_encoder_from_env
|
|
27
|
+
cross_encoder = create_cross_encoder_from_env()
|
|
32
28
|
self.cross_encoder = cross_encoder
|
|
33
29
|
|
|
34
30
|
def rerank(
|
|
@@ -368,7 +368,7 @@ class SearchTracer:
|
|
|
368
368
|
|
|
369
369
|
# Extract score components (only include non-None values)
|
|
370
370
|
score_components = {}
|
|
371
|
-
for key in ["semantic_similarity", "bm25_score", "rrf_score", "recency_normalized", "frequency_normalized"]:
|
|
371
|
+
for key in ["semantic_similarity", "bm25_score", "rrf_score", "recency_normalized", "frequency_normalized", "cross_encoder_score", "cross_encoder_score_normalized"]:
|
|
372
372
|
if key in result and result[key] is not None:
|
|
373
373
|
score_components[key] = result[key]
|
|
374
374
|
|
hindsight_api/main.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for Hindsight API.
|
|
3
|
+
|
|
4
|
+
Run the server with:
|
|
5
|
+
hindsight-api
|
|
6
|
+
|
|
7
|
+
Stop with Ctrl+C.
|
|
8
|
+
"""
|
|
9
|
+
import argparse
|
|
10
|
+
import asyncio
|
|
11
|
+
import atexit
|
|
12
|
+
import os
|
|
13
|
+
import signal
|
|
14
|
+
import sys
|
|
15
|
+
import warnings
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
import uvicorn
|
|
19
|
+
|
|
20
|
+
from . import MemoryEngine
|
|
21
|
+
from .api import create_app
|
|
22
|
+
from .config import get_config, HindsightConfig
|
|
23
|
+
|
|
24
|
+
# Filter deprecation warnings from third-party libraries
|
|
25
|
+
warnings.filterwarnings("ignore", message="websockets.legacy is deprecated")
|
|
26
|
+
warnings.filterwarnings("ignore", message="websockets.server.WebSocketServerProtocol is deprecated")
|
|
27
|
+
|
|
28
|
+
# Disable tokenizers parallelism to avoid warnings
|
|
29
|
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
30
|
+
|
|
31
|
+
# Global reference for cleanup
|
|
32
|
+
_memory: Optional[MemoryEngine] = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _cleanup():
|
|
36
|
+
"""Synchronous cleanup function to stop resources on exit."""
|
|
37
|
+
global _memory
|
|
38
|
+
if _memory is not None and _memory._pg0 is not None:
|
|
39
|
+
try:
|
|
40
|
+
loop = asyncio.new_event_loop()
|
|
41
|
+
loop.run_until_complete(_memory._pg0.stop())
|
|
42
|
+
loop.close()
|
|
43
|
+
print("\npg0 stopped.")
|
|
44
|
+
except Exception as e:
|
|
45
|
+
print(f"\nError stopping pg0: {e}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _signal_handler(signum, frame):
|
|
49
|
+
"""Handle SIGINT/SIGTERM to ensure cleanup."""
|
|
50
|
+
print(f"\nReceived signal {signum}, shutting down...")
|
|
51
|
+
_cleanup()
|
|
52
|
+
sys.exit(0)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
"""Main entry point for the CLI."""
|
|
57
|
+
global _memory
|
|
58
|
+
|
|
59
|
+
# Load configuration from environment (for CLI args defaults)
|
|
60
|
+
config = get_config()
|
|
61
|
+
|
|
62
|
+
parser = argparse.ArgumentParser(
|
|
63
|
+
prog="hindsight-api",
|
|
64
|
+
description="Hindsight API Server",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Server options
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--host", default=config.host,
|
|
70
|
+
help=f"Host to bind to (default: {config.host}, env: HINDSIGHT_API_HOST)"
|
|
71
|
+
)
|
|
72
|
+
parser.add_argument(
|
|
73
|
+
"--port", type=int, default=config.port,
|
|
74
|
+
help=f"Port to bind to (default: {config.port}, env: HINDSIGHT_API_PORT)"
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--log-level", default=config.log_level,
|
|
78
|
+
choices=["critical", "error", "warning", "info", "debug", "trace"],
|
|
79
|
+
help=f"Log level (default: {config.log_level}, env: HINDSIGHT_API_LOG_LEVEL)"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Development options
|
|
83
|
+
parser.add_argument(
|
|
84
|
+
"--reload", action="store_true",
|
|
85
|
+
help="Enable auto-reload on code changes (development only)"
|
|
86
|
+
)
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--workers", type=int, default=1,
|
|
89
|
+
help="Number of worker processes (default: 1)"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Access log options
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
"--access-log", action="store_true",
|
|
95
|
+
help="Enable access log"
|
|
96
|
+
)
|
|
97
|
+
parser.add_argument(
|
|
98
|
+
"--no-access-log", dest="access_log", action="store_false",
|
|
99
|
+
help="Disable access log (default)"
|
|
100
|
+
)
|
|
101
|
+
parser.set_defaults(access_log=False)
|
|
102
|
+
|
|
103
|
+
# Proxy options
|
|
104
|
+
parser.add_argument(
|
|
105
|
+
"--proxy-headers", action="store_true",
|
|
106
|
+
help="Enable X-Forwarded-Proto, X-Forwarded-For headers"
|
|
107
|
+
)
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
"--forwarded-allow-ips", default=None,
|
|
110
|
+
help="Comma separated list of IPs to trust with proxy headers"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# SSL options
|
|
114
|
+
parser.add_argument(
|
|
115
|
+
"--ssl-keyfile", default=None,
|
|
116
|
+
help="SSL key file"
|
|
117
|
+
)
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
"--ssl-certfile", default=None,
|
|
120
|
+
help="SSL certificate file"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
args = parser.parse_args()
|
|
124
|
+
|
|
125
|
+
# Configure Python logging based on log level
|
|
126
|
+
# Update config with CLI override if provided
|
|
127
|
+
if args.log_level != config.log_level:
|
|
128
|
+
config = HindsightConfig(
|
|
129
|
+
database_url=config.database_url,
|
|
130
|
+
llm_provider=config.llm_provider,
|
|
131
|
+
llm_api_key=config.llm_api_key,
|
|
132
|
+
llm_model=config.llm_model,
|
|
133
|
+
llm_base_url=config.llm_base_url,
|
|
134
|
+
embeddings_provider=config.embeddings_provider,
|
|
135
|
+
embeddings_local_model=config.embeddings_local_model,
|
|
136
|
+
embeddings_tei_url=config.embeddings_tei_url,
|
|
137
|
+
reranker_provider=config.reranker_provider,
|
|
138
|
+
reranker_local_model=config.reranker_local_model,
|
|
139
|
+
reranker_tei_url=config.reranker_tei_url,
|
|
140
|
+
host=args.host,
|
|
141
|
+
port=args.port,
|
|
142
|
+
log_level=args.log_level,
|
|
143
|
+
mcp_enabled=config.mcp_enabled,
|
|
144
|
+
)
|
|
145
|
+
config.configure_logging()
|
|
146
|
+
|
|
147
|
+
# Register cleanup handlers
|
|
148
|
+
atexit.register(_cleanup)
|
|
149
|
+
signal.signal(signal.SIGINT, _signal_handler)
|
|
150
|
+
signal.signal(signal.SIGTERM, _signal_handler)
|
|
151
|
+
|
|
152
|
+
# Create MemoryEngine (reads configuration from environment)
|
|
153
|
+
_memory = MemoryEngine()
|
|
154
|
+
|
|
155
|
+
# Create FastAPI app
|
|
156
|
+
app = create_app(
|
|
157
|
+
memory=_memory,
|
|
158
|
+
http_api_enabled=True,
|
|
159
|
+
mcp_api_enabled=config.mcp_enabled,
|
|
160
|
+
mcp_mount_path="/mcp",
|
|
161
|
+
initialize_memory=True,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Prepare uvicorn config
|
|
165
|
+
uvicorn_config = {
|
|
166
|
+
"app": app,
|
|
167
|
+
"host": args.host,
|
|
168
|
+
"port": args.port,
|
|
169
|
+
"log_level": args.log_level,
|
|
170
|
+
"access_log": args.access_log,
|
|
171
|
+
"proxy_headers": args.proxy_headers,
|
|
172
|
+
"ws": "wsproto", # Use wsproto instead of websockets to avoid deprecation warnings
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# Add optional parameters if provided
|
|
176
|
+
if args.reload:
|
|
177
|
+
uvicorn_config["reload"] = True
|
|
178
|
+
if args.workers > 1:
|
|
179
|
+
uvicorn_config["workers"] = args.workers
|
|
180
|
+
if args.forwarded_allow_ips:
|
|
181
|
+
uvicorn_config["forwarded_allow_ips"] = args.forwarded_allow_ips
|
|
182
|
+
if args.ssl_keyfile:
|
|
183
|
+
uvicorn_config["ssl_keyfile"] = args.ssl_keyfile
|
|
184
|
+
if args.ssl_certfile:
|
|
185
|
+
uvicorn_config["ssl_certfile"] = args.ssl_certfile
|
|
186
|
+
|
|
187
|
+
print(f"\nStarting Hindsight API...")
|
|
188
|
+
print(f" URL: http://{args.host}:{args.port}")
|
|
189
|
+
print(f" Database: {config.database_url}")
|
|
190
|
+
print(f" LLM: {config.llm_provider} / {config.llm_model}")
|
|
191
|
+
print(f" Embeddings: {config.embeddings_provider}")
|
|
192
|
+
print(f" Reranker: {config.reranker_provider}")
|
|
193
|
+
if config.mcp_enabled:
|
|
194
|
+
print(f" MCP: enabled at /mcp")
|
|
195
|
+
print()
|
|
196
|
+
|
|
197
|
+
uvicorn.run(**uvicorn_config)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == "__main__":
|
|
201
|
+
main()
|
hindsight_api/migrations.py
CHANGED
|
@@ -88,11 +88,11 @@ def run_migrations(database_url: str, script_location: Optional[str] = None) ->
|
|
|
88
88
|
try:
|
|
89
89
|
# Determine script location
|
|
90
90
|
if script_location is None:
|
|
91
|
-
# Default: use the alembic directory
|
|
92
|
-
# This file is in:
|
|
93
|
-
#
|
|
94
|
-
|
|
95
|
-
script_location = str(
|
|
91
|
+
# Default: use the alembic directory inside the hindsight_api package
|
|
92
|
+
# This file is in: hindsight_api/migrations.py
|
|
93
|
+
# Alembic is in: hindsight_api/alembic/
|
|
94
|
+
package_dir = Path(__file__).parent
|
|
95
|
+
script_location = str(package_dir / "alembic")
|
|
96
96
|
|
|
97
97
|
script_path = Path(script_location)
|
|
98
98
|
if not script_path.exists():
|
|
@@ -162,8 +162,8 @@ def check_migration_status(database_url: Optional[str] = None, script_location:
|
|
|
162
162
|
|
|
163
163
|
# Get head revision from migration scripts
|
|
164
164
|
if script_location is None:
|
|
165
|
-
|
|
166
|
-
script_location = str(
|
|
165
|
+
package_dir = Path(__file__).parent
|
|
166
|
+
script_location = str(package_dir / "alembic")
|
|
167
167
|
|
|
168
168
|
script_path = Path(script_location)
|
|
169
169
|
if not script_path.exists():
|
hindsight_api/server.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI server for Hindsight API.
|
|
3
|
+
|
|
4
|
+
This module provides the ASGI app for uvicorn import string usage:
|
|
5
|
+
uvicorn hindsight_api.server:app
|
|
6
|
+
|
|
7
|
+
For CLI usage, use the hindsight-api command instead.
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import warnings
|
|
11
|
+
|
|
12
|
+
# Filter deprecation warnings from third-party libraries
|
|
13
|
+
warnings.filterwarnings("ignore", message="websockets.legacy is deprecated")
|
|
14
|
+
warnings.filterwarnings("ignore", message="websockets.server.WebSocketServerProtocol is deprecated")
|
|
15
|
+
|
|
16
|
+
from hindsight_api import MemoryEngine
|
|
17
|
+
from hindsight_api.api import create_app
|
|
18
|
+
from hindsight_api.config import get_config
|
|
19
|
+
|
|
20
|
+
# Disable tokenizers parallelism to avoid warnings
|
|
21
|
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
22
|
+
|
|
23
|
+
# Load configuration and configure logging
|
|
24
|
+
config = get_config()
|
|
25
|
+
config.configure_logging()
|
|
26
|
+
|
|
27
|
+
# Create app at module level (required for uvicorn import string)
|
|
28
|
+
# MemoryEngine reads configuration from environment variables automatically
|
|
29
|
+
_memory = MemoryEngine()
|
|
30
|
+
|
|
31
|
+
# Create unified app with both HTTP and optionally MCP
|
|
32
|
+
app = create_app(
|
|
33
|
+
memory=_memory,
|
|
34
|
+
http_api_enabled=True,
|
|
35
|
+
mcp_api_enabled=config.mcp_enabled,
|
|
36
|
+
mcp_mount_path="/mcp"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
# When run directly, delegate to the CLI
|
|
42
|
+
from hindsight_api.main import main
|
|
43
|
+
main()
|
|
@@ -1,31 +1,42 @@
|
|
|
1
|
-
hindsight_api/__init__.py,sha256=
|
|
2
|
-
hindsight_api/
|
|
1
|
+
hindsight_api/__init__.py,sha256=gPkRHnMATZqBgc7b-Mcro4f_gY9W0BlnGBE0zg1t_IY,1139
|
|
2
|
+
hindsight_api/config.py,sha256=V29wimS9xCOP20lXL6VUJZZKB46YbD_8TV9LHUtJKng,5186
|
|
3
|
+
hindsight_api/main.py,sha256=Tl_w0kM-bcB0Y37_rKv_RzGOj_ReMuio20JtSunz9aE,6079
|
|
3
4
|
hindsight_api/metrics.py,sha256=j4-eeqVjjcGQxAxS_GgEaBNm10KdUxrGS_I2d1IM1hY,7255
|
|
4
|
-
hindsight_api/migrations.py,sha256=
|
|
5
|
+
hindsight_api/migrations.py,sha256=nSbU37ZszVZifYJTU_vEXfusTxWaUea9dRi7-Ao3-SQ,7349
|
|
5
6
|
hindsight_api/models.py,sha256=ncIi8agl3PVk7ffyXlJosFym1jJZZhVmXTguZ3EnAEc,12515
|
|
6
7
|
hindsight_api/pg0.py,sha256=_GuVccjE0_OthaK72_GXC3NkcI9q3MkU3i8m685mDfQ,13542
|
|
8
|
+
hindsight_api/server.py,sha256=C_w3_xzKRVkSsFkujhMz9S4nDlAc0eOFClGIefTCZIk,1263
|
|
9
|
+
hindsight_api/alembic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
|
|
10
|
+
hindsight_api/alembic/env.py,sha256=i0gc3GN2rWidtqRp-vdvnJTIR0zl1X4Uokqp_WnTsAo,4837
|
|
11
|
+
hindsight_api/alembic/script.py.mako,sha256=04kgeBtNMa4cCnG8CfQcKt6P6rnloIfj8wy0u_DBydM,704
|
|
12
|
+
hindsight_api/alembic/versions/5a366d414dce_initial_schema.py,sha256=qTrvBWf-48sL2kx7eNq1BoDjF_QfePGq2-wKrK__wWk,16774
|
|
13
|
+
hindsight_api/alembic/versions/b7c4d8e9f1a2_add_chunks_table.py,sha256=g0edMh_4w8WDVcnB-GA1l-RdreYJkV-kJWZX9VJHYp8,2592
|
|
14
|
+
hindsight_api/alembic/versions/c8e5f2a3b4d1_add_retain_params_to_documents.py,sha256=brlAuHO5ygarkfsjW-p70NiVDGfj98BjczaptGPyYNg,1159
|
|
15
|
+
hindsight_api/alembic/versions/d9f6a3b4c5e2_rename_bank_to_interactions.py,sha256=pLz_s5kV3IHQ3TrpvHmtXQOkdfK4iQ07w93dEsXu3Qk,1536
|
|
16
|
+
hindsight_api/alembic/versions/e0a1b2c3d4e5_disposition_to_3_traits.py,sha256=fykAU4V4jq1wVpCBoH0TzvSFyeyxZtk-6hpaZAbrFxU,2327
|
|
17
|
+
hindsight_api/alembic/versions/rename_personality_to_disposition.py,sha256=xlp-Is96e3TvCwSqhPctQRqLBHcl3dvDmlzbCMZGw1A,2196
|
|
7
18
|
hindsight_api/api/__init__.py,sha256=dIJqoygqYaEgm-Bd7qwZ4UTnb9UPyXtlDxZnQpvVC0o,2946
|
|
8
|
-
hindsight_api/api/http.py,sha256=
|
|
19
|
+
hindsight_api/api/http.py,sha256=_vy70aWQ5TJlhXgIw6PbH0mrqat8_J_0rQwY73h24Pg,69863
|
|
9
20
|
hindsight_api/api/mcp.py,sha256=DldtC8LQUguAbJSSHnEBS65wC-AQBjcF8rl51xUR1gQ,7808
|
|
10
|
-
hindsight_api/engine/__init__.py,sha256=
|
|
11
|
-
hindsight_api/engine/cross_encoder.py,sha256=
|
|
21
|
+
hindsight_api/engine/__init__.py,sha256=W_y6iAHgu-HUpvdXlI6JJ0KO42wVkrWvcUSJZqTCj_M,1406
|
|
22
|
+
hindsight_api/engine/cross_encoder.py,sha256=D6iTTXv23R1n8O_IoAsBCsCbevZTNhnJSt4AirADksg,10789
|
|
12
23
|
hindsight_api/engine/db_utils.py,sha256=p1Ne70wPP327xdPI_XjMfnagilY8sknbkhEIZuED6DU,2724
|
|
13
|
-
hindsight_api/engine/embeddings.py,sha256=
|
|
24
|
+
hindsight_api/engine/embeddings.py,sha256=RdK9A3lUjp1FZFArllhTgKo70Pot4ZUEJ1Pw70BpNmk,10218
|
|
14
25
|
hindsight_api/engine/entity_resolver.py,sha256=w5DPCuYNsK4GF8Qe3oY7jCKcOT1WYx2h0YD1nX0QRtA,23184
|
|
15
|
-
hindsight_api/engine/llm_wrapper.py,sha256=
|
|
16
|
-
hindsight_api/engine/memory_engine.py,sha256=
|
|
26
|
+
hindsight_api/engine/llm_wrapper.py,sha256=v7XWms1uCj9Bev-Txy4iGhofZTQmA29aaudZCSD3UHs,18672
|
|
27
|
+
hindsight_api/engine/memory_engine.py,sha256=QQz-5vD_Qh_0qFtNoWcynUQesw_Hyh8TZ3qzq4tTB7g,134144
|
|
17
28
|
hindsight_api/engine/query_analyzer.py,sha256=K0QCg7tsbqtwC7TR5wt3FPoP8QDuZsX9r0Zljc8nnYo,19733
|
|
18
29
|
hindsight_api/engine/response_models.py,sha256=e-_vE1zAVFLpkl6SeHIYvHcQ4Z-AaOdq0jjjhh8yHk4,8683
|
|
19
30
|
hindsight_api/engine/task_backend.py,sha256=ojxMC9PeHdnkWVs2ozeqycjI_1mmpkDa0_Qfej9AHrg,7287
|
|
20
31
|
hindsight_api/engine/utils.py,sha256=VAjpZSbdiwhlE6cDlYfTt_-5hIJ--0xtfixETK0LPSk,6910
|
|
21
32
|
hindsight_api/engine/retain/__init__.py,sha256=L_QuR1YLHsJ7OCmVFNsZe8WDjbsTTHL-wCiUXtw1aUE,1230
|
|
22
|
-
hindsight_api/engine/retain/bank_utils.py,sha256=
|
|
33
|
+
hindsight_api/engine/retain/bank_utils.py,sha256=r-_411UuuQwxlIxEteA2GE_Wr9BdsNt0OVCjaJgcISU,14143
|
|
23
34
|
hindsight_api/engine/retain/chunk_storage.py,sha256=rjmfnllS185tmjJGkMjWZ9q_6hJO4N6Ll9jgPx6f5xo,2081
|
|
24
35
|
hindsight_api/engine/retain/deduplication.py,sha256=9YXgVI_m1Mtz5Cv46ZceCEs0GwpLqTPHrZ-vlWlXk6I,3313
|
|
25
36
|
hindsight_api/engine/retain/embedding_processing.py,sha256=cHTt3rPvDCWBWVPfSeg6bwH8HoXYGmP4bvS21boNONI,1734
|
|
26
37
|
hindsight_api/engine/retain/embedding_utils.py,sha256=Q24h_iw6pRAW2vDWPvauWY1o3bXLzW3eWvSxDALDiE0,1588
|
|
27
38
|
hindsight_api/engine/retain/entity_processing.py,sha256=F_6yYjf7Me5khg-X57ZW4wK5BBAmzMpry-TXwVFQZ-8,2658
|
|
28
|
-
hindsight_api/engine/retain/fact_extraction.py,sha256=
|
|
39
|
+
hindsight_api/engine/retain/fact_extraction.py,sha256=dZ0FYepvndUfWTowGxM5vrrF0NV_LIIGsANA6Ze6Mf4,50818
|
|
29
40
|
hindsight_api/engine/retain/fact_storage.py,sha256=rKJiWr_1lrqyB6s0mTCnTiHVZIUbCfd3zigNwISnVPI,5637
|
|
30
41
|
hindsight_api/engine/retain/link_creation.py,sha256=rkYKO73dWBL8BbRBeiwNgHzwrU-sKWUjmrgLIxr3LiA,3280
|
|
31
42
|
hindsight_api/engine/retain/link_utils.py,sha256=g80xUMr45na8UX505VnoZ7jSKdvyRgxDHnDoINd8GNI,28828
|
|
@@ -35,17 +46,15 @@ hindsight_api/engine/retain/types.py,sha256=JJ4t8Qtp64kTPB9CKOFDXqdos2i8GZXmJZNz
|
|
|
35
46
|
hindsight_api/engine/search/__init__.py,sha256=7X6U10bVw0JRWxQdE5RCfVpawDlSUldi1mPoCzY0V0A,363
|
|
36
47
|
hindsight_api/engine/search/fusion.py,sha256=so6LU7kWRR-VJd1Pxlu8idRJ7P2WLCoDwXUnb8jQifo,4309
|
|
37
48
|
hindsight_api/engine/search/observation_utils.py,sha256=SPrDx6M0daJ_zLLkk78GlQIG3EL7DqMKSu_etKerUfU,4331
|
|
38
|
-
hindsight_api/engine/search/reranking.py,sha256=
|
|
49
|
+
hindsight_api/engine/search/reranking.py,sha256=znjN78VfuT4PqprhGRPd2B9WtVMhAU5A662s0xCvU7g,3329
|
|
39
50
|
hindsight_api/engine/search/retrieval.py,sha256=i6pffxsfYkTjQYS8iNMC60owMr__a8i7khkHb4LUWuI,19661
|
|
40
51
|
hindsight_api/engine/search/scoring.py,sha256=feFPalpbIMndp8j2Ab0zvu7fRq3c43Wmzrjw3piQ0eM,5167
|
|
41
52
|
hindsight_api/engine/search/temporal_extraction.py,sha256=5klrZdza3mkgk5A15_m_j4IIfOHMc6fUR9UJuzLa790,1812
|
|
42
53
|
hindsight_api/engine/search/think_utils.py,sha256=VJJXFmBg03yO4Mg--UBMlTQW9IZOj2eyTZztjzhT8F8,11315
|
|
43
54
|
hindsight_api/engine/search/trace.py,sha256=Hx-siW9yAfqZoK9LG6esbed0vQuHMNsGxSvCg4FK6-4,11042
|
|
44
|
-
hindsight_api/engine/search/tracer.py,sha256=
|
|
55
|
+
hindsight_api/engine/search/tracer.py,sha256=LQ78knpMxyZmPUvm3PJNN2opCyA-LpB47JZ84n0g2pw,15074
|
|
45
56
|
hindsight_api/engine/search/types.py,sha256=qIeHW_gT7f291vteTZXygAM8oAaPp2dq6uEdvOyOwzs,5488
|
|
46
|
-
hindsight_api/
|
|
47
|
-
hindsight_api/
|
|
48
|
-
hindsight_api-0.1.
|
|
49
|
-
hindsight_api-0.1.
|
|
50
|
-
hindsight_api-0.1.0.dist-info/entry_points.txt,sha256=53Fn-VxtkqreZhOPTJB_FupH7e5GyiMY3gzEp22d8xs,57
|
|
51
|
-
hindsight_api-0.1.0.dist-info/RECORD,,
|
|
57
|
+
hindsight_api-0.1.1.dist-info/METADATA,sha256=tM55UIWOxwNgYSbLncDbmd-5cpoiwnnrRet55e53gPw,1466
|
|
58
|
+
hindsight_api-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
59
|
+
hindsight_api-0.1.1.dist-info/entry_points.txt,sha256=ZDj1gJCi6Ga6VLdPgRSrRizQ4dUTreefjeG_tO1CuHk,58
|
|
60
|
+
hindsight_api-0.1.1.dist-info/RECORD,,
|