Files
promptfoo--promptfoo/site/docs/providers/vllm.md
T
wehub-resource-sync 0d3cb498a3
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

318 lines
12 KiB
Markdown

---
sidebar_label: vLLM
description: "Run vLLM's OpenAI-compatible server with promptfoo, including local chat targets, self-hosted judges, and thinking-model grading."
---
# vLLM
[vLLM's OpenAI-compatible server](https://docs.vllm.ai/en/latest/serving/openai_compatible_server/)
implements `/v1/chat/completions`, `/v1/completions`, `/v1/responses`, and `/v1/embeddings`.
Promptfoo connects to it through the OpenAI provider by changing `apiBaseUrl`.
Use this page when:
- vLLM is the model under test
- vLLM is the local LLM-as-a-judge provider for model-graded assertions such as
`llm-rubric`, `g-eval`, `factuality`, `answer-relevance`, `context-*`, or `select-best`
- your vLLM model returns a separate `reasoning` field and promptfoo should grade only the final `content`
## Start a vLLM server
```bash
vllm serve Qwen/Qwen3-8B \
--host 0.0.0.0 \
--port 8000 \
--served-model-name qwen3-8b \
--api-key token-abc123
```
Then verify the server directly:
```bash
curl http://localhost:8000/v1/chat/completions \
-H 'Authorization: Bearer token-abc123' \
-H 'Content-Type: application/json' \
-d '{
"model": "qwen3-8b",
"messages": [{"role": "user", "content": "Reply with OK"}],
"max_tokens": 8
}'
```
`apiBaseUrl` should be the `/v1` root. Promptfoo appends `/chat/completions`,
`/completions`, or `/embeddings` depending on the provider type.
For a wiring-only smoke test, a tiny reasoning model such as `Qwen/Qwen3-0.6B` can verify that
promptfoo reaches vLLM and that `showThinking` behaves correctly. Do not use a tiny model as a real
judge; use it only to test the endpoint, parser, and config shape:
```bash
vllm serve Qwen/Qwen3-0.6B \
--host 0.0.0.0 \
--port 8000 \
--served-model-name llm_judge \
--reasoning-parser qwen3 \
--max-model-len 4096 \
--api-key token-abc123
```
### Example judge models
Keep `--served-model-name` short and stable; promptfoo uses that alias in
`openai:chat:<served-model-name>`.
#### GPT-OSS
For GPT-OSS, use the Hugging Face model name as the vLLM model and expose a short served alias.
The example below uses `openai/gpt-oss-20b`; use a larger GPT-OSS checkpoint the same way when your
host has enough memory:
```bash
vllm serve openai/gpt-oss-20b \
--host 0.0.0.0 \
--port 8000 \
--served-model-name gpt-oss-20b \
--api-key token-abc123
```
Prefer a Linux CUDA or ROCm host for GPT-OSS with vLLM. If CPU or ARM serving fails, check the
[vLLM GPT-OSS recipe](https://docs.vllm.ai/projects/recipes/en/latest/OpenAI/GPT-OSS.html) for the
backend support notes that match your vLLM release.
#### GLM-4.7
For GLM-4.7, use a vLLM and Transformers combination that supports the exact GLM checkpoint you are
serving. The [vLLM GLM recipe](https://docs.vllm.ai/projects/recipes/en/latest/GLM/GLM.html) keeps
the install guidance for GLM releases:
```bash
vllm serve zai-org/GLM-4.7-FP8 \
--host 0.0.0.0 \
--port 8000 \
--served-model-name glm-4.7 \
--tensor-parallel-size 4 \
--tool-call-parser glm47 \
--reasoning-parser glm45 \
--enable-auto-tool-choice \
--api-key token-abc123
```
For `zai-org/GLM-4.7-Flash`, use a served name such as `glm-4.7-flash`. If you do not need tool
calling, the tool flags are optional for ordinary model-graded assertions; keep
`--reasoning-parser glm45` when you want vLLM to split reasoning from final content.
## Use vLLM as the target model
```yaml title="promptfooconfig.yaml"
prompts:
- '{{question}}'
providers:
- id: openai:chat:qwen3-8b
label: vLLM qwen3-8b
config:
apiBaseUrl: http://localhost:8000/v1
apiKey: token-abc123
temperature: 0.2
max_tokens: 512
tests:
- vars:
question: 'What is the capital of France?'
assert:
- type: contains
value: Paris
```
For completions models, use `openai:completion:<served-model-name>` instead of
`openai:chat:<served-model-name>`.
You can also put the endpoint in an environment variable:
```bash
export OPENAI_BASE_URL=http://localhost:8000/v1
export OPENAI_API_KEY=token-abc123
```
## Use vLLM as an LLM judge
Model-graded assertions call a separate grading provider. Configure that provider under
`defaultTest.options.provider` when every model-graded assertion should use the same vLLM judge:
```yaml title="promptfooconfig.yaml"
prompts:
- '{{answer}}'
providers:
# System under test. This can be any provider.
- echo
defaultTest:
options:
provider:
id: openai:chat:llm_judge
label: Judge: llm_judge @ vLLM
config:
apiBaseUrl: http://localhost:8000/v1
apiKey: token-abc123
temperature: 0
max_tokens: 10000
showThinking: false
tests:
- vars:
answer: 'Use the Forgot password link and verify by email or SMS.'
assert:
- type: llm-rubric
value: |
Pass if the answer explains how to reset a password and mentions a verification step.
```
Do not repeat `provider: openai:chat:llm_judge` on an assertion when the full provider object
already lives in `defaultTest.options.provider`. An assertion-level `provider` overrides the default
provider object, so the `apiBaseUrl`, `apiKey`, `showThinking`, and other config values above will
not be used.
If only one assertion should use vLLM, put the full object on that assertion:
```yaml
assert:
- type: llm-rubric
value: 'Answer gives correct password reset instructions'
provider:
id: openai:chat:llm_judge
config:
apiBaseUrl: http://localhost:8000/v1
apiKey: token-abc123
temperature: 0
max_tokens: 10000
showThinking: false
```
## Thinking and reasoning models
When vLLM is started with a reasoning parser, responses may include:
- `message.reasoning_content` or `message.reasoning`: hidden reasoning extracted by vLLM
- `message.content`: final answer
Promptfoo's OpenAI-compatible chat provider includes reasoning in the returned output by default:
```text
Thinking: <reasoning>
<content>
```
That is useful when vLLM is the target model, because assertions can inspect the full visible output.
It is usually wrong when vLLM is the judge, because model-graded assertions consume the judge output
as the material to parse, embed, classify, or score. If the reasoning text contains JSON-looking
scratchpad content, attribution markers, candidate sentences, or numeric choices, promptfoo can use
that scratchpad before the final answer.
Set `showThinking: false` on vLLM judge providers so promptfoo discards reasoning fields and parses
only `content`.
This depends on vLLM successfully splitting the response. If the request stops before the model
closes its thinking block, vLLM can return raw `<think>...` text in `message.content` instead of
`reasoning_content`. In that case `showThinking: false` cannot distinguish scratchpad text from
final content. Increase the server `--max-model-len` and provider `max_tokens`, or disable thinking
for judge calls with `chat_template_kwargs.enable_thinking: false`.
`search-rubric` is special because it requires web search. A plain vLLM chat server is not a
web-search-capable grader; promptfoo will prefer or load a search-capable provider instead. The
`showThinking` guidance applies to the search provider that actually grades the assertion.
This applies to every model-graded assertion that consumes text from the judge. JSON-first metrics
can parse scratchpad JSON, RAG metrics can score scratchpad sentences or attribution markers,
`answer-relevance` can embed generated questions with `Thinking:` prepended, and `select-best` can
read a scratchpad number as the winning index.
### Disable thinking at the vLLM API level
`showThinking: false` only changes what promptfoo reads from the response; the model may still spend
tokens thinking. For small local judges and CI smoke tests, disabling thinking is often faster and
avoids truncated `<think>` content. Qwen3 and GLM chat templates support disabling thinking per
request through `chat_template_kwargs`:
```yaml
defaultTest:
options:
provider:
id: openai:chat:llm_judge
config:
apiBaseUrl: http://localhost:8000/v1
apiKey: token-abc123
showThinking: false
passthrough:
chat_template_kwargs:
enable_thinking: false
```
For GPT-OSS-style chat completions, request-level reasoning controls use different fields:
```yaml
defaultTest:
options:
provider:
id: openai:chat:gpt-oss-20b
config:
apiBaseUrl: http://localhost:8000/v1
apiKey: token-abc123
showThinking: false
passthrough:
include_reasoning: false
reasoning_effort: low
```
Keep `showThinking: false` even when you pass model-specific controls such as
`include_reasoning: false` or `chat_template_kwargs.enable_thinking: false`. Those controls save
tokens when vLLM honors them; `showThinking: false` is the promptfoo-side guard.
## Provider maps for text and embeddings
Some assertions need a text judge and an embedding model. Use a provider map when a single eval uses
both text-graded assertions and embedding-based assertions such as `answer-relevance` or `similar`:
```yaml
defaultTest:
options:
provider:
text:
id: openai:chat:llm_judge
config:
apiBaseUrl: http://localhost:8000/v1
apiKey: token-abc123
temperature: 0
showThinking: false
embedding:
id: openai:embedding:intfloat/e5-large-v2
config:
apiBaseUrl: http://localhost:8000/v1
apiKey: token-abc123
```
## Troubleshooting
| Symptom | Fix |
| -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `API key is not set` | Set `apiKey` in provider config, or set `OPENAI_API_KEY`. If vLLM was started without `--api-key`, any placeholder such as `empty` is fine. |
| `ECONNREFUSED` | Use `127.0.0.1` instead of `localhost`, verify the vLLM port, and confirm Docker or a remote host exposes the port. |
| Promptfoo calls OpenAI instead of vLLM | Put `apiBaseUrl: http://.../v1` on the provider object, or set `OPENAI_BASE_URL`. Do not set `apiBaseUrl` to `/v1/chat/completions`. |
| Judge returns `Could not extract JSON`, wrong categories, odd RAG scores, or wrong `select-best` winners | Set `showThinking: false` on the judge provider and keep the full provider object in `defaultTest.options.provider` or `assert.provider`. |
| Judge output still starts with `<think>` even with `showThinking: false` | The generation was truncated before vLLM split reasoning into `reasoning_content`. Increase `--max-model-len` / `max_tokens`, or disable thinking via `passthrough.chat_template_kwargs.enable_thinking: false`. |
| `search-rubric` uses a different provider than vLLM | This is expected unless the configured provider has web-search capability. Plain vLLM chat is not a search provider; configure a web-search-capable grader for `search-rubric`. |
| `assert.provider` appears to ignore `defaultTest.options.provider.config` | This is expected precedence. Use the full provider object at the assertion level, or remove `assert.provider` so the default provider object is inherited. |
Run with `--no-cache` while debugging:
```bash
promptfoo eval -c promptfooconfig.yaml --no-cache -o results.json
```
Then inspect the judge result:
```bash
jq '.results.results[].gradingResult.componentResults[] | {pass, score, reason}' results.json
```