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

14 KiB
Raw Permalink Blame History

title, sidebar_label, description, sidebar_position, keywords
title sidebar_label description sidebar_position keywords
QuiverAI Provider QuiverAI Generate and vectorize SVG vector graphics with QuiverAI's Arrow models in promptfoo. 42
quiverai
svg
vector graphics
arrow
image generation
vectorization

QuiverAI

The QuiverAI provider generates and vectorizes SVG graphics with the Arrow family of models. Output is raw SVG markup, which works with text-based assertions like is-xml, contains, and llm-rubric. Promptfoo also indexes valid single-SVG outputs in the Media Library while preserving the original SVG text for assertions and exports.

Two endpoints are supported:

  • Text → SVG (quiverai:<model>) calls POST /v1/svgs/generations.
  • Image → SVG (quiverai:vectorize:<model>) calls POST /v1/svgs/vectorizations.

Setup

  1. Create an API key at app.quiver.ai
  2. Set the environment variable:
export QUIVERAI_API_KEY=your-api-key

Models

Run GET /v1/models for the live list. The currently released Arrow models are:

Model Provider id Use case
Arrow 1.1 quiverai:arrow-1.1 Default. Best general-purpose tradeoff between quality and credit cost.
Arrow 1.1 Max quiverai:arrow-1.1-max Higher fidelity for dense illustrations, logos, and technical drawings.
Arrow 1.0 quiverai:arrow-1.0 Previous-generation model retained for parity.

The default model is arrow-1.1.

Provider format

quiverai:<model-name>             # text → SVG (default)
quiverai:vectorize:<model-name>   # image → SVG
quiverai:generate:<model-name>    # explicit text → SVG (alias)

quiverai:chat:<model-name> is a legacy alias for the generation endpoint.

Text → SVG

providers:
  - id: quiverai:arrow-1.1
    config:
      temperature: 0.7
      max_output_tokens: 8192
      instructions: 'flat design, minimal color palette'

With reference images (URL string shorthand or { url } / { base64 }):

