llmprism 0.2.0

A unified, fluent Rust interface for integrating Large Language Model providers -- a Rust port of the PHP Prism library.
Documentation
name: CI

# Runs on every push/PR. Nothing here calls a real provider API -- every live
# smoke test under tests/ (one per provider) detects its missing API key env
# var and skips itself, so this workflow needs no secrets to stay green.
on:
  push:
    branches: [main]
  pull_request:

env:
  CARGO_TERM_COLOR: always

jobs:
  fmt:
    name: Formatting
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - run: cargo fmt --all -- --check

  clippy:
    name: Clippy (all features)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - run: cargo clippy --all-features --all-targets -- -D warnings

  msrv:
    name: MSRV (Cargo.toml's rust-version)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Read rust-version from Cargo.toml
        id: msrv
        run: echo "version=$(grep -m1 '^rust-version' Cargo.toml | sed -E 's/.*"(.*)".*/\1/')" >> "$GITHUB_OUTPUT"
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ steps.msrv.outputs.version }}
      - uses: Swatinem/rust-cache@v2
      # A build, not a full test run: the MSRV promise is "this compiles on
      # that toolchain", not "every dev-dependency (some of which may have a
      # higher MSRV than the library itself) does too".
      - run: cargo build --features full

  test:
    name: Test (${{ matrix.features }})
    runs-on: ubuntu-latest
    strategy:
      matrix:
        # "default" catches the core crate on its own -- no HTTP client, no
        # provider code -- so a change that accidentally requires a feature
        # flag to compile gets caught here.
        features: ["default", "full"]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: cargo test (default features)
        if: matrix.features == 'default'
        run: cargo test
      - name: cargo test (all features)
        if: matrix.features == 'full'
        run: cargo test --features full

  docs:
    name: Docs build cleanly
    runs-on: ubuntu-latest
    env:
      RUSTDOCFLAGS: -D warnings
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo doc --all-features --no-deps
      # Also check with nothing enabled: an intra-doc link in the crate-root
      # doc comment (which is always compiled, regardless of features) to a
      # cfg-gated item is invisible under `--all-features` alone -- the link
      # resolves there because the item happens to exist -- but breaks for
      # anyone who runs a plain `cargo doc`/`rust-analyzer` with no features
      # enabled, which is the default. Caught exactly this way once already.
      - run: cargo doc --no-deps

  feature-matrix:
    name: Build + docs (--features ${{ matrix.feature }})
    runs-on: ubuntu-latest
    # `full` (via the `test` job above) isn't enough on its own to catch every
    # possible mistake: a feature-gated item that only compiles because some
    # *other* enabled feature happens to pull in what it needs will build fine
    # under `full` and then break for anyone enabling that feature alone. This
    # job builds each provider feature completely on its own
    # (`--no-default-features`, nothing else turned on) to catch that class of
    # bug -- it's exactly how a real bug during development (a macro applying
    # `#[cfg(feature = ...)]` to a struct but not the `impl` blocks beside it)
    # was first caught, and it's cheap enough to just keep doing on every push.
    strategy:
      matrix:
        feature:
          - openai
          - anthropic
          - groq
          - deepseek
          - mistral
          - xai
          - openrouter
          - perplexity
          - zai
          - ollama
          - voyageai
          - elevenlabs
          - gemini
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo build --no-default-features --features ${{ matrix.feature }}
      # Same reasoning as the `docs` job's plain `cargo doc --no-deps` step,
      # applied per-feature rather than only to the all-features-off case: a
      # cfg-gated intra-doc link can resolve under `--all-features` (the
      # target happens to exist there) while still being broken for anyone
      # enabling just this one feature. Found several of exactly this shape
      # while adding the `with_client` escape hatch -- each provider's own
      # module doc linking to another provider's type that isn't guaranteed
      # present.
      - run: RUSTDOCFLAGS="-D warnings" cargo doc --no-default-features --features ${{ matrix.feature }} --no-deps

  examples:
    name: Examples build and the offline one runs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo build --examples --features full
      # The only example that needs no API key at all -- the rest gate on
      # their provider's key being set and skip themselves the same way the
      # live smoke tests under tests/ do, so there's nothing more to run here
      # without secrets this workflow deliberately doesn't have.
      - run: cargo run --example testing_with_fake_provider

  audit:
    name: Security advisories
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}