AI checks

AI checks need stresseur 0.4 or later.

An AI answer changes from run to run, so assertions: can only check its shape. Add expect: to the request step that calls your AI. The checks run after the flow’s steps, so they never count in its timings. npx stresseur ci turns each check into a pass rate across its runs.

Keys

Key Checks
schema The body against a JSON Schema, inline or a file path
schema_at A path to validate instead of the whole body
max_latency_ms The step’s duration
max_ttft_ms Time to first token (needs stream:)
usage Where the response reports tokens (default response.body.usage)
max_input_tokens, max_output_tokens, max_total_tokens Token budgets
judge An LLM judge: criteria, steps, input, output, min_score (1–5)

Example

- request:
    name: StreamChat
    depends_on: Login
    method: POST
    url: '{{ #env:APP_URL }}/api/chat'
    headers: { Authorization: 'Bearer {{ Login.response.body.token }}' }
    body: { message: 'How do I cancel my plan?' }
    stream: openai
    expect:
      max_ttft_ms: 800
      max_latency_ms: 6000
      max_output_tokens: 400
      judge:
        criteria: Answers how to cancel, using only the help doc.
        steps:
          - Check it says where to cancel (Settings > Billing).
          - Penalize any claim the help doc does not make.
        input: |
          Question: How do I cancel my plan?
          Help doc: Cancel any time in Settings > Billing > Cancel plan.
        output: '{{ response.text }}'
        min_score: 4

The judge sees only input and output, so put the question and any reference facts in input. Always write steps. Set iterations: 4 on the flow to call your AI fewer times per CI run.

The judge key in CI

The judge runs in your CI with your key. Add it as a secret:

gh secret set STRESSEUR_JUDGE_API_KEY

npx stresseur init-ci already maps STRESSEUR_JUDGE_API_KEY, ANTHROPIC_API_KEY and OPENAI_API_KEY from your secrets. It does so on the run step only, so your app never sees them. An empty secret counts as not set.

Without a key, judge checks are skipped with a notice that names gh secret set STRESSEUR_JUDGE_API_KEY. A skipped check never fails the run.

Pull requests from forks get no secrets. Their judge checks are skipped, and the notice says why.

Pick the model at the top of the flow file with judge: { provider, model }: provider is anthropic (the default) or openai, for any OpenAI-compatible API with base_url. Verdicts are cached in .stresseur/judge-cache.json, so an unchanged answer is never judged twice.

Streaming

Put stream: on the request step: openai, anthropic, vercel-ai or sse (raw). The step then exposes response.text (the assembled answer), response.ttft_ms, response.usage and response.events. stream_timeout_ms (default 30000) fails a stream that never closes. A recorded stream, or one in a HAR file, already gets the right stream:.

The engine runs these checks itself, and stream: and expect: survive import and export.

npx stresseur generate prints a hint on AI endpoints. Add expect: yourself, or let your coding agent do it after install-skill. To start from a browser capture, use generate --har (HAR).

On the pull request

The comment gets a Quality section when a check’s pass rate drops against the last 30 runs on main. A drop of 10 points warns. To fail the check, set fail_drop in api-tests/stresseur.yml; this needs Team:

gate:
  quality:
    pass_rate: { warn_drop: 10%, fail_drop: 25% }