hindsight-api 0.1.4__py3-none-any.whl → 0.1.5__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/api/mcp.py +1 -5
- hindsight_api/config.py +9 -0
- hindsight_api/engine/cross_encoder.py +1 -6
- hindsight_api/engine/llm_wrapper.py +13 -9
- hindsight_api/engine/memory_engine.py +71 -59
- hindsight_api/engine/search/__init__.py +15 -1
- hindsight_api/engine/search/graph_retrieval.py +235 -0
- hindsight_api/engine/search/mpfp_retrieval.py +454 -0
- hindsight_api/engine/search/retrieval.py +337 -163
- hindsight_api/engine/search/trace.py +1 -0
- hindsight_api/engine/search/tracer.py +8 -3
- hindsight_api/engine/search/types.py +4 -1
- hindsight_api/pg0.py +54 -326
- {hindsight_api-0.1.4.dist-info → hindsight_api-0.1.5.dist-info}/METADATA +6 -5
- {hindsight_api-0.1.4.dist-info → hindsight_api-0.1.5.dist-info}/RECORD +17 -15
- {hindsight_api-0.1.4.dist-info → hindsight_api-0.1.5.dist-info}/WHEEL +0 -0
- {hindsight_api-0.1.4.dist-info → hindsight_api-0.1.5.dist-info}/entry_points.txt +0 -0
hindsight_api/pg0.py
CHANGED
|
@@ -1,373 +1,116 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import json
|
|
3
2
|
import logging
|
|
4
|
-
import os
|
|
5
|
-
import platform
|
|
6
|
-
import re
|
|
7
|
-
import shutil
|
|
8
|
-
import stat
|
|
9
|
-
import subprocess
|
|
10
|
-
from pathlib import Path
|
|
11
3
|
from typing import Optional
|
|
12
4
|
|
|
13
|
-
import
|
|
5
|
+
from pg0 import Pg0
|
|
14
6
|
|
|
15
7
|
logger = logging.getLogger(__name__)
|
|
16
8
|
|
|
17
|
-
# pg0 configuration
|
|
18
|
-
BINARY_NAME = "pg0"
|
|
19
9
|
DEFAULT_PORT = 5555
|
|
20
10
|
DEFAULT_USERNAME = "hindsight"
|
|
21
11
|
DEFAULT_PASSWORD = "hindsight"
|
|
22
12
|
DEFAULT_DATABASE = "hindsight"
|
|
23
13
|
|
|
24
14
|
|
|
25
|
-
def get_platform_binary_name() -> str:
|
|
26
|
-
"""Get the appropriate binary name for the current platform.
|
|
27
|
-
|
|
28
|
-
Supported platforms:
|
|
29
|
-
- macOS ARM64 (darwin-aarch64)
|
|
30
|
-
- Linux x86_64 (gnu)
|
|
31
|
-
- Linux ARM64 (gnu)
|
|
32
|
-
- Windows x86_64
|
|
33
|
-
"""
|
|
34
|
-
system = platform.system().lower()
|
|
35
|
-
machine = platform.machine().lower()
|
|
36
|
-
|
|
37
|
-
# Normalize architecture names
|
|
38
|
-
if machine in ("x86_64", "amd64"):
|
|
39
|
-
arch = "x86_64"
|
|
40
|
-
elif machine in ("arm64", "aarch64"):
|
|
41
|
-
arch = "aarch64"
|
|
42
|
-
else:
|
|
43
|
-
raise RuntimeError(
|
|
44
|
-
f"Embedded PostgreSQL is not supported on architecture: {machine}. "
|
|
45
|
-
f"Supported architectures: x86_64/amd64 (Linux, Windows), aarch64/arm64 (macOS, Linux)"
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
if system == "darwin" and arch == "aarch64":
|
|
49
|
-
return "pg0-darwin-aarch64"
|
|
50
|
-
elif system == "linux" and arch == "x86_64":
|
|
51
|
-
return "pg0-linux-x86_64-gnu"
|
|
52
|
-
elif system == "linux" and arch == "aarch64":
|
|
53
|
-
return "pg0-linux-aarch64-gnu"
|
|
54
|
-
elif system == "windows" and arch == "x86_64":
|
|
55
|
-
return "pg0-windows-x86_64.exe"
|
|
56
|
-
else:
|
|
57
|
-
raise RuntimeError(
|
|
58
|
-
f"Embedded PostgreSQL is not supported on {system}-{arch}. "
|
|
59
|
-
f"Supported platforms: darwin-aarch64 (macOS ARM), linux-x86_64-gnu, linux-aarch64-gnu, windows-x86_64"
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def get_download_url(
|
|
64
|
-
version: str = "latest",
|
|
65
|
-
repo: str = "vectorize-io/pg0",
|
|
66
|
-
) -> str:
|
|
67
|
-
"""Get the download URL for pg0 binary."""
|
|
68
|
-
binary_name = get_platform_binary_name()
|
|
69
|
-
|
|
70
|
-
if version == "latest":
|
|
71
|
-
return f"https://github.com/{repo}/releases/latest/download/{binary_name}"
|
|
72
|
-
else:
|
|
73
|
-
return f"https://github.com/{repo}/releases/download/{version}/{binary_name}"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _find_pg0_binary() -> Optional[Path]:
|
|
77
|
-
"""Find pg0 binary in PATH or default install location."""
|
|
78
|
-
# First check PATH
|
|
79
|
-
pg0_in_path = shutil.which("pg0")
|
|
80
|
-
if pg0_in_path:
|
|
81
|
-
return Path(pg0_in_path)
|
|
82
|
-
|
|
83
|
-
# Fall back to default install location
|
|
84
|
-
default_path = Path.home() / ".hindsight" / "bin" / "pg0"
|
|
85
|
-
if default_path.exists() and os.access(default_path, os.X_OK):
|
|
86
|
-
return default_path
|
|
87
|
-
|
|
88
|
-
return None
|
|
89
|
-
|
|
90
|
-
|
|
91
15
|
class EmbeddedPostgres:
|
|
92
|
-
"""
|
|
93
|
-
Manages an embedded PostgreSQL server instance using pg0.
|
|
94
|
-
|
|
95
|
-
This class handles:
|
|
96
|
-
- Finding or downloading the pg0 CLI
|
|
97
|
-
- Starting/stopping the PostgreSQL server
|
|
98
|
-
- Getting the connection URI
|
|
99
|
-
|
|
100
|
-
Example:
|
|
101
|
-
pg = EmbeddedPostgres()
|
|
102
|
-
await pg.ensure_installed()
|
|
103
|
-
await pg.start()
|
|
104
|
-
uri = await pg.get_uri()
|
|
105
|
-
# ... use uri with asyncpg ...
|
|
106
|
-
await pg.stop()
|
|
107
|
-
"""
|
|
16
|
+
"""Manages an embedded PostgreSQL server instance using pg0-embedded."""
|
|
108
17
|
|
|
109
18
|
def __init__(
|
|
110
19
|
self,
|
|
111
|
-
version: str = "latest",
|
|
112
20
|
port: int = DEFAULT_PORT,
|
|
113
21
|
username: str = DEFAULT_USERNAME,
|
|
114
22
|
password: str = DEFAULT_PASSWORD,
|
|
115
23
|
database: str = DEFAULT_DATABASE,
|
|
116
24
|
name: str = "hindsight",
|
|
25
|
+
**kwargs,
|
|
117
26
|
):
|
|
118
|
-
"""
|
|
119
|
-
Initialize the embedded PostgreSQL manager.
|
|
120
|
-
|
|
121
|
-
Args:
|
|
122
|
-
version: Version of pg0 to download if not found. Defaults to "latest"
|
|
123
|
-
port: Port to listen on. Defaults to 5555
|
|
124
|
-
username: Username for the database. Defaults to "hindsight"
|
|
125
|
-
password: Password for the database. Defaults to "hindsight"
|
|
126
|
-
database: Database name to create. Defaults to "hindsight"
|
|
127
|
-
name: Instance name for pg0. Defaults to "hindsight"
|
|
128
|
-
"""
|
|
129
|
-
self.version = version
|
|
130
27
|
self.port = port
|
|
131
28
|
self.username = username
|
|
132
29
|
self.password = password
|
|
133
30
|
self.database = database
|
|
134
31
|
self.name = name
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def is_installed(self) -> bool:
|
|
148
|
-
"""Check if pg0 is available (in PATH or installed)."""
|
|
149
|
-
self._binary_path = _find_pg0_binary()
|
|
150
|
-
return self._binary_path is not None
|
|
151
|
-
|
|
152
|
-
async def ensure_installed(self) -> None:
|
|
153
|
-
"""
|
|
154
|
-
Ensure pg0 is available.
|
|
155
|
-
|
|
156
|
-
Checks PATH and default location. If not found, raises an error
|
|
157
|
-
instructing the user to install pg0 manually.
|
|
158
|
-
"""
|
|
159
|
-
if self.is_installed():
|
|
160
|
-
logger.debug(f"pg0 found at {self._binary_path}")
|
|
161
|
-
return
|
|
162
|
-
|
|
163
|
-
raise RuntimeError(
|
|
164
|
-
"pg0 is not installed. Please install it manually:\n"
|
|
165
|
-
" curl -fsSL https://github.com/vectorize-io/pg0/releases/latest/download/pg0-linux-amd64 -o ~/.local/bin/pg0 && chmod +x ~/.local/bin/pg0\n"
|
|
166
|
-
"Or visit: https://github.com/vectorize-io/pg0/releases"
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
def _run_command(self, *args: str, capture_output: bool = True) -> subprocess.CompletedProcess:
|
|
170
|
-
"""Run a pg0 command synchronously."""
|
|
171
|
-
cmd = [str(self.binary_path), *args]
|
|
172
|
-
return subprocess.run(cmd, capture_output=capture_output, text=True)
|
|
173
|
-
|
|
174
|
-
async def _run_command_async(self, *args: str, timeout: int = 120) -> tuple[int, str, str]:
|
|
175
|
-
"""Run a pg0 command asynchronously."""
|
|
176
|
-
cmd = [str(self.binary_path), *args]
|
|
177
|
-
|
|
178
|
-
def run_sync():
|
|
179
|
-
try:
|
|
180
|
-
result = subprocess.run(
|
|
181
|
-
cmd,
|
|
182
|
-
stdin=subprocess.DEVNULL,
|
|
183
|
-
stdout=subprocess.PIPE,
|
|
184
|
-
stderr=subprocess.PIPE,
|
|
185
|
-
text=True,
|
|
186
|
-
timeout=timeout,
|
|
187
|
-
)
|
|
188
|
-
return result.returncode, result.stdout, result.stderr
|
|
189
|
-
except subprocess.TimeoutExpired:
|
|
190
|
-
return 1, "", "Command timed out"
|
|
191
|
-
|
|
192
|
-
loop = asyncio.get_event_loop()
|
|
193
|
-
return await loop.run_in_executor(None, run_sync)
|
|
194
|
-
|
|
195
|
-
def _extract_uri_from_output(self, output: str) -> Optional[str]:
|
|
196
|
-
"""Extract the PostgreSQL URI from pg0 start output."""
|
|
197
|
-
match = re.search(r"Connection URI:\s*(postgresql://[^\s]+)", output)
|
|
198
|
-
if match:
|
|
199
|
-
return match.group(1)
|
|
200
|
-
return None
|
|
201
|
-
|
|
202
|
-
async def _get_version(self) -> str:
|
|
203
|
-
"""Get the pg0 version."""
|
|
204
|
-
returncode, stdout, stderr = await self._run_command_async("--version", timeout=10)
|
|
205
|
-
if returncode == 0 and stdout:
|
|
206
|
-
return stdout.strip()
|
|
207
|
-
return "unknown"
|
|
32
|
+
self._pg0: Optional[Pg0] = None
|
|
33
|
+
|
|
34
|
+
def _get_pg0(self) -> Pg0:
|
|
35
|
+
if self._pg0 is None:
|
|
36
|
+
self._pg0 = Pg0(
|
|
37
|
+
name=self.name,
|
|
38
|
+
port=self.port,
|
|
39
|
+
username=self.username,
|
|
40
|
+
password=self.password,
|
|
41
|
+
database=self.database,
|
|
42
|
+
)
|
|
43
|
+
return self._pg0
|
|
208
44
|
|
|
209
45
|
async def start(self, max_retries: int = 3, retry_delay: float = 2.0) -> str:
|
|
210
|
-
"""
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
Args:
|
|
214
|
-
max_retries: Maximum number of start attempts (default: 3)
|
|
215
|
-
retry_delay: Initial delay between retries in seconds (default: 2.0)
|
|
216
|
-
|
|
217
|
-
Returns:
|
|
218
|
-
The connection URI for the started server.
|
|
219
|
-
|
|
220
|
-
Raises:
|
|
221
|
-
RuntimeError: If the server fails to start after all retries.
|
|
222
|
-
"""
|
|
223
|
-
if not self.is_installed():
|
|
224
|
-
raise RuntimeError("pg0 is not installed. Call ensure_installed() first.")
|
|
225
|
-
|
|
226
|
-
# Log pg0 version
|
|
227
|
-
version = await self._get_version()
|
|
228
|
-
logger.info(f"Starting embedded PostgreSQL with pg0 {version} (name: {self.name}, port: {self.port})...")
|
|
46
|
+
"""Start the PostgreSQL server with retry logic."""
|
|
47
|
+
logger.info(f"Starting embedded PostgreSQL (name: {self.name}, port: {self.port})...")
|
|
229
48
|
|
|
49
|
+
pg0 = self._get_pg0()
|
|
230
50
|
last_error = None
|
|
231
|
-
for attempt in range(1, max_retries + 1):
|
|
232
|
-
returncode, stdout, stderr = await self._run_command_async(
|
|
233
|
-
"start",
|
|
234
|
-
"--name", self.name,
|
|
235
|
-
"--port", str(self.port),
|
|
236
|
-
"--username", self.username,
|
|
237
|
-
"--password", self.password,
|
|
238
|
-
"--database", self.database,
|
|
239
|
-
timeout=300,
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
# Try to extract URI from output
|
|
243
|
-
uri = self._extract_uri_from_output(stdout)
|
|
244
|
-
if uri:
|
|
245
|
-
logger.info(f"PostgreSQL started on port {self.port}")
|
|
246
|
-
return uri
|
|
247
51
|
|
|
248
|
-
|
|
52
|
+
for attempt in range(1, max_retries + 1):
|
|
249
53
|
try:
|
|
250
|
-
|
|
54
|
+
loop = asyncio.get_event_loop()
|
|
55
|
+
info = await loop.run_in_executor(None, pg0.start)
|
|
251
56
|
logger.info(f"PostgreSQL started on port {self.port}")
|
|
57
|
+
# Construct URI manually since pg0-embedded may return None
|
|
58
|
+
uri = info.uri if info and info.uri else f"postgresql://{self.username}:{self.password}@localhost:{self.port}/{self.database}"
|
|
252
59
|
return uri
|
|
253
|
-
except
|
|
254
|
-
|
|
60
|
+
except Exception as e:
|
|
61
|
+
last_error = str(e)
|
|
62
|
+
if attempt < max_retries:
|
|
63
|
+
delay = retry_delay * (2 ** (attempt - 1))
|
|
64
|
+
logger.debug(f"pg0 start attempt {attempt}/{max_retries} failed: {last_error}")
|
|
65
|
+
logger.debug(f"Retrying in {delay:.1f}s...")
|
|
66
|
+
await asyncio.sleep(delay)
|
|
67
|
+
else:
|
|
68
|
+
logger.debug(f"pg0 start attempt {attempt}/{max_retries} failed: {last_error}")
|
|
255
69
|
|
|
256
|
-
# Start failed, log and retry
|
|
257
|
-
last_error = stderr or f"pg0 start returned exit code {returncode}"
|
|
258
|
-
if attempt < max_retries:
|
|
259
|
-
delay = retry_delay * (2 ** (attempt - 1))
|
|
260
|
-
logger.debug(f"pg0 start attempt {attempt}/{max_retries} failed: {last_error.strip()}")
|
|
261
|
-
logger.debug(f"Retrying in {delay:.1f}s...")
|
|
262
|
-
await asyncio.sleep(delay)
|
|
263
|
-
else:
|
|
264
|
-
logger.debug(f"pg0 start attempt {attempt}/{max_retries} failed: {last_error.strip()}")
|
|
265
|
-
|
|
266
|
-
# All retries exhausted - fail
|
|
267
70
|
raise RuntimeError(
|
|
268
71
|
f"Failed to start embedded PostgreSQL after {max_retries} attempts. "
|
|
269
|
-
f"Last error: {last_error
|
|
72
|
+
f"Last error: {last_error}"
|
|
270
73
|
)
|
|
271
74
|
|
|
272
75
|
async def stop(self) -> None:
|
|
273
76
|
"""Stop the PostgreSQL server."""
|
|
274
|
-
|
|
275
|
-
return
|
|
276
|
-
|
|
77
|
+
pg0 = self._get_pg0()
|
|
277
78
|
logger.info(f"Stopping embedded PostgreSQL (name: {self.name})...")
|
|
278
79
|
|
|
279
|
-
returncode, stdout, stderr = await self._run_command_async("stop", "--name", self.name)
|
|
280
|
-
|
|
281
|
-
if returncode != 0:
|
|
282
|
-
if "not running" in stderr.lower():
|
|
283
|
-
return
|
|
284
|
-
raise RuntimeError(f"Failed to stop PostgreSQL: {stderr}")
|
|
285
|
-
|
|
286
|
-
logger.info("Embedded PostgreSQL stopped")
|
|
287
|
-
|
|
288
|
-
async def _get_info(self) -> dict:
|
|
289
|
-
"""Get info from pg0 using the `info -o json` command."""
|
|
290
|
-
if not self.is_installed():
|
|
291
|
-
raise RuntimeError("pg0 is not installed.")
|
|
292
|
-
|
|
293
|
-
returncode, stdout, stderr = await self._run_command_async(
|
|
294
|
-
"info", "--name", self.name, "-o", "json"
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
if returncode != 0:
|
|
298
|
-
raise RuntimeError(f"Failed to get PostgreSQL info: {stderr}")
|
|
299
|
-
|
|
300
80
|
try:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
81
|
+
loop = asyncio.get_event_loop()
|
|
82
|
+
await loop.run_in_executor(None, pg0.stop)
|
|
83
|
+
logger.info("Embedded PostgreSQL stopped")
|
|
84
|
+
except Exception as e:
|
|
85
|
+
if "not running" in str(e).lower():
|
|
86
|
+
return
|
|
87
|
+
raise RuntimeError(f"Failed to stop PostgreSQL: {e}")
|
|
304
88
|
|
|
305
89
|
async def get_uri(self) -> str:
|
|
306
90
|
"""Get the connection URI for the PostgreSQL server."""
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
91
|
+
pg0 = self._get_pg0()
|
|
92
|
+
loop = asyncio.get_event_loop()
|
|
93
|
+
info = await loop.run_in_executor(None, pg0.info)
|
|
94
|
+
# Construct URI manually since pg0-embedded may return None
|
|
95
|
+
uri = info.uri if info and info.uri else f"postgresql://{self.username}:{self.password}@localhost:{self.port}/{self.database}"
|
|
311
96
|
return uri
|
|
312
97
|
|
|
313
|
-
async def status(self) -> dict:
|
|
314
|
-
"""Get the status of the PostgreSQL server."""
|
|
315
|
-
if not self.is_installed():
|
|
316
|
-
return {"installed": False, "running": False}
|
|
317
|
-
|
|
318
|
-
try:
|
|
319
|
-
info = await self._get_info()
|
|
320
|
-
return {
|
|
321
|
-
"installed": True,
|
|
322
|
-
"running": info.get("running", False),
|
|
323
|
-
"uri": info.get("uri"),
|
|
324
|
-
}
|
|
325
|
-
except RuntimeError:
|
|
326
|
-
return {"installed": True, "running": False}
|
|
327
|
-
|
|
328
98
|
async def is_running(self) -> bool:
|
|
329
99
|
"""Check if the PostgreSQL server is currently running."""
|
|
330
|
-
if not self.is_installed():
|
|
331
|
-
return False
|
|
332
100
|
try:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
101
|
+
pg0 = self._get_pg0()
|
|
102
|
+
loop = asyncio.get_event_loop()
|
|
103
|
+
info = await loop.run_in_executor(None, pg0.info)
|
|
104
|
+
return info is not None and info.running
|
|
105
|
+
except Exception:
|
|
336
106
|
return False
|
|
337
107
|
|
|
338
108
|
async def ensure_running(self) -> str:
|
|
339
|
-
"""
|
|
340
|
-
Ensure the PostgreSQL server is running.
|
|
341
|
-
|
|
342
|
-
Installs if needed, starts if not running.
|
|
343
|
-
|
|
344
|
-
Returns:
|
|
345
|
-
The connection URI.
|
|
346
|
-
"""
|
|
347
|
-
await self.ensure_installed()
|
|
348
|
-
|
|
109
|
+
"""Ensure the PostgreSQL server is running, starting it if needed."""
|
|
349
110
|
if await self.is_running():
|
|
350
111
|
return await self.get_uri()
|
|
351
|
-
|
|
352
112
|
return await self.start()
|
|
353
113
|
|
|
354
|
-
def uninstall(self) -> None:
|
|
355
|
-
"""Remove the pg0 binary (only if we installed it)."""
|
|
356
|
-
default_path = Path.home() / ".hindsight" / "bin" / "pg0"
|
|
357
|
-
if default_path.exists():
|
|
358
|
-
default_path.unlink()
|
|
359
|
-
logger.info(f"Removed {default_path}")
|
|
360
|
-
|
|
361
|
-
def clear_data(self) -> None:
|
|
362
|
-
"""Remove all PostgreSQL data (destructive!)."""
|
|
363
|
-
result = self._run_command("drop", "--name", self.name, "--force")
|
|
364
|
-
if result.returncode == 0:
|
|
365
|
-
logger.info(f"Dropped pg0 instance {self.name}")
|
|
366
|
-
else:
|
|
367
|
-
logger.warning(f"Failed to drop pg0 instance {self.name}: {result.stderr}")
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
# Convenience functions
|
|
371
114
|
|
|
372
115
|
_default_instance: Optional[EmbeddedPostgres] = None
|
|
373
116
|
|
|
@@ -375,33 +118,18 @@ _default_instance: Optional[EmbeddedPostgres] = None
|
|
|
375
118
|
def get_embedded_postgres() -> EmbeddedPostgres:
|
|
376
119
|
"""Get or create the default EmbeddedPostgres instance."""
|
|
377
120
|
global _default_instance
|
|
378
|
-
|
|
379
121
|
if _default_instance is None:
|
|
380
122
|
_default_instance = EmbeddedPostgres()
|
|
381
|
-
|
|
382
123
|
return _default_instance
|
|
383
124
|
|
|
384
125
|
|
|
385
126
|
async def start_embedded_postgres() -> str:
|
|
386
|
-
"""
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
Downloads, installs, and starts PostgreSQL in one call.
|
|
390
|
-
|
|
391
|
-
Returns:
|
|
392
|
-
Connection URI string
|
|
393
|
-
|
|
394
|
-
Example:
|
|
395
|
-
db_url = await start_embedded_postgres()
|
|
396
|
-
conn = await asyncpg.connect(db_url)
|
|
397
|
-
"""
|
|
398
|
-
pg = get_embedded_postgres()
|
|
399
|
-
return await pg.ensure_running()
|
|
127
|
+
"""Quick start function for embedded PostgreSQL."""
|
|
128
|
+
return await get_embedded_postgres().ensure_running()
|
|
400
129
|
|
|
401
130
|
|
|
402
131
|
async def stop_embedded_postgres() -> None:
|
|
403
132
|
"""Stop the default embedded PostgreSQL instance."""
|
|
404
133
|
global _default_instance
|
|
405
|
-
|
|
406
134
|
if _default_instance:
|
|
407
135
|
await _default_instance.stop()
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hindsight-api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Temporal + Semantic + Entity Memory System for AI agents using PostgreSQL
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: alembic>=1.17.1
|
|
7
7
|
Requires-Dist: asyncpg>=0.29.0
|
|
8
8
|
Requires-Dist: dateparser>=1.2.2
|
|
9
9
|
Requires-Dist: fastapi[standard]>=0.120.3
|
|
10
|
-
Requires-Dist: fastmcp>=2.
|
|
10
|
+
Requires-Dist: fastmcp>=2.3.0
|
|
11
11
|
Requires-Dist: google-genai>=1.0.0
|
|
12
12
|
Requires-Dist: greenlet>=3.2.4
|
|
13
13
|
Requires-Dist: httpx>=0.27.0
|
|
@@ -17,17 +17,18 @@ Requires-Dist: opentelemetry-api>=1.20.0
|
|
|
17
17
|
Requires-Dist: opentelemetry-exporter-prometheus>=0.41b0
|
|
18
18
|
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.41b0
|
|
19
19
|
Requires-Dist: opentelemetry-sdk>=1.20.0
|
|
20
|
+
Requires-Dist: pg0-embedded>=0.1.0
|
|
20
21
|
Requires-Dist: pgvector>=0.4.1
|
|
21
22
|
Requires-Dist: psycopg2-binary>=2.9.11
|
|
22
23
|
Requires-Dist: pydantic>=2.0.0
|
|
23
24
|
Requires-Dist: python-dateutil>=2.8.0
|
|
24
25
|
Requires-Dist: python-dotenv>=1.0.0
|
|
25
26
|
Requires-Dist: rich>=13.0.0
|
|
26
|
-
Requires-Dist: sentence-transformers
|
|
27
|
+
Requires-Dist: sentence-transformers<3.3.0,>=3.0.0
|
|
27
28
|
Requires-Dist: sqlalchemy>=2.0.44
|
|
28
29
|
Requires-Dist: tiktoken>=0.12.0
|
|
29
|
-
Requires-Dist: torch
|
|
30
|
-
Requires-Dist: transformers
|
|
30
|
+
Requires-Dist: torch<2.6.0,>=2.0.0
|
|
31
|
+
Requires-Dist: transformers<4.46.0,>=4.30.0
|
|
31
32
|
Requires-Dist: uvicorn>=0.38.0
|
|
32
33
|
Requires-Dist: wsproto>=1.0.0
|
|
33
34
|
Provides-Extra: test
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
hindsight_api/__init__.py,sha256=gPkRHnMATZqBgc7b-Mcro4f_gY9W0BlnGBE0zg1t_IY,1139
|
|
2
2
|
hindsight_api/banner.py,sha256=0dsNMXZSuOlj5lkzFykYB19BBOaePgzDTPydPEFel9M,4604
|
|
3
|
-
hindsight_api/config.py,sha256=
|
|
3
|
+
hindsight_api/config.py,sha256=rZveBJXViU2q7TrcY5KQnVCN3WpqXAtbp8kLMgjgy-U,5502
|
|
4
4
|
hindsight_api/main.py,sha256=aPftkauWHDaC7We0OF08EsDYuwjBHMZTPznoan-nxnc,6131
|
|
5
5
|
hindsight_api/metrics.py,sha256=j4-eeqVjjcGQxAxS_GgEaBNm10KdUxrGS_I2d1IM1hY,7255
|
|
6
6
|
hindsight_api/migrations.py,sha256=nSbU37ZszVZifYJTU_vEXfusTxWaUea9dRi7-Ao3-SQ,7349
|
|
7
7
|
hindsight_api/models.py,sha256=ncIi8agl3PVk7ffyXlJosFym1jJZZhVmXTguZ3EnAEc,12515
|
|
8
|
-
hindsight_api/pg0.py,sha256=
|
|
8
|
+
hindsight_api/pg0.py,sha256=cGAnMif5mFeBV2XCmOPKsDajU6o0L-qud7VOKt0gchk,4786
|
|
9
9
|
hindsight_api/server.py,sha256=C_w3_xzKRVkSsFkujhMz9S4nDlAc0eOFClGIefTCZIk,1263
|
|
10
10
|
hindsight_api/alembic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
|
|
11
11
|
hindsight_api/alembic/env.py,sha256=i0gc3GN2rWidtqRp-vdvnJTIR0zl1X4Uokqp_WnTsAo,4837
|
|
@@ -18,14 +18,14 @@ hindsight_api/alembic/versions/e0a1b2c3d4e5_disposition_to_3_traits.py,sha256=fy
|
|
|
18
18
|
hindsight_api/alembic/versions/rename_personality_to_disposition.py,sha256=xlp-Is96e3TvCwSqhPctQRqLBHcl3dvDmlzbCMZGw1A,2196
|
|
19
19
|
hindsight_api/api/__init__.py,sha256=dIJqoygqYaEgm-Bd7qwZ4UTnb9UPyXtlDxZnQpvVC0o,2946
|
|
20
20
|
hindsight_api/api/http.py,sha256=G23QBZw097boDBNYeox_MdfLWdmDUZczrakTvGTzb3Q,71195
|
|
21
|
-
hindsight_api/api/mcp.py,sha256=
|
|
21
|
+
hindsight_api/api/mcp.py,sha256=Ugkb4LtGAvi0IvO2wLvJzK62KUGTOmNpYEt1f3R-674,7584
|
|
22
22
|
hindsight_api/engine/__init__.py,sha256=W_y6iAHgu-HUpvdXlI6JJ0KO42wVkrWvcUSJZqTCj_M,1406
|
|
23
|
-
hindsight_api/engine/cross_encoder.py,sha256=
|
|
23
|
+
hindsight_api/engine/cross_encoder.py,sha256=0LfuhhvL2wmafAxH0VytcmVAV0X5RzrfZKskOEVj8jI,10489
|
|
24
24
|
hindsight_api/engine/db_utils.py,sha256=p1Ne70wPP327xdPI_XjMfnagilY8sknbkhEIZuED6DU,2724
|
|
25
25
|
hindsight_api/engine/embeddings.py,sha256=RdK9A3lUjp1FZFArllhTgKo70Pot4ZUEJ1Pw70BpNmk,10218
|
|
26
26
|
hindsight_api/engine/entity_resolver.py,sha256=w5DPCuYNsK4GF8Qe3oY7jCKcOT1WYx2h0YD1nX0QRtA,23184
|
|
27
|
-
hindsight_api/engine/llm_wrapper.py,sha256=
|
|
28
|
-
hindsight_api/engine/memory_engine.py,sha256=
|
|
27
|
+
hindsight_api/engine/llm_wrapper.py,sha256=SEzgGJRMVCOwt67J5Ggjyayk-7fWiD7OLbwYLDjsdOw,21401
|
|
28
|
+
hindsight_api/engine/memory_engine.py,sha256=saE8flEFNIP9I0XQ7HiNBwo2-hK63e5MVGGEVQxV7oU,135905
|
|
29
29
|
hindsight_api/engine/query_analyzer.py,sha256=K0QCg7tsbqtwC7TR5wt3FPoP8QDuZsX9r0Zljc8nnYo,19733
|
|
30
30
|
hindsight_api/engine/response_models.py,sha256=e-_vE1zAVFLpkl6SeHIYvHcQ4Z-AaOdq0jjjhh8yHk4,8683
|
|
31
31
|
hindsight_api/engine/task_backend.py,sha256=ojxMC9PeHdnkWVs2ozeqycjI_1mmpkDa0_Qfej9AHrg,7287
|
|
@@ -44,18 +44,20 @@ hindsight_api/engine/retain/link_utils.py,sha256=kTMxO9fOXHQvRJZ-gsTrdrKmfw16trW
|
|
|
44
44
|
hindsight_api/engine/retain/observation_regeneration.py,sha256=ykEMZihF1Vt8Z7427k1OJKyEjYp0qIs9P1IqP6eyI58,8069
|
|
45
45
|
hindsight_api/engine/retain/orchestrator.py,sha256=z71wMuJsdjLFlhhwnLHuJ3Y3QQk-9w_faKn1zZUYCXw,17156
|
|
46
46
|
hindsight_api/engine/retain/types.py,sha256=JJ4t8Qtp64kTPB9CKOFDXqdos2i8GZXmJZNzBDaNwHY,6514
|
|
47
|
-
hindsight_api/engine/search/__init__.py,sha256=
|
|
47
|
+
hindsight_api/engine/search/__init__.py,sha256=Ug0TLmgHRR1Dv2gkxXsYwAe4TxuYnjYjXPufuijcMsI,802
|
|
48
48
|
hindsight_api/engine/search/fusion.py,sha256=so6LU7kWRR-VJd1Pxlu8idRJ7P2WLCoDwXUnb8jQifo,4309
|
|
49
|
+
hindsight_api/engine/search/graph_retrieval.py,sha256=hHscHqTIrX4eCUuZXX3R7hqH0cUHgKXMVexYljvD3B0,8592
|
|
50
|
+
hindsight_api/engine/search/mpfp_retrieval.py,sha256=L-Aorab-kNocYjymGLwBaAEQusGYCJMwXIEJQIplrBk,14154
|
|
49
51
|
hindsight_api/engine/search/observation_utils.py,sha256=SPrDx6M0daJ_zLLkk78GlQIG3EL7DqMKSu_etKerUfU,4331
|
|
50
52
|
hindsight_api/engine/search/reranking.py,sha256=znjN78VfuT4PqprhGRPd2B9WtVMhAU5A662s0xCvU7g,3329
|
|
51
|
-
hindsight_api/engine/search/retrieval.py,sha256=
|
|
53
|
+
hindsight_api/engine/search/retrieval.py,sha256=ekugBzcANz5q6SyIIX00HQza0phVUNU0xL4xqbXwvwk,24998
|
|
52
54
|
hindsight_api/engine/search/scoring.py,sha256=feFPalpbIMndp8j2Ab0zvu7fRq3c43Wmzrjw3piQ0eM,5167
|
|
53
55
|
hindsight_api/engine/search/temporal_extraction.py,sha256=5klrZdza3mkgk5A15_m_j4IIfOHMc6fUR9UJuzLa790,1812
|
|
54
56
|
hindsight_api/engine/search/think_utils.py,sha256=VJJXFmBg03yO4Mg--UBMlTQW9IZOj2eyTZztjzhT8F8,11315
|
|
55
|
-
hindsight_api/engine/search/trace.py,sha256=
|
|
56
|
-
hindsight_api/engine/search/tracer.py,sha256=
|
|
57
|
-
hindsight_api/engine/search/types.py,sha256=
|
|
58
|
-
hindsight_api-0.1.
|
|
59
|
-
hindsight_api-0.1.
|
|
60
|
-
hindsight_api-0.1.
|
|
61
|
-
hindsight_api-0.1.
|
|
57
|
+
hindsight_api/engine/search/trace.py,sha256=YYaOQ_OPgxzDdrGlNwxd4Pp6BcDyuCM4wegAp4esZ6U,11170
|
|
58
|
+
hindsight_api/engine/search/tracer.py,sha256=qsMGZ63412dghSbvO8gKYyAT3MzXU_wTY9cJorgIa7E,15399
|
|
59
|
+
hindsight_api/engine/search/types.py,sha256=fAG2BocKLGBQ6dEbT3UzZA1JZaiJlco_BJ5Dz45MKQA,5652
|
|
60
|
+
hindsight_api-0.1.5.dist-info/METADATA,sha256=dhaSenCcXJEspSk8-wVOhpnln6XtJ9aoqv0B_Um818o,1523
|
|
61
|
+
hindsight_api-0.1.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
62
|
+
hindsight_api-0.1.5.dist-info/entry_points.txt,sha256=ZDj1gJCi6Ga6VLdPgRSrRizQ4dUTreefjeG_tO1CuHk,58
|
|
63
|
+
hindsight_api-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|