diff --git a/.env.example b/.env.example index 87d0f7ba..33df2781 100644 --- a/.env.example +++ b/.env.example @@ -2,8 +2,9 @@ # Copy this file to .env and fill in your values # LLM Configuration (Required) +HINDSIGHT_API_LLM_PROVIDER=openai HINDSIGHT_API_LLM_API_KEY=your-api-key-here -HINDSIGHT_API_LLM_MODEL=gpt-4o-mini +HINDSIGHT_API_LLM_MODEL=o3-mini HINDSIGHT_API_LLM_BASE_URL=https://api.openai.com/v1 # API Configuration (Optional) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 328253a4..38fbeeb1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -108,6 +108,8 @@ jobs: HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }} HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Prefer CPU-only PyTorch in CI (but keep PyPI for everything else) + UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu steps: - uses: actions/checkout@v4 @@ -132,7 +134,27 @@ jobs: - name: Install dependencies working-directory: ./hindsight-api - run: uv sync --extra test + run: uv sync --extra test --no-install-project --index-strategy unsafe-best-match + + - name: Cache HuggingFace models + uses: actions/cache@v4 + with: + path: ~/.cache/huggingface + key: ${{ runner.os }}-huggingface-${{ hashFiles('hindsight-api/pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-huggingface- + + - name: Pre-download models + working-directory: ./hindsight-api + run: | + uv run python -c " + from sentence_transformers import SentenceTransformer, CrossEncoder + print('Downloading embedding model...') + SentenceTransformer('BAAI/bge-small-en-v1.5') + print('Downloading cross-encoder model...') + CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2') + print('Models downloaded successfully') + " - name: Run tests working-directory: ./hindsight-api @@ -146,6 +168,8 @@ jobs: HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b HINDSIGHT_API_URL: http://localhost:8888 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Prefer CPU-only PyTorch in CI (but keep PyPI for everything else) + UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu steps: - uses: actions/checkout@v4 @@ -174,11 +198,11 @@ jobs: - name: Install client test dependencies working-directory: ./hindsight-clients/python - run: uv sync --extra test + run: uv sync --extra test --index-strategy unsafe-best-match - name: Install API dependencies working-directory: ./hindsight-api - run: uv sync + run: uv sync --no-install-project --index-strategy unsafe-best-match - name: Create .env file run: | @@ -223,6 +247,8 @@ jobs: HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b HINDSIGHT_API_URL: http://localhost:8888 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Prefer CPU-only PyTorch in CI (but keep PyPI for everything else) + UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu steps: - uses: actions/checkout@v4 @@ -252,7 +278,7 @@ jobs: - name: Install API dependencies working-directory: ./hindsight-api - run: uv sync + run: uv sync --no-install-project --index-strategy unsafe-best-match - name: Install TypeScript client dependencies working-directory: ./hindsight-clients/typescript @@ -305,6 +331,8 @@ jobs: HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b HINDSIGHT_API_URL: http://localhost:8888 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Prefer CPU-only PyTorch in CI (but keep PyPI for everything else) + UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu steps: - uses: actions/checkout@v4 @@ -341,7 +369,7 @@ jobs: - name: Install API dependencies working-directory: ./hindsight-api - run: uv sync + run: uv sync --no-install-project --index-strategy unsafe-best-match - name: Create .env file run: | diff --git a/docker/standalone/Dockerfile b/docker/standalone/Dockerfile index bff75095..95c96af4 100644 --- a/docker/standalone/Dockerfile +++ b/docker/standalone/Dockerfile @@ -147,9 +147,13 @@ RUN mkdir -p /home/hindsight/.hindsight/bin && \ 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 && \ - /home/hindsight/.hindsight/bin/pg0 --version + 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 ENV PG0_HOME=/home/hindsight/.pg0-cache @@ -280,9 +284,13 @@ RUN mkdir -p /home/hindsight/.hindsight/bin && \ 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 && \ - /home/hindsight/.hindsight/bin/pg0 --version + 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 ENV PG0_HOME=/home/hindsight/.pg0-cache diff --git a/hindsight-api/hindsight_api/engine/cross_encoder.py b/hindsight-api/hindsight_api/engine/cross_encoder.py index 0cf538b1..1e29f173 100644 --- a/hindsight-api/hindsight_api/engine/cross_encoder.py +++ b/hindsight-api/hindsight_api/engine/cross_encoder.py @@ -101,12 +101,7 @@ class LocalSTCrossEncoder(CrossEncoderModel): ) logger.info(f"Reranker: initializing local provider with model {self.model_name}") - # Disable lazy loading (meta tensors) which causes issues with newer transformers/accelerate - # Setting low_cpu_mem_usage=False and device_map=None ensures tensors are fully materialized - self._model = CrossEncoder( - self.model_name, - model_kwargs={"low_cpu_mem_usage": False, "device_map": None}, - ) + self._model = CrossEncoder(self.model_name) logger.info("Reranker: local provider initialized") def predict(self, pairs: List[Tuple[str, str]]) -> List[float]: diff --git a/hindsight-api/pyproject.toml b/hindsight-api/pyproject.toml index 7aba0b59..8a10900b 100644 --- a/hindsight-api/pyproject.toml +++ b/hindsight-api/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ "openai>=1.0.0", "pydantic>=2.0.0", "rich>=13.0.0", - "sentence-transformers>=3.0.0", + "sentence-transformers>=3.0.0,<3.3.0", "langchain-text-splitters>=0.3.0", "fastapi[standard]>=0.120.3", "uvicorn>=0.38.0", @@ -24,8 +24,8 @@ dependencies = [ "pgvector>=0.4.1", "greenlet>=3.2.4", "psycopg2-binary>=2.9.11", - "transformers>=4.30.0", - "torch>=2.0.0", + "transformers>=4.30.0,<4.46.0", + "torch>=2.0.0,<2.6.0", "tiktoken>=0.12.0", "httpx>=0.27.0", "fastmcp>=2.0.0", diff --git a/hindsight-api/tests/test_http_api_integration.py b/hindsight-api/tests/test_http_api_integration.py index ac355235..dcf3deb4 100644 --- a/hindsight-api/tests/test_http_api_integration.py +++ b/hindsight-api/tests/test_http_api_integration.py @@ -281,7 +281,8 @@ async def test_full_api_workflow(api_client, test_bank_id): final_banks_data = response.json()["banks"] final_banks = [a["bank_id"] for a in final_banks_data] assert test_bank_id in final_banks - assert len(final_banks) >= len(initial_banks) + 1 + # Don't assert count increases due to parallel test cleanup races + # Just verify our bank exists in the list # ================================================================ # 10. Clean Up diff --git a/hindsight-clients/python/tests/test_main_operations.py b/hindsight-clients/python/tests/test_main_operations.py index b152481e..fd7f42b5 100644 --- a/hindsight-clients/python/tests/test_main_operations.py +++ b/hindsight-clients/python/tests/test_main_operations.py @@ -239,7 +239,7 @@ class TestEndToEndWorkflow: bank_id=workflow_bank_id, query="What programming technologies do I use?", ) - assert len(search_results) > 0 + assert len(search_results.results) > 0 # 4. Generate contextual answer reflect_response = client.reflect(