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
258 lines
8.5 KiB
Markdown
258 lines
8.5 KiB
Markdown
# Provider Setup Patterns
|
|
|
|
Use these as starting points; keep secrets in env vars.
|
|
|
|
## Live HTTP JSON endpoint
|
|
|
|
```yaml
|
|
# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json
|
|
description: HTTP endpoint smoke test
|
|
|
|
prompts:
|
|
- '{{message}}'
|
|
|
|
providers:
|
|
- id: https
|
|
label: live-chat-api
|
|
config:
|
|
url: '{{env.CHAT_API_URL}}'
|
|
method: POST
|
|
stateful: false
|
|
headers:
|
|
Content-Type: application/json
|
|
Authorization: 'Bearer {{env.CHAT_API_TOKEN}}'
|
|
body:
|
|
message: '{{prompt}}'
|
|
transformResponse: json.output
|
|
|
|
tests:
|
|
- description: endpoint returns text
|
|
vars:
|
|
message: Say exactly PONG.
|
|
assert:
|
|
- type: contains
|
|
value: PONG
|
|
```
|
|
|
|
Smoke with `promptfoo eval -c promptfooconfig.yaml -o output.json --no-cache
|
|
--no-share`. Use `stateful: false` for stateless targets. If the app maintains
|
|
conversation state, omit it and map `{{sessionId}}` into the request or configure
|
|
`sessionParser`.
|
|
|
|
## Live HTTP GET endpoint
|
|
|
|
Use `queryParams` instead of hand-building query strings when the target accepts GET parameters.
|
|
|
|
```yaml
|
|
providers:
|
|
- id: https
|
|
label: search-api
|
|
config:
|
|
url: '{{env.SEARCH_API_URL}}'
|
|
method: GET
|
|
stateful: false
|
|
headers:
|
|
Authorization: 'Bearer {{env.SEARCH_API_TOKEN}}'
|
|
queryParams:
|
|
q: '{{prompt}}'
|
|
user_id: '{{user_id}}'
|
|
transformResponse: json.answer
|
|
```
|
|
|
|
## OpenAI-compatible chat endpoint
|
|
|
|
Use an HTTP provider when the endpoint is OpenAI-shaped but not one of Promptfoo's native providers.
|
|
|
|
```yaml
|
|
providers:
|
|
- id: https
|
|
label: openai-compatible-chat
|
|
config:
|
|
url: '{{env.CHAT_COMPLETIONS_URL}}'
|
|
method: POST
|
|
stateful: false
|
|
headers:
|
|
Content-Type: application/json
|
|
Authorization: 'Bearer {{env.CHAT_COMPLETIONS_TOKEN}}'
|
|
body:
|
|
model: '{{env.CHAT_COMPLETIONS_MODEL}}'
|
|
messages:
|
|
- role: system
|
|
content: Return concise answers.
|
|
- role: user
|
|
content: '{{prompt}}'
|
|
transformResponse: json.choices[0].message.content
|
|
```
|
|
|
|
## Text response endpoint
|
|
|
|
Use `text` in `transformResponse` when the target returns plain text.
|
|
|
|
```yaml
|
|
providers:
|
|
- id: https
|
|
label: text-chat-api
|
|
config:
|
|
url: '{{env.TEXT_CHAT_API_URL}}'
|
|
method: POST
|
|
stateful: false
|
|
headers:
|
|
Content-Type: text/plain
|
|
body: '{{prompt}}'
|
|
transformResponse: text.replace(/^Assistant:\s*/, '')
|
|
```
|
|
|
|
## OpenAPI operation to HTTP provider
|
|
|
|
Map one operation at a time: base URL to an env var, path parameters into the URL with `urlencode`, request/header/query fields into `body`/`headers`/`queryParams`, and the first successful response schema into `transformResponse` (prefer `200`, otherwise the lowest explicit `2xx` status).
|
|
|
|
The bundled `scripts/openapi-operation-to-config.mjs` helper supports local OpenAPI `$ref`s plus `allOf` and first-variant `oneOf`/`anyOf` schemas. It lets operation parameters override path parameters, URL-encodes path/form values, skips readOnly request and writeOnly response fields even through `$ref`/composed schemas, keeps wire names intact, creates safe vars, preserves headers, and maps prompt fields (`message`, `question`, `input`, `text`, `q`, `query`) to `{{prompt}}`. With `--token-env`, it infers Bearer/OAuth2/OpenID/header/query/cookie API-key auth, uses parameter/media examples (including example-only bodies), +json media, text request bodies, form-url-encoded request bodies, structured multipart request bodies with generated file parts, typed/format schema samples from const/defaults/enums, root JSON array request bodies, schema/example-derived response transforms, health/status `message` vars, and `--auth-header X-API-Key --auth-prefix none`.
|
|
|
|
```yaml
|
|
providers:
|
|
- id: https
|
|
config:
|
|
url: '{{env.INVOICE_API_BASE_URL}}/v1/invoices/{{invoice_id}}/chat'
|
|
method: POST
|
|
headers:
|
|
Authorization: 'Bearer {{env.INVOICE_API_TOKEN}}'
|
|
body: { user_id: '{{user_id}}', message: '{{prompt}}' }
|
|
transformResponse: json.output
|
|
```
|
|
|
|
## Static-code-derived local wrappers
|
|
|
|
Use this for direct local code or custom setup. Pick JavaScript for Node apps and Python for Python app modules.
|
|
|
|
```yaml
|
|
# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json
|
|
description: Local agent provider smoke test
|
|
prompts:
|
|
- '{{message}}'
|
|
providers:
|
|
- id: file://provider.py:call_api # use file://provider.js for Node wrappers
|
|
config:
|
|
workers: 1
|
|
timeout: 30000
|
|
tests:
|
|
- description: local provider returns text
|
|
vars:
|
|
message: Say exactly PONG.
|
|
assert:
|
|
- type: contains
|
|
value: PONG
|
|
```
|
|
|
|
```javascript
|
|
export default class LocalAgentProvider {
|
|
constructor(options = {}) {
|
|
this.config = options.config || {};
|
|
}
|
|
|
|
id() {
|
|
return 'local-agent';
|
|
}
|
|
|
|
async callApi(prompt, context = {}) {
|
|
const vars = context.vars || {};
|
|
const result = await callAgent({
|
|
message: prompt,
|
|
userId: vars.user_id || this.config.defaultUserId || 'validate-user',
|
|
});
|
|
|
|
return typeof result?.output === 'string'
|
|
? { output: result.output }
|
|
: { error: 'Agent returned no string output' };
|
|
}
|
|
}
|
|
```
|
|
|
|
```python
|
|
import sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
from app.invoice_agent import call_agent # noqa: E402
|
|
def call_api(prompt: str, options: dict, context: dict) -> dict:
|
|
config = options.get("config", {}) if isinstance(options, dict) else {}
|
|
vars = context.get("vars", {}) if isinstance(context, dict) else {}
|
|
user_id = vars.get("user_id") or config.get("defaultUserId") or "validate-user"
|
|
result = call_agent(message=prompt, user_id=user_id)
|
|
if not isinstance(result.get("output"), str):
|
|
return {"error": "Agent returned no string output"}
|
|
return {"output": result["output"]}
|
|
```
|
|
|
|
Python providers implement `call_api(prompt, options, context)` unless the id
|
|
uses a custom `file://provider.py:function_name` suffix. Anchor `sys.path` to
|
|
the provider directory before nearby app imports. Use `PROMPTFOO_PYTHON` or
|
|
`config.pythonExecutable` for a venv, `PROMPTFOO_PYTHON_WORKERS` or
|
|
`config.workers` for concurrency, and `config.timeout` for slow SDK calls.
|
|
`validate target` may call providers without vars, so wrappers need harmless defaults.
|
|
|
|
## Redteam target with named inputs
|
|
|
|
Use `targets` and preserve the app's real input fields. This gives redteam
|
|
plugins access to the actual authorization and injection surface.
|
|
|
|
```yaml
|
|
# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json
|
|
description: Multi-input redteam target
|
|
|
|
targets:
|
|
- id: https
|
|
label: invoice-agent
|
|
config:
|
|
url: '{{env.INVOICE_AGENT_URL}}'
|
|
method: POST
|
|
stateful: false
|
|
headers:
|
|
Content-Type: application/json
|
|
Authorization: 'Bearer {{env.INVOICE_AGENT_TOKEN}}'
|
|
body:
|
|
vendor_id: '{{vendor_id}}'
|
|
invoice_id: '{{invoice_id}}'
|
|
message: '{{message}}'
|
|
transformResponse: json.output
|
|
inputs:
|
|
vendor_id: Vendor identifier for the signed-in user.
|
|
invoice_id: Invoice identifier being discussed.
|
|
message: User message to the assistant.
|
|
|
|
redteam:
|
|
purpose: >-
|
|
Invoice support assistant that answers invoice questions only for the
|
|
authenticated vendor and must not reveal or modify other vendors' invoices.
|
|
numTests: 3
|
|
plugins:
|
|
- bola
|
|
- rbac
|
|
- indirect-prompt-injection
|
|
strategies:
|
|
- basic
|
|
```
|
|
|
|
Do not set `redteam.injectVar` for multi-input mode. Define `inputs` on the
|
|
target; Promptfoo automatically creates the internal combined `__prompt` value
|
|
for generation and grading.
|
|
|
|
## Hybrid discovery notes
|
|
|
|
Use this when static code and a live endpoint are both available. Record the contract before writing YAML:
|
|
|
|
- Static source: route/handler/client file and line range.
|
|
- Expected request: method, path, headers, body/query fields, and auth env var.
|
|
- Safe live probe: exact non-mutating payload and observed response field.
|
|
- Promptfoo mapping: vars to request fields and `transformResponse`.
|
|
- Open questions: mutations, session behavior, rate limits, or missing auth.
|
|
|
|
Prefer a local wrapper when the static path exposes app logic directly; prefer
|
|
`id: https` when the live endpoint contract is simple and safely probeable.
|
|
|
|
## Static discovery checklist
|
|
|
|
```bash
|
|
rg -n "app\\.(get|post|put|patch)|router\\.(get|post|put|patch)|fetch\\(|axios\\." .
|
|
rg -n "openapi|swagger|routes|controller|handler|chat|completion|agent" .
|
|
rg -n "Authorization|Bearer|apiKey|x-api-key|transformResponse|callApi" .
|
|
```
|