Files
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

8.5 KiB

Provider Setup Patterns

Use these as starting points; keep secrets in env vars.

Live HTTP JSON endpoint

# 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.

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.

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.

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 $refs 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.

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-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
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' };
  }
}
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-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

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" .