1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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:
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:
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 }}