From 94c2b85c81467eebcfe890d19f5dd0ac39a75632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Fri, 12 Dec 2025 19:13:26 +0100 Subject: [PATCH] switch to pg0-embedded (#28) * switch to pg0-embedded * switch to pg0-embedded * stricter mcp lib --- .github/actions/setup-pg0/action.yml | 11 - .github/workflows/test.yml | 12 - docker/standalone/Dockerfile | 71 +---- hindsight-api/hindsight_api/api/mcp.py | 6 +- hindsight-api/hindsight_api/pg0.py | 378 ++++--------------------- hindsight-api/pyproject.toml | 3 +- uv.lock | 14 + 7 files changed, 80 insertions(+), 415 deletions(-) delete mode 100644 .github/actions/setup-pg0/action.yml diff --git a/.github/actions/setup-pg0/action.yml b/.github/actions/setup-pg0/action.yml deleted file mode 100644 index 87641b31..00000000 --- a/.github/actions/setup-pg0/action.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: 'Setup pg0' -description: 'Install pg0 embedded PostgreSQL' - -runs: - using: 'composite' - steps: - - name: Install pg0 - shell: bash - run: | - curl -fsSL https://raw.githubusercontent.com/vectorize-io/pg0/main/install.sh | bash - echo "$HOME/.pg0/bin" >> $GITHUB_PATH diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 38fbeeb1..fc534078 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -125,9 +125,6 @@ jobs: with: python-version-file: ".python-version" - - name: Install pg0 - uses: ./.github/actions/setup-pg0 - - name: Build API working-directory: ./hindsight-api run: uv build @@ -185,9 +182,6 @@ jobs: with: python-version-file: ".python-version" - - name: Install pg0 - uses: ./.github/actions/setup-pg0 - - name: Build API working-directory: ./hindsight-api run: uv build @@ -269,9 +263,6 @@ jobs: with: node-version: '20' - - name: Install pg0 - uses: ./.github/actions/setup-pg0 - - name: Build API working-directory: ./hindsight-api run: uv build @@ -360,9 +351,6 @@ jobs: hindsight-clients/rust/target key: ${{ runner.os }}-cargo-client-${{ hashFiles('hindsight-clients/rust/Cargo.lock') }} - - name: Install pg0 - uses: ./.github/actions/setup-pg0 - - name: Build API working-directory: ./hindsight-api run: uv build diff --git a/docker/standalone/Dockerfile b/docker/standalone/Dockerfile index 95c96af4..123b1f0b 100644 --- a/docker/standalone/Dockerfile +++ b/docker/standalone/Dockerfile @@ -130,38 +130,10 @@ RUN mkdir -p /app/data && chown -R hindsight:hindsight /app USER hindsight # Set PATH for hindsight user -ENV PATH="/home/hindsight/.hindsight/bin:/app/api/.venv/bin:${PATH}" +ENV PATH="/app/api/.venv/bin:${PATH}" -# Install pg0 binary -RUN mkdir -p /home/hindsight/.hindsight/bin && \ - ARCH=$(uname -m) && \ - if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then \ - PG0_BINARY="pg0-linux-aarch64-gnu"; \ - elif [ "$ARCH" = "x86_64" ]; then \ - PG0_BINARY="pg0-linux-x86_64-gnu"; \ - else \ - echo "Unsupported architecture: $ARCH" && exit 1; \ - fi && \ - echo "Installing pg0 binary: $PG0_BINARY" && \ - for i in 1 2 3 4 5; do \ - curl -fsSL -o /home/hindsight/.hindsight/bin/pg0 \ - "https://github.com/vectorize-io/pg0/releases/latest/download/$PG0_BINARY" && \ - chmod +x /home/hindsight/.hindsight/bin/pg0 && \ - ls -lh /home/hindsight/.hindsight/bin/pg0 && \ - file /home/hindsight/.hindsight/bin/pg0 && \ - ldd /home/hindsight/.hindsight/bin/pg0 2>&1 || true && \ - break || (echo "Retry $i failed, waiting..." && sleep 10); \ - done && \ - echo "Testing pg0 binary..." && \ - /home/hindsight/.hindsight/bin/pg0 --version || (echo "pg0 --version failed with exit code $?"; ldd /home/hindsight/.hindsight/bin/pg0; exit 1) - -# Pre-download PostgreSQL binaries +# Pre-cache PostgreSQL binaries by starting/stopping pg0-embedded ENV PG0_HOME=/home/hindsight/.pg0-cache -RUN pg0 start --help && \ - (pg0 start --name hindsight --port 5555 --username hindsight --password hindsight --database hindsight && \ - sleep 2 && \ - pg0 stop --name hindsight && \ - echo "PostgreSQL pre-cached to $PG0_HOME") || echo "Pre-download skipped" ENV PG0_HOME=/home/hindsight/.pg0 @@ -267,38 +239,17 @@ RUN mkdir -p /app/data && chown -R hindsight:hindsight /app USER hindsight # Set PATH for hindsight user -ENV PATH="/home/hindsight/.hindsight/bin:/app/api/.venv/bin:${PATH}" +ENV PATH="/app/api/.venv/bin:${PATH}" -# Install pg0 binary -RUN mkdir -p /home/hindsight/.hindsight/bin && \ - ARCH=$(uname -m) && \ - if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then \ - PG0_BINARY="pg0-linux-aarch64-gnu"; \ - elif [ "$ARCH" = "x86_64" ]; then \ - PG0_BINARY="pg0-linux-x86_64-gnu"; \ - else \ - echo "Unsupported architecture: $ARCH" && exit 1; \ - fi && \ - echo "Installing pg0 binary: $PG0_BINARY" && \ - for i in 1 2 3 4 5; do \ - curl -fsSL -o /home/hindsight/.hindsight/bin/pg0 \ - "https://github.com/vectorize-io/pg0/releases/latest/download/$PG0_BINARY" && \ - chmod +x /home/hindsight/.hindsight/bin/pg0 && \ - ls -lh /home/hindsight/.hindsight/bin/pg0 && \ - file /home/hindsight/.hindsight/bin/pg0 && \ - ldd /home/hindsight/.hindsight/bin/pg0 2>&1 || true && \ - break || (echo "Retry $i failed, waiting..." && sleep 10); \ - done && \ - echo "Testing pg0 binary..." && \ - /home/hindsight/.hindsight/bin/pg0 --version || (echo "pg0 --version failed with exit code $?"; ldd /home/hindsight/.hindsight/bin/pg0; exit 1) - -# Pre-download PostgreSQL binaries +# Pre-cache PostgreSQL binaries by starting/stopping pg0-embedded ENV PG0_HOME=/home/hindsight/.pg0-cache -RUN pg0 start --help && \ - (pg0 start --name hindsight --port 5555 --username hindsight --password hindsight --database hindsight && \ - sleep 2 && \ - pg0 stop --name hindsight && \ - echo "PostgreSQL pre-cached to $PG0_HOME") || echo "Pre-download skipped" +RUN /app/api/.venv/bin/python -c "\ +from pg0 import Pg0; \ +print('Pre-caching PostgreSQL binaries...'); \ +pg = Pg0(name='hindsight', port=5555, username='hindsight', password='hindsight', database='hindsight'); \ +pg.start(); \ +pg.stop(); \ +print('PostgreSQL pre-cached to PG0_HOME')" || echo "Pre-download skipped" ENV PG0_HOME=/home/hindsight/.pg0 diff --git a/hindsight-api/hindsight_api/api/mcp.py b/hindsight-api/hindsight_api/api/mcp.py index 79a2d095..f5eacc80 100644 --- a/hindsight-api/hindsight_api/api/mcp.py +++ b/hindsight-api/hindsight_api/api/mcp.py @@ -121,11 +121,7 @@ class MCPMiddleware: self.app = app self.memory = memory self.mcp_server = create_mcp_server(memory) - # Use sse_app - http_app requires lifespan management that's complex with middleware - import warnings - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.mcp_app = self.mcp_server.sse_app() + self.mcp_app = self.mcp_server.http_app() async def __call__(self, scope, receive, send): if scope["type"] != "http": diff --git a/hindsight-api/hindsight_api/pg0.py b/hindsight-api/hindsight_api/pg0.py index 86cc20aa..ac642b52 100644 --- a/hindsight-api/hindsight_api/pg0.py +++ b/hindsight-api/hindsight_api/pg0.py @@ -1,373 +1,114 @@ import asyncio -import json import logging -import os -import platform -import re -import shutil -import stat -import subprocess -from pathlib import Path from typing import Optional -import httpx +from pg0 import Pg0 logger = logging.getLogger(__name__) -# pg0 configuration -BINARY_NAME = "pg0" DEFAULT_PORT = 5555 DEFAULT_USERNAME = "hindsight" DEFAULT_PASSWORD = "hindsight" DEFAULT_DATABASE = "hindsight" -def get_platform_binary_name() -> str: - """Get the appropriate binary name for the current platform. - - Supported platforms: - - macOS ARM64 (darwin-aarch64) - - Linux x86_64 (gnu) - - Linux ARM64 (gnu) - - Windows x86_64 - """ - system = platform.system().lower() - machine = platform.machine().lower() - - # Normalize architecture names - if machine in ("x86_64", "amd64"): - arch = "x86_64" - elif machine in ("arm64", "aarch64"): - arch = "aarch64" - else: - raise RuntimeError( - f"Embedded PostgreSQL is not supported on architecture: {machine}. " - f"Supported architectures: x86_64/amd64 (Linux, Windows), aarch64/arm64 (macOS, Linux)" - ) - - if system == "darwin" and arch == "aarch64": - return "pg0-darwin-aarch64" - elif system == "linux" and arch == "x86_64": - return "pg0-linux-x86_64-gnu" - elif system == "linux" and arch == "aarch64": - return "pg0-linux-aarch64-gnu" - elif system == "windows" and arch == "x86_64": - return "pg0-windows-x86_64.exe" - else: - raise RuntimeError( - f"Embedded PostgreSQL is not supported on {system}-{arch}. " - f"Supported platforms: darwin-aarch64 (macOS ARM), linux-x86_64-gnu, linux-aarch64-gnu, windows-x86_64" - ) - - -def get_download_url( - version: str = "latest", - repo: str = "vectorize-io/pg0", -) -> str: - """Get the download URL for pg0 binary.""" - binary_name = get_platform_binary_name() - - if version == "latest": - return f"https://github.com/{repo}/releases/latest/download/{binary_name}" - else: - return f"https://github.com/{repo}/releases/download/{version}/{binary_name}" - - -def _find_pg0_binary() -> Optional[Path]: - """Find pg0 binary in PATH or default install location.""" - # First check PATH - pg0_in_path = shutil.which("pg0") - if pg0_in_path: - return Path(pg0_in_path) - - # Fall back to default install location - default_path = Path.home() / ".hindsight" / "bin" / "pg0" - if default_path.exists() and os.access(default_path, os.X_OK): - return default_path - - return None - - class EmbeddedPostgres: - """ - Manages an embedded PostgreSQL server instance using pg0. - - This class handles: - - Finding or downloading the pg0 CLI - - Starting/stopping the PostgreSQL server - - Getting the connection URI - - Example: - pg = EmbeddedPostgres() - await pg.ensure_installed() - await pg.start() - uri = await pg.get_uri() - # ... use uri with asyncpg ... - await pg.stop() - """ + """Manages an embedded PostgreSQL server instance using pg0-embedded.""" def __init__( self, - version: str = "latest", port: int = DEFAULT_PORT, username: str = DEFAULT_USERNAME, password: str = DEFAULT_PASSWORD, database: str = DEFAULT_DATABASE, name: str = "hindsight", + **kwargs, ): - """ - Initialize the embedded PostgreSQL manager. - - Args: - version: Version of pg0 to download if not found. Defaults to "latest" - port: Port to listen on. Defaults to 5555 - username: Username for the database. Defaults to "hindsight" - password: Password for the database. Defaults to "hindsight" - database: Database name to create. Defaults to "hindsight" - name: Instance name for pg0. Defaults to "hindsight" - """ - self.version = version self.port = port self.username = username self.password = password self.database = database self.name = name + self._pg0: Optional[Pg0] = None - # Will be set when binary is found/installed - self._binary_path: Optional[Path] = _find_pg0_binary() - - @property - def binary_path(self) -> Path: - """Get the path to the pg0 binary.""" - if self._binary_path is None: - # Default install location - return Path.home() / ".hindsight" / "bin" / "pg0" - return self._binary_path - - def is_installed(self) -> bool: - """Check if pg0 is available (in PATH or installed).""" - self._binary_path = _find_pg0_binary() - return self._binary_path is not None - - async def ensure_installed(self) -> None: - """ - Ensure pg0 is available. - - Checks PATH and default location. If not found, raises an error - instructing the user to install pg0 manually. - """ - if self.is_installed(): - logger.debug(f"pg0 found at {self._binary_path}") - return - - raise RuntimeError( - "pg0 is not installed. Please install it manually:\n" - " curl -fsSL https://github.com/vectorize-io/pg0/releases/latest/download/pg0-linux-amd64 -o ~/.local/bin/pg0 && chmod +x ~/.local/bin/pg0\n" - "Or visit: https://github.com/vectorize-io/pg0/releases" - ) - - def _run_command(self, *args: str, capture_output: bool = True) -> subprocess.CompletedProcess: - """Run a pg0 command synchronously.""" - cmd = [str(self.binary_path), *args] - return subprocess.run(cmd, capture_output=capture_output, text=True) - - async def _run_command_async(self, *args: str, timeout: int = 120) -> tuple[int, str, str]: - """Run a pg0 command asynchronously.""" - cmd = [str(self.binary_path), *args] - - def run_sync(): - try: - result = subprocess.run( - cmd, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - timeout=timeout, - ) - return result.returncode, result.stdout, result.stderr - except subprocess.TimeoutExpired: - return 1, "", "Command timed out" - - loop = asyncio.get_event_loop() - return await loop.run_in_executor(None, run_sync) - - def _extract_uri_from_output(self, output: str) -> Optional[str]: - """Extract the PostgreSQL URI from pg0 start output.""" - match = re.search(r"Connection URI:\s*(postgresql://[^\s]+)", output) - if match: - return match.group(1) - return None - - async def _get_version(self) -> str: - """Get the pg0 version.""" - returncode, stdout, stderr = await self._run_command_async("--version", timeout=10) - if returncode == 0 and stdout: - return stdout.strip() - return "unknown" + def _get_pg0(self) -> Pg0: + if self._pg0 is None: + self._pg0 = Pg0( + name=self.name, + port=self.port, + username=self.username, + password=self.password, + database=self.database, + ) + return self._pg0 async def start(self, max_retries: int = 3, retry_delay: float = 2.0) -> str: - """ - Start the PostgreSQL server with retry logic. - - Args: - max_retries: Maximum number of start attempts (default: 3) - retry_delay: Initial delay between retries in seconds (default: 2.0) - - Returns: - The connection URI for the started server. - - Raises: - RuntimeError: If the server fails to start after all retries. - """ - if not self.is_installed(): - raise RuntimeError("pg0 is not installed. Call ensure_installed() first.") - - # Log pg0 version - version = await self._get_version() - logger.info(f"Starting embedded PostgreSQL with pg0 {version} (name: {self.name}, port: {self.port})...") + """Start the PostgreSQL server with retry logic.""" + logger.info(f"Starting embedded PostgreSQL (name: {self.name}, port: {self.port})...") + pg0 = self._get_pg0() last_error = None + for attempt in range(1, max_retries + 1): - returncode, stdout, stderr = await self._run_command_async( - "start", - "--name", self.name, - "--port", str(self.port), - "--username", self.username, - "--password", self.password, - "--database", self.database, - timeout=300, - ) - - # Try to extract URI from output - uri = self._extract_uri_from_output(stdout) - if uri: - logger.info(f"PostgreSQL started on port {self.port}") - return uri - - # Check if pg0 info can find the running instance try: - uri = await self.get_uri() + loop = asyncio.get_event_loop() + info = await loop.run_in_executor(None, pg0.start) logger.info(f"PostgreSQL started on port {self.port}") - return uri - except RuntimeError: - pass + return info.uri + except Exception as e: + last_error = str(e) + if attempt < max_retries: + delay = retry_delay * (2 ** (attempt - 1)) + logger.debug(f"pg0 start attempt {attempt}/{max_retries} failed: {last_error}") + logger.debug(f"Retrying in {delay:.1f}s...") + await asyncio.sleep(delay) + else: + logger.debug(f"pg0 start attempt {attempt}/{max_retries} failed: {last_error}") - # Start failed, log and retry - last_error = stderr or f"pg0 start returned exit code {returncode}" - if attempt < max_retries: - delay = retry_delay * (2 ** (attempt - 1)) - logger.debug(f"pg0 start attempt {attempt}/{max_retries} failed: {last_error.strip()}") - logger.debug(f"Retrying in {delay:.1f}s...") - await asyncio.sleep(delay) - else: - logger.debug(f"pg0 start attempt {attempt}/{max_retries} failed: {last_error.strip()}") - - # All retries exhausted - fail raise RuntimeError( f"Failed to start embedded PostgreSQL after {max_retries} attempts. " - f"Last error: {last_error.strip() if last_error else 'unknown'}" + f"Last error: {last_error}" ) async def stop(self) -> None: """Stop the PostgreSQL server.""" - if not self.is_installed(): - return - + pg0 = self._get_pg0() logger.info(f"Stopping embedded PostgreSQL (name: {self.name})...") - returncode, stdout, stderr = await self._run_command_async("stop", "--name", self.name) - - if returncode != 0: - if "not running" in stderr.lower(): - return - raise RuntimeError(f"Failed to stop PostgreSQL: {stderr}") - - logger.info("Embedded PostgreSQL stopped") - - async def _get_info(self) -> dict: - """Get info from pg0 using the `info -o json` command.""" - if not self.is_installed(): - raise RuntimeError("pg0 is not installed.") - - returncode, stdout, stderr = await self._run_command_async( - "info", "--name", self.name, "-o", "json" - ) - - if returncode != 0: - raise RuntimeError(f"Failed to get PostgreSQL info: {stderr}") - try: - return json.loads(stdout.strip()) - except json.JSONDecodeError as e: - raise RuntimeError(f"Failed to parse pg0 info output: {e}") + loop = asyncio.get_event_loop() + await loop.run_in_executor(None, pg0.stop) + logger.info("Embedded PostgreSQL stopped") + except Exception as e: + if "not running" in str(e).lower(): + return + raise RuntimeError(f"Failed to stop PostgreSQL: {e}") async def get_uri(self) -> str: """Get the connection URI for the PostgreSQL server.""" - info = await self._get_info() - uri = info.get("uri") - if not uri: + pg0 = self._get_pg0() + loop = asyncio.get_event_loop() + info = await loop.run_in_executor(None, pg0.info) + if info is None or not info.running: raise RuntimeError("PostgreSQL server is not running or URI not available") - return uri - - async def status(self) -> dict: - """Get the status of the PostgreSQL server.""" - if not self.is_installed(): - return {"installed": False, "running": False} - - try: - info = await self._get_info() - return { - "installed": True, - "running": info.get("running", False), - "uri": info.get("uri"), - } - except RuntimeError: - return {"installed": True, "running": False} + return info.uri async def is_running(self) -> bool: """Check if the PostgreSQL server is currently running.""" - if not self.is_installed(): - return False try: - info = await self._get_info() - return info.get("running", False) - except RuntimeError: + pg0 = self._get_pg0() + loop = asyncio.get_event_loop() + info = await loop.run_in_executor(None, pg0.info) + return info is not None and info.running + except Exception: return False async def ensure_running(self) -> str: - """ - Ensure the PostgreSQL server is running. - - Installs if needed, starts if not running. - - Returns: - The connection URI. - """ - await self.ensure_installed() - + """Ensure the PostgreSQL server is running, starting it if needed.""" if await self.is_running(): return await self.get_uri() - return await self.start() - def uninstall(self) -> None: - """Remove the pg0 binary (only if we installed it).""" - default_path = Path.home() / ".hindsight" / "bin" / "pg0" - if default_path.exists(): - default_path.unlink() - logger.info(f"Removed {default_path}") - - def clear_data(self) -> None: - """Remove all PostgreSQL data (destructive!).""" - result = self._run_command("drop", "--name", self.name, "--force") - if result.returncode == 0: - logger.info(f"Dropped pg0 instance {self.name}") - else: - logger.warning(f"Failed to drop pg0 instance {self.name}: {result.stderr}") - - -# Convenience functions _default_instance: Optional[EmbeddedPostgres] = None @@ -375,33 +116,18 @@ _default_instance: Optional[EmbeddedPostgres] = None def get_embedded_postgres() -> EmbeddedPostgres: """Get or create the default EmbeddedPostgres instance.""" global _default_instance - if _default_instance is None: _default_instance = EmbeddedPostgres() - return _default_instance async def start_embedded_postgres() -> str: - """ - Quick start function for embedded PostgreSQL. - - Downloads, installs, and starts PostgreSQL in one call. - - Returns: - Connection URI string - - Example: - db_url = await start_embedded_postgres() - conn = await asyncpg.connect(db_url) - """ - pg = get_embedded_postgres() - return await pg.ensure_running() + """Quick start function for embedded PostgreSQL.""" + return await get_embedded_postgres().ensure_running() async def stop_embedded_postgres() -> None: """Stop the default embedded PostgreSQL instance.""" global _default_instance - if _default_instance: await _default_instance.stop() diff --git a/hindsight-api/pyproject.toml b/hindsight-api/pyproject.toml index 8a10900b..70f32462 100644 --- a/hindsight-api/pyproject.toml +++ b/hindsight-api/pyproject.toml @@ -28,7 +28,8 @@ dependencies = [ "torch>=2.0.0,<2.6.0", "tiktoken>=0.12.0", "httpx>=0.27.0", - "fastmcp>=2.0.0", + "fastmcp>=2.3.0", + "pg0-embedded>=0.1.0", "python-dateutil>=2.8.0", "opentelemetry-api>=1.20.0", "opentelemetry-sdk>=1.20.0", diff --git a/uv.lock b/uv.lock index 99b16639..51ddc6cf 100644 --- a/uv.lock +++ b/uv.lock @@ -1182,6 +1182,7 @@ dependencies = [ { name = "opentelemetry-exporter-prometheus" }, { name = "opentelemetry-instrumentation-fastapi" }, { name = "opentelemetry-sdk" }, + { name = "pg0-embedded" }, { name = "pgvector" }, { name = "psycopg2-binary" }, { name = "pydantic" }, @@ -1233,6 +1234,7 @@ requires-dist = [ { name = "opentelemetry-exporter-prometheus", specifier = ">=0.41b0" }, { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.41b0" }, { name = "opentelemetry-sdk", specifier = ">=1.20.0" }, + { name = "pg0-embedded", specifier = ">=0.1.0" }, { name = "pgvector", specifier = ">=0.4.1" }, { name = "psycopg2-binary", specifier = ">=2.9.11" }, { name = "pydantic", specifier = ">=2.0.0" }, @@ -2502,6 +2504,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305 }, ] +[[package]] +name = "pg0-embedded" +version = "0.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/2a/26aed143a5bc4396321c5016c3e7574d596b94122e4de555ec21ebd1f135/pg0_embedded-0.10.1.tar.gz", hash = "sha256:afbfa9e050bec48587d55410e2a93694390c8fb50e1bbab2ac22a36a7eec146d", size = 17619 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/61/03f089d9d812e782db200f330e7cf35903834d7f106a2d650bb92c73c8d7/pg0_embedded-0.10.1-py3-none-macosx_14_0_arm64.whl", hash = "sha256:913010ad1a2321367f47cdc907a1639537af36597cd9b61549a341d8da3f249b", size = 13073670 }, + { url = "https://files.pythonhosted.org/packages/72/03/d6e64310c59da880cda4931216f328f72a014c19722fa754b1a0d6422cbb/pg0_embedded-0.10.1-py3-none-manylinux_2_35_aarch64.whl", hash = "sha256:b6f2fc089e844a67dbc1b16899f582ca857744bdd7842b4166a6eecce807a5af", size = 14785516 }, + { url = "https://files.pythonhosted.org/packages/7b/25/a2f84a1c142b48c2a41f14765721650076e2be56762b5ad7cd72ae32e5e4/pg0_embedded-0.10.1-py3-none-manylinux_2_35_x86_64.whl", hash = "sha256:f2ae4ed1ce0aa42a310f20b1ea47dd6091f0f74106b7ded39e9c089e7f80ab25", size = 15224456 }, + { url = "https://files.pythonhosted.org/packages/d0/a8/64963aef0d6ae720b88068441ebdede95b727218c26927e0b8c17d91cf2f/pg0_embedded-0.10.1-py3-none-win_amd64.whl", hash = "sha256:39516c952edc050fbb9e24c35d28c9e013f108879b9c8e91091325231cbdb5a1", size = 54977766 }, +] + [[package]] name = "pgvector" version = "0.4.1"