RFC: Switch to rust backend for performance#27
Conversation
chonknick
commented
Jan 7, 2026
- Add ci.yml for Rust tests and Python build verification
- Add release.yml for publishing to crates.io and PyPI
- Update sync-check.yml for new src/data path
- Remove old lint.yml, publish.yml, test.yml (consolidated into ci.yml)
- Rewrite core library in Rust for high performance - Add Python bindings using PyO3 and maturin - Support all 11 embedding providers: - OpenAI, VoyageAI, Cohere, Jina, Mistral - Gemini, Together, Mixedbread, Nomic, DeepInfra, Cloudflare - Features: - Sync embed() and async aembed() methods - Model catalog with 64+ models (compile-time embedded) - Configurable max_retries and timeout - Automatic retry with exponential backoff - Cost tracking per request - Context manager support (sync and async) - to_numpy() for easy numpy conversion - Move legacy Python implementation to src_legacy/
- Update publish.yml for multi-platform wheel builds (Linux, macOS, Windows) - Add crates.io publishing with CARGO_REGISTRY_TOKEN - Update test.yml for Rust tests and Python bindings - Add manual workflow_dispatch trigger for selective publishing
- Add ci.yml for Rust tests and Python build verification - Add release.yml for publishing to crates.io and PyPI - Update sync-check.yml for new src/data path - Remove old lint.yml, publish.yml, test.yml (consolidated into ci.yml)
There was a problem hiding this comment.
Pull request overview
This PR transitions the catsu embeddings client from a pure Python implementation to a Rust-based backend with Python bindings, aimed at improving performance. The changes consolidate multiple GitHub Actions workflows, update the data path for model catalog, and introduce Rust implementations for 11 embedding providers.
Key changes:
- Replaces Python provider implementations with Rust equivalents
- Adds Python bindings via PyO3 for the Rust core
- Consolidates GitHub Actions workflows (test.yml, lint.yml, publish.yml → ci.yml, release.yml)
- Updates model catalog path from
src/catsu/data/models.jsontosrc/data/models.json
Reviewed changes
Copilot reviewed 81 out of 84 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| website/public/models.json | Updates symlink path to new data location |
| src/providers/*.rs | Implements embedding providers in Rust (VoyageAI, Together, OpenAI, Nomic, Mixedbread, Mistral, Jina, Gemini, DeepInfra, Cohere, Cloudflare) |
| src/catalog.rs | Adds Rust model catalog with embedded JSON data |
| packages/python/* | Introduces Python bindings and packaging for Rust library |
| examples/*.{rs,py} | Adds example usage files for each provider |
| .github/workflows/* | Consolidates and updates CI/CD workflows |
| Cargo.toml | Adds Rust workspace and dependencies |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| from catsu import CatsuClient | ||
|
|
||
|
|
||
| def main(): | ||
| client = CatsuClient() |
There was a problem hiding this comment.
Import uses CatsuClient but other example files import Client. The exposed Python API should use Client for consistency.
| from catsu import CatsuClient | |
| def main(): | |
| client = CatsuClient() | |
| from catsu import Client | |
| def main(): | |
| client = Client() |
| from catsu import CatsuClient | ||
|
|
||
|
|
||
| def main(): | ||
| client = CatsuClient() |
There was a problem hiding this comment.
Import uses CatsuClient but other example files import Client. The exposed Python API should use Client for consistency.
| from catsu import CatsuClient | |
| def main(): | |
| client = CatsuClient() | |
| from catsu import Client | |
| def main(): | |
| client = Client() |
| name: Test Rust | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Install Rust | ||
| uses: dtolnay/rust-toolchain@stable | ||
|
|
||
| - name: Run tests | ||
| run: cargo test | ||
|
|
||
| - name: Check formatting | ||
| run: cargo fmt --check | ||
|
|
||
| - name: Run clippy | ||
| run: cargo clippy -- -D warnings | ||
|
|
||
| test-python: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 months ago
To fix the problem, explicitly restrict the GITHUB_TOKEN permissions in this workflow. Since these jobs only check out code and run Rust/Python builds and tests, they only need read access to repository contents. The minimal and appropriate fix is to add a permissions: block at the top level of the workflow (so it applies to all jobs) with contents: read.
Concretely, edit .github/workflows/ci.yml and insert a new permissions: section between the name: CI and the on: block. No other changes are required, because none of the jobs needs write permissions to GitHub resources. The resulting header of the file will be:
name: CI
permissions:
contents: read
on:
push:
branches: [main]
pull_request:
branches: [main]This keeps existing functionality intact while enforcing least‑privilege access for the workflow’s GITHUB_TOKEN.
| @@ -1,5 +1,8 @@ | ||
| name: CI | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] |
| name: Test Python | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up uv | ||
| uses: astral-sh/setup-uv@v5 | ||
| with: | ||
| enable-cache: true | ||
|
|
||
| - name: Set up Python | ||
| run: uv python install 3.12 | ||
|
|
||
| - name: Create virtualenv and install dependencies | ||
| run: | | ||
| uv venv | ||
| uv pip install pytest pytest-asyncio | ||
|
|
||
| - name: Build wheel | ||
| uses: PyO3/maturin-action@v1 | ||
| with: | ||
| command: build | ||
| args: --release -m packages/python/Cargo.toml -o dist | ||
|
|
||
| - name: Install wheel | ||
| run: uv pip install dist/*.whl | ||
|
|
||
| - name: Run tests | ||
| run: | | ||
| source .venv/bin/activate | ||
| python -c "from catsu import Client, EmbedResponse, ModelInfo, Usage; print('Import OK')" | ||
| python -c "from catsu import Client; c = Client(); print(f'Models: {len(c.list_models())}')" |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 months ago
In general, the fix is to explicitly set a permissions block so that GITHUB_TOKEN has only the minimal scopes required. Since this workflow only checks out code and runs local build/test commands, it only needs read access to repository contents. The simplest and safest change is to add a top-level permissions section with contents: read, which will apply to all jobs (test-rust and test-python) because they do not have their own permissions blocks.
Concretely, in .github/workflows/ci.yml, add a new top-level permissions: mapping near the top of the file (e.g., after name: CI and before on:). Set contents: read as the only permission. No steps need to be modified and no additional imports or actions are required. This preserves existing functionality while constraining the GITHUB_TOKEN to read-only repository contents.
| @@ -1,5 +1,8 @@ | ||
| name: CI | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] |
| name: Publish to crates.io | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Install Rust | ||
| uses: dtolnay/rust-toolchain@stable | ||
|
|
||
| - name: Publish to crates.io | ||
| run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} | ||
|
|
||
| publish-python: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 months ago
In general, the fix is to explicitly restrict GITHUB_TOKEN permissions in this workflow to the minimum required. Since the jobs only need to check out code, build, upload artifacts, and publish to external registries using separate secrets, they only require read access to repository contents. We can define a single root‐level permissions block that applies to all jobs in the workflow.
Concretely, in .github/workflows/release.yml, add a permissions: section near the top of the file (after name: or after on:) specifying contents: read. This will ensure that all jobs use a GITHUB_TOKEN limited to read‑only access to repository contents. No other code or steps need to change, and no additional imports or definitions are required.
| @@ -4,6 +4,9 @@ | ||
| release: | ||
| types: [published] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| publish-crate: | ||
| name: Publish to crates.io |
| name: Build wheels (${{ matrix.os }}-${{ matrix.target }}) | ||
| runs-on: ${{ matrix.os }} | ||
| strategy: | ||
| matrix: | ||
| include: | ||
| - os: ubuntu-latest | ||
| target: x86_64 | ||
| - os: ubuntu-latest | ||
| target: aarch64 | ||
| - os: macos-latest | ||
| target: x86_64 | ||
| - os: macos-latest | ||
| target: aarch64 | ||
| - os: windows-latest | ||
| target: x86_64 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.12' | ||
|
|
||
| - name: Build wheels | ||
| uses: PyO3/maturin-action@v1 | ||
| with: | ||
| target: ${{ matrix.target }} | ||
| args: --release --out dist -m packages/python/Cargo.toml --find-interpreter | ||
| manylinux: auto | ||
|
|
||
| - name: Upload wheels | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: wheels-${{ matrix.os }}-${{ matrix.target }} | ||
| path: dist | ||
|
|
||
| publish-python-sdist: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 months ago
The general fix is to explicitly declare a permissions block that limits the GITHUB_TOKEN to the least privileges needed. Here, the jobs only need to read repository contents to check out code; they do not push commits, modify releases, or interact with issues/PRs. Therefore, adding permissions: contents: read at the workflow root is sufficient and will apply to all jobs in this workflow unless overridden.
Concretely, in .github/workflows/release.yml, add a top-level permissions: section after the name: (or before on:) with contents: read. This single change constrains the default token permissions for all jobs (publish-crate, publish-python, publish-python-sdist, and upload-pypi) without changing any of the existing steps or their behavior. No additional imports, methods, or definitions are required, as this is purely a configuration change in the workflow YAML.
| @@ -1,5 +1,8 @@ | ||
| name: Release | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] |
| name: Build Python sdist | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Build sdist | ||
| uses: PyO3/maturin-action@v1 | ||
| with: | ||
| command: sdist | ||
| args: --out dist -m packages/python/Cargo.toml | ||
|
|
||
| - name: Upload sdist | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: wheels-sdist | ||
| path: dist | ||
|
|
||
| upload-pypi: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 months ago
In general, the problem is fixed by adding an explicit permissions: block that restricts the GITHUB_TOKEN to the minimal scopes required. For this workflow, none of the jobs need to write to the repository, or access issues, pull requests, etc. They only need to read the repository contents to build and then use separate registry tokens to publish. So we can safely set contents: read at the workflow level, which applies to all jobs and satisfies CodeQL’s recommendation.
The best fix without changing functionality is to add a single top-level permissions: block right after the on: section in .github/workflows/release.yml:
- Set
permissions: contents: readat the root level. - This will automatically apply to all jobs (
publish-crate,publish-python,publish-python-sdist,upload-pypi) since they do not define their ownpermissions. - No job requires additional scopes (such as
packagesorid-token) given the current snippet, because publishing is done with explicit secrets, notGITHUB_TOKEN, and there’s no OIDC usage shown.
No imports or additional methods are needed because this is a YAML workflow configuration change only.
| @@ -4,6 +4,9 @@ | ||
| release: | ||
| types: [published] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| publish-crate: | ||
| name: Publish to crates.io |
| name: Upload to PyPI | ||
| needs: [publish-python, publish-python-sdist] | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Download artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| pattern: wheels-* | ||
| path: dist | ||
| merge-multiple: true | ||
|
|
||
| - name: Publish to PyPI | ||
| uses: pypa/gh-action-pypi-publish@release/v1 | ||
| with: | ||
| password: ${{ secrets.PYPI_TOKEN }} |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 months ago
In general, the fix is to add an explicit permissions block that grants the least privileges required by the jobs. Since the shown jobs only need to read repository contents (for checkout) and do not modify issues, PRs, or contents, they can safely run with contents: read. Define this at the top (root) of the workflow so it applies to all jobs, including upload-pypi, which CodeQL highlighted.
Concretely, edit .github/workflows/release.yml and insert a root-level permissions block after the name: Release line (before on:). Use:
permissions:
contents: readThis keeps GITHUB_TOKEN limited to read-only repository contents for all jobs, without changing the current functionality, because none of the steps need write access. No additional imports, methods, or definitions are required.
| @@ -1,5 +1,8 @@ | ||
| name: Release | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
catsu-docs | fd98d47 | Commit Preview URL Branch Preview URL |
Jan 07 2026, 08:52 AM |
Deploying catsu with
|
| Latest commit: |
fd98d47
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://a624cc3e.catsu-3ib.pages.dev |
| Branch Preview URL: | https://refactor-rust.catsu-3ib.pages.dev |