Integrations

Vercel AI SDK

Run Drex decision models from the AI SDK's experimental_decide with the @nace-ai/ai-sdk-provider package and a Nace API key.

@nace-ai/ai-sdk-provider adds Nace to the AI SDK as a decision provider. Its models answer the AI SDK's experimental_decide questions with Drex. Requests go straight to Nace with your Nace API key, the same POST /v1/systemone call the API reference describes. It does not go through the Vercel AI Gateway.

@nace-ai/ai-sdk-provider isn't on npm yet, so the install command below works once it's published.

Install

npm install @nace-ai/ai-sdk-provider ai

The package is tested with ai 7.0.157 on Node.js 22 and 24 and in the edge runtime. It is MIT licensed. experimental_decide is experimental in the AI SDK, so its API can change in a minor release.

Create an API key under API Keys in the dashboard, as the Quickstart explains, and set it as NACE_API_KEY. Then import the default provider:

import { nace } from "@nace-ai/ai-sdk-provider";

createNace makes a provider with other settings:

import { createNace } from "@nace-ai/ai-sdk-provider";

const nace = createNace({
	apiKey: process.env.NACE_API_KEY,
	baseURL: "https://console.nace.ai/v1",
	headers: { "X-Nace-Client": "my-app/1.2.0" },
});
  • apiKey defaults to NACE_API_KEY. That variable is only sent to https://console.nace.ai and https://drex.nace.ai, so any other baseURL, such as your own proxy, needs apiKey passed explicitly.
  • baseURL defaults to NACE_BASE_URL, then https://console.nace.ai/v1.
  • headers are added to every request. Requests send X-Nace-Client: vercel-ai-sdk/<version>; set your own X-Nace-Client (name/version) to have Nace record your application as the client instead.
  • fetch replaces the global fetch.

Requests never follow a redirect, so your key is never forwarded to another host.

Text

import { experimental_decide as decide } from "ai";
import { nace } from "@nace-ai/ai-sdk-provider";

const { answers, providerMetadata } = await decide({
	model: nace.decisionModel("drex-v2.0"),
	state: "My order #1042 arrived broken and I want my money back.",
	questions: {
		refund: { type: "boolean", instructions: "Does the customer ask for a refund?" },
		topic: {
			type: "choice",
			instructions: "What is the ticket about?",
			criteria: {
				billing: "Payments, refunds, invoices",
				shipping: "Delivery and damaged items",
				other: null,
			},
		},
		urgency: {
			type: "score",
			instructions: "How urgent is the ticket?",
			criteria: ["Not urgent", "Somewhat urgent", "Very urgent"],
		},
	},
});

answers.refund.probability; // 0.93
answers.topic.choice; // "shipping"
answers.urgency.score; // 0.86, between 0 and 2
providerMetadata?.nace?.confidence; // { refund: 0.93, topic: 0.32, urgency: 0.7, ... }

Images

Drex Max reads images. Put them in the state as file parts: they reach the model in state order as Image 1, Image 2 and so on, so refer to them by those names.

import { readFile } from "node:fs/promises";
import { experimental_decide as decide } from "ai";
import { nace } from "@nace-ai/ai-sdk-provider";

const { answers } = await decide({
	model: nace.decisionModel("drex-max-v1.1"),
	state: [
		{ type: "text", text: "Image 1 and Image 2 are product photos." },
		{ type: "file", mediaType: "image/png", data: await readFile("red.png") },
		{ type: "file", mediaType: "image/png", data: await readFile("blue.png") },
	],
	questions: {
		firstIsRed: { type: "boolean", instructions: "Is Image 1 red?" },
		blueImage: {
			type: "choice",
			instructions: "Which image is blue?",
			criteria: { first: "Image 1", second: "Image 2" },
		},
	},
});

An image can be PNG, JPEG or WebP, given as bytes, base64, or a URL that the AI SDK downloads first. The provider sends them as the request's images array, so the image limits apply: at most 8 images and a 4 MiB body, base64 included. Any other model answers an image with a 422.

Models

Any model id is sent unchanged, so a new version works before the package lists it.

ModelReadsPer 1M input tokens
drex-max-v1.1text, JSON, images$0.03
drex-maxtext, JSON, images$0.03
drex-v2.0text, JSON$0.06
drex-v1.5text, JSON$0.05
drex-latestthe alias described on Models

The model that answered is response.modelId. Pricing has the billing rules: input tokens only, and only for a successful request.

Questions and answers

AI SDK questionDrex questionAnswer
booleannoul. instructions, criteria.true and criteria.false can each be left out, as long as one is set.probability that the statement is true
choicechoice. A null description is allowed.choice and probabilities
scorescore. A null level is sent as an empty label.score, the probability-weighted mean of the level indices, and probabilities
boxNot supported. decide rejects it before it sends a request.

Instructions and criteria given as JSON values are sent as JSON text. A state with one JSON value is sent as that value, and a state of several text and JSON parts is joined into one string.

providerMetadata.nace has each answer's confidence, as Reading answers defines it, and the requestId to quote to support. usage has the input and output tokens. A request holds at most 512 questions; Limits has the rest.

Errors and retries

A failed request throws the AI SDK's APICallError with the status and message of the error. decide retries a 429, a 5xx and a 529 overloaded up to maxRetries times, 2 by default, and honors Retry-After. The provider itself never retries, so a request is sent at most maxRetries + 1 times. A 4xx such as 422 is not retried.

maxOutputTokens, reasoning and providerOptions.nace have no effect: they are left out of the request and listed in the result's warnings.

On this page