providers:
  - id: quiverai:arrow-1.1
    config:
      references:
        - https://example.com/style-reference.png
        - { url: https://example.com/another.png }
      instructions: 'Match the style of the reference image'

Generation parameters

Parameter Type Default Description
instructions string Style guidance separate from the prompt
references array Reference images: URL string, { url }, or { base64 }. Arrow 1.1 accepts up to 4 references; Arrow 1.1 Max accepts up to 16.
temperature number 1 Randomness (02)
top_p number 1 Nucleus sampling (01)
presence_penalty number 0 Penalize repeated patterns (-2 to 2)
max_output_tokens integer Maximum output tokens (1131,072)
n integer 1 Number of SVGs to generate (116)
stream boolean true Set false to enable response caching
apiKey string API key (overrides environment variable)
apiBaseUrl string Custom API base URL

When n > 1, multiple SVGs are joined with double newlines and ordered by the response's index.

Image → SVG

The vectorize endpoint converts a raster image (PNG, JPEG, WebP) to SVG. The image can come from the prompt or from config.image.

providers:
  - id: quiverai:vectorize:arrow-1.1
    config:
      auto_crop: true
      target_size: 1024

prompts:
  - '{{image_url}}'

tests:
  - vars:
      image_url: https://example.com/logo.png
    assert:
      - type: is-xml

Accepted prompt forms:

  • A plain https://... URL
  • A data:image/...;base64,... data URL
  • A JSON object string like {"url": "..."} or {"base64": "..."}
  • A raw base64 payload (treated as { base64: ... })

When using an inline data URL in a YAML config, pass it through a variable such as '{{image_data}}' or set config.image.base64. A bare data: string in prompts: is interpreted by Promptfoo's prompt loader before the QuiverAI provider sees it.

You can also provide the image directly in the config and use the prompt for unrelated context:

providers:
  - id: quiverai:vectorize:arrow-1.1
    config:
      image:
        url: https://example.com/logo.png
      auto_crop: true

Vectorize parameters

Parameter Type Default Description
image object Override image input ({ url } or { base64 }); falls back to the prompt
auto_crop boolean false Auto-crop to the dominant subject before vectorization
target_size integer Square resize target in pixels (1284,096)
temperature number 1 Randomness (02)
top_p number 1 Nucleus sampling (01)
presence_penalty number 0 Penalize repeated patterns (-2 to 2)
max_output_tokens integer Maximum output tokens (1131,072)
stream boolean true Set false to enable response caching
apiKey string API key (overrides environment variable)
apiBaseUrl string Custom API base URL

Streaming

Streaming is on by default. The provider receives generating, reasoning, and draft events while the SVG is being produced and assembles the final SVG from the content event(s). Set stream: false to use the JSON endpoint and enable response caching.

Billing and metadata

QuiverAI bills in credits, not USD. Each successful response surfaces credit cost on the response (top-level credits for non-streaming, per-output credits on streaming content events). Promptfoo exposes both fields via response metadata:

result.metadata.responseId; // server-generated request/output id
result.metadata.credits; // total credits debited for this call

The deprecated usage token block is also propagated to tokenUsage for backwards compatibility, even though the API now zeros those values.

Pipeline: GPT Image → QuiverAI vectorize

Chaining a high-quality raster generator (such as OpenAI's gpt-image-2) into QuiverAI's vectorizer is one of the cleanest ways to produce a consistent, editable SVG icon set. Wrap the two calls in a custom JS provider so the pipeline is one provider in your eval and works with normal is-xml, contains, and llm-rubric assertions.

class GptImageToQuiverPipeline {
  constructor(options = {}) {
    this.providerId = options.id || 'pipeline:gpt-image-2->quiverai-vectorize';
    this.config = options.config || {};
  }

  id() {
    return this.providerId;
  }

  async callApi(prompt) {
    // 1. Generate raster with OpenAI gpt-image-2.
    const imgRes = await fetch('https://api.openai.com/v1/images/generations', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
      },
      body: JSON.stringify({
        model: this.config.imageModel || 'gpt-image-2',
        prompt,
        size: '1024x1024',
        quality: 'high',
        background: 'auto', // gpt-image-2 does not accept 'transparent'
        n: 1,
      }),
    });
    if (!imgRes.ok) {
      throw new Error(`OpenAI image step failed: HTTP ${imgRes.status}`);
    }
    const img = await imgRes.json();
    const rasterB64 = img.data?.[0]?.b64_json;
    if (!rasterB64) {
      throw new Error('OpenAI image step returned no image data');
    }

    // 2. Vectorize with QuiverAI Arrow.
    const svgRes = await fetch('https://api.quiver.ai/v1/svgs/vectorizations', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.QUIVERAI_API_KEY}`,
      },
      body: JSON.stringify({
        model: this.config.vectorizeModel || 'arrow-1.1',
        image: { base64: rasterB64 },
        auto_crop: true,
        target_size: 1024,
      }),
    });
    if (!svgRes.ok) {
      throw new Error(`QuiverAI vectorize step failed: HTTP ${svgRes.status}`);
    }
    const svg = await svgRes.json();
    const outputSvg = svg.data?.[0]?.svg;
    if (!outputSvg) {
      throw new Error('QuiverAI vectorize step returned no SVG data');
    }
    return {
      output: outputSvg,
      metadata: { credits: svg.credits, responseId: svg.id },
    };
  }
}

module.exports = GptImageToQuiverPipeline;
prompts:
  - 'Centered icon of {{subject}}, flat vector illustration, bold shapes, minimal palette, clear silhouette.'

providers:
  - id: file://pipeline-provider.js
    label: 'GPT Image-2 → Arrow 1.1'
    config:
      imageModel: gpt-image-2
      vectorizeModel: arrow-1.1
  - id: file://pipeline-provider.js
    label: 'GPT Image-2 → Arrow 1.1 Max'
    config:
      imageModel: gpt-image-2
      vectorizeModel: arrow-1.1-max

tests:
  - vars:
      subject: a friendly red panda mascot facing forward
    assert:
      - type: is-xml
      - type: llm-rubric
        value: A clearly recognizable red panda face with reddish-orange fur and dark facial markings.

A complete working example, including red-panda-themed prompts and side-by-side Arrow 1.1 / Arrow 1.1 Max configs, lives at examples/provider-quiverai/promptfooconfig.pipeline.yaml. In the May 2026 verification run behind this example, Arrow 1.1 debited 15 credits per vectorize and Arrow 1.1 Max debited 20 — both surfaced via metadata.credits so you can budget per eval. Read the live GET /v1/models response for current pricing_credits.

:::tip Each pipeline call hits two providers serially, so individual evaluations take longer than a pure generation run. Lower --max-concurrency if you start hitting QuiverAI's per-minute rate limit, and prefer stream: false on the vectorize step when you want response caching across re-runs. :::

Example

prompts:
  - 'Create a simple SVG icon of: {{subject}}'

providers:
  - id: quiverai:arrow-1.1
    config:
      max_output_tokens: 8192

tests:
  - vars:
      subject: a red heart
    assert:
      - type: is-xml
      - type: llm-rubric
        value: Contains a heart shape in red color

  - vars:
      subject: a yellow star
    assert:
      - type: is-xml
      - type: llm-rubric
        value: Contains a star shape in yellow/gold color

:::note llm-rubric assertions require a grading provider. By default this uses OpenAI, so set OPENAI_API_KEY or configure a different grader. :::

Troubleshooting

Error Cause Fix
insufficient_credits Account has no credits Add credits at app.quiver.ai
invalid_api_key Key is missing or invalid Check QUIVERAI_API_KEY is set correctly
rate_limit_exceeded Per-minute rate limit hit Reduce --max-concurrency or add delays between requests
weekly_limit_exceeded Org weekly quota hit Wait for the rolling weekly window or contact support — retries cannot recover.
account_frozen Account is frozen Contact QuiverAI support
model_not_found Invalid model name Use one of arrow-1.1, arrow-1.1-max, arrow-1.0
upstream_error Transient upstream dependency fail Retry — this is usually transient

Error messages include a request_id for debugging with QuiverAI support.

Environment Variables

Variable Description
QUIVERAI_API_KEY API key (required)

See Also