Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ jobs:

publish-npm:
name: Publish to npm
needs: [build-macos-arm64, build-linux-x64, build-windows-x64]
needs: [build-macos-arm64, build-linux-x64]
runs-on: ubuntu-latest
if: |
startsWith(github.ref, 'refs/tags/v') &&
Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- name: Detect Blackfire credentials
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20
id: blackfire
run: |
if [ -n "${{ secrets.BLACKFIRE_SERVER_ID }}" ] && \
[ -n "${{ secrets.BLACKFIRE_SERVER_TOKEN }}" ] && \
[ -n "${{ secrets.BLACKFIRE_CLIENT_ID }}" ] && \
[ -n "${{ secrets.BLACKFIRE_CLIENT_TOKEN }}" ]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
else
echo "enabled=false" >> "$GITHUB_OUTPUT"
fi

- name: Install dependencies (Linux)
if: matrix.os == 'ubuntu-latest'
Expand Down Expand Up @@ -472,6 +485,33 @@ jobs:
- name: Run integration tests (macOS/Linux)
if: matrix.os != 'windows-latest'
run: npm run test:integration

- name: Upload coverage reports to Codecov
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20 && always()
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
fail_ci_if_error: false

- name: Setup Blackfire
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20 && steps.blackfire.outputs.enabled == 'true'
uses: shivammathur/setup-php@v2
with:
tools: blackfire
env:
BLACKFIRE_SERVER_ID: ${{ secrets.BLACKFIRE_SERVER_ID }}
BLACKFIRE_SERVER_TOKEN: ${{ secrets.BLACKFIRE_SERVER_TOKEN }}
BLACKFIRE_CLIENT_ID: ${{ secrets.BLACKFIRE_CLIENT_ID }}
BLACKFIRE_CLIENT_TOKEN: ${{ secrets.BLACKFIRE_CLIENT_TOKEN }}

- name: Run Blackfire profile
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20 && steps.blackfire.outputs.enabled == 'true'
run: blackfire run node scripts/blackfire-profile.js

- name: Skip Blackfire profile
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20 && steps.blackfire.outputs.enabled != 'true'
run: echo "Blackfire secrets are not configured; skipping Blackfire profiling."

- name: Skip integration tests (Windows - FAISS not installed)
if: matrix.os == 'windows-latest'
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- language: c-cpp
build-mode: manual
env:
FAISS_REF: 3f127ee3bad4922b1ba0cf2f17f1368d99a241ec
FAISS_REF: v1.14.1

steps:
- name: Checkout code
Expand All @@ -50,9 +50,8 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y cmake libopenblas-dev libomp-dev build-essential
git clone --depth 1 https://github.com/facebookresearch/faiss.git /tmp/faiss
git clone --branch "$FAISS_REF" --depth 1 https://github.com/facebookresearch/faiss.git /tmp/faiss
cd /tmp/faiss
git checkout "$FAISS_REF"
cmake -B build \
-DFAISS_ENABLE_GPU=OFF \
-DFAISS_ENABLE_PYTHON=OFF \
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
name: Nightly build, test, and example smoke
runs-on: ubuntu-latest
env:
FAISS_REF: 3f127ee3bad4922b1ba0cf2f17f1368d99a241ec
FAISS_REF: v1.14.1
OMP_NUM_THREADS: 1
OMP_THREAD_LIMIT: 1

Expand All @@ -31,9 +31,8 @@ jobs:

- name: Build and install FAISS (CPU)
run: |
git clone --depth 1 https://github.com/facebookresearch/faiss.git /tmp/faiss
git clone --branch "$FAISS_REF" --depth 1 https://github.com/facebookresearch/faiss.git /tmp/faiss
cd /tmp/faiss
git checkout "$FAISS_REF"
cmake -B build \
-DFAISS_ENABLE_GPU=OFF \
-DFAISS_ENABLE_PYTHON=OFF \
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,24 @@ npm test # All tests
npm run test:unit # Unit tests only
npm run test:integration # Integration tests only
npm run test:ci # CI tests (faster, no manual tests)
npm run profile:blackfire # Deterministic profiling workload used by Blackfire in CI
```

### CI Coverage And Profiling

The GitHub Actions CI workflow uploads Jest coverage to Codecov from the `ubuntu-latest` / `Node 20` job. To enable it, add this repository secret:

```bash
CODECOV_TOKEN
```

Blackfire profiling is also wired into that same canonical Linux CI job. The workflow automatically skips the Blackfire step when credentials are not configured. To enable Blackfire in GitHub Actions, add these repository secrets:

```bash
BLACKFIRE_SERVER_ID
BLACKFIRE_SERVER_TOKEN
BLACKFIRE_CLIENT_ID
BLACKFIRE_CLIENT_TOKEN
```

### Generating Documentation
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"test:ci": "jest --ci --coverage --config=jest.ci.config.js --testPathIgnorePatterns=manual",
"test:unit": "jest test/unit",
"test:integration": "jest test/integration",
"profile:blackfire": "node scripts/blackfire-profile.js",
"test:watch": "jest --watch",
"test:memory": "node --expose-gc test/memory.test.js",
"test:local": "npm run clean && npm run build && npm test",
Expand Down
77 changes: 77 additions & 0 deletions scripts/blackfire-profile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env node

const { FaissIndex } = require('../src/js/index');

function createRng(seed) {
let state = seed >>> 0;
return function next() {
state = (Math.imul(1664525, state) + 1013904223) >>> 0;
return state / 0x100000000;
};
}

function generateNormalizedVectors(count, dims, seed) {
const next = createRng(seed);
const vectors = new Float32Array(count * dims);

for (let row = 0; row < count; row++) {
let norm = 0;
const offset = row * dims;

for (let col = 0; col < dims; col++) {
const value = next() * 2 - 1;
vectors[offset + col] = value;
norm += value * value;
}

norm = Math.sqrt(norm) || 1;
for (let col = 0; col < dims; col++) {
vectors[offset + col] /= norm;
}
}

return vectors;
}

async function main() {
const dims = 128;
const vectorCount = 2048;
const queryCount = 32;
const iterations = 40;

const index = new FaissIndex({ type: 'FLAT_L2', dims });

try {
const vectors = generateNormalizedVectors(vectorCount, dims, 12345);
const queries = generateNormalizedVectors(queryCount, dims, 67890);

await index.add(vectors);

for (let i = 0; i < iterations; i++) {
const queryOffset = (i % queryCount) * dims;
const query = queries.subarray(queryOffset, queryOffset + dims);
await index.search(query, 10);
}

await index.searchBatch(queries, 10);

const stats = index.getStats();
console.log(
JSON.stringify({
profiled: true,
type: stats.type,
dims: stats.dims,
ntotal: stats.ntotal,
iterations,
queryCount,
})
);
} finally {
index.dispose();
}
}

main().catch((error) => {
console.error(error);
process.exit(1);
});
Loading