Migrate from TypeSafe
Point the TypeSafe SDK at Drex, then fix the requests Drex rejects on purpose.
Drex is wire-compatible with TypeSafe's Jev. The @typesafe-ai/sdk package works against https://console.nace.ai without a fork. This guide covers the configuration change, the requests that Drex rejects, and a check that the switch worked. It is written against SDK version 0.6.0.
Or switch to the Drex SDK
The TypeScript and Python Drex SDKs are built on the TypeSafe SDKs. systemOne / system_one, the question helpers, the answer types, RetryPolicy, the logging options and the error classes keep their names and arguments, so a migration is mostly a rename:
| TypeSafe | Drex |
|---|---|
@typesafe-ai/sdk, typesafe-sdk | drex-sdk on npm and PyPI |
TypeSafeClient, AsyncTypeSafeClient | DrexClient, AsyncDrexClient |
TYPESAFE_API_KEY, TYPESAFE_BASE_URL, TYPESAFE_DEFAULT_MODEL, TYPESAFE_LOG_LEVEL | DREX_API_KEY, DREX_BASE_URL, DREX_DEFAULT_MODEL, DREX_LOG_LEVEL |
TypeSafeError, TypeSafeAPIError (Python) | DrexError, DrexAPIError |
The Drex SDKs already default to https://console.nace.ai, drex-latest and a 60-second timeout. They leave out a null instructions rather than sending it, add error classes for 402 and 529, and read request_id from x-request-id. They also cover the document routes. The rest of this guide is for code that keeps the TypeSafe SDK.
Change three environment variables
The SDK reads its base URL, key, and default model from the environment when the constructor does not set them.
TYPESAFE_BASE_URL=https://console.nace.ai
TYPESAFE_API_KEY=nace_sk_...
TYPESAFE_DEFAULT_MODEL=drex-v1.0If your code sets these in the constructor instead, change them there. Explicit options take precedence over environment variables.
import { TypeSafeClient } from "@typesafe-ai/sdk";
const client = new TypeSafeClient({
apiKey: process.env.TYPESAFE_API_KEY,
});import { TypeSafeClient } from "@typesafe-ai/sdk";
const client = new TypeSafeClient({
baseURL: "https://console.nace.ai",
apiKey: process.env.DREX_API_KEY,
defaultModel: "drex-v1.0",
timeout: 60_000,
});Set defaultModel in one of the two places. Without it, the SDK sends jev-latest, and Drex returns 422. The timeout line is explained under Raise the SDK timeout.
Verify the switch
List the models. The SDK unwraps the { "models": [...] } envelope and returns the array.
const models = await client.models.list();
console.log(models.map((m) => m.name));
// [ 'drex-v1.0', 'drex-v1.5', 'drex-latest', 'drex-v1.1' ]The list has each pinned version, then the drex-latest alias, then retired ids such as drex-v1.1, which drex-v1.5 now answers. See Models. GET /v1/models needs a valid key but works with zero credit, so this check does not spend anything. A 401 here means the key is wrong. See Authentication.
What changes
Work through this list before you send production traffic. Each item is a request that TypeSafe accepted and Drex rejects, or a behavior that differs.
Send a drex-* model name
Drex accepts pinned versions such as drex-v1.0, the drex-latest alias, any other drex-* name, and legacy nacedm-* names. Anything else returns 422, so a half-migrated client fails on the first call instead of silently using the wrong model. A request with "model": "jev-latest" returns this issue:
{
"path": "model",
"message": "unknown model \"jev-latest\"; use \"drex-v1.0\", \"drex-v1.5\" or \"drex-latest\""
}Search your code for hard-coded jev- model names and for calls that pass model per request.
Pass instructions as a string
Drex requires instructions to be a string when present. The SDK types allow a JSON object, an array, or null, and noul() with no arguments sends instructions: null. Drex rejects both:
{
"path": "questions.a.instructions",
"message": "must be a string"
}Replace noul() and noul(null, criteria) with noul("Your question as text", criteria). Replace JSON instructions with text.
Pass criteria descriptions as strings
The SDK types allow a JSON object or array as any criteria description. Drex does not.
choice: each label's description must be a string ornull. A JSON description returns the issuemust be a string or nullatquestions.<name>.criteria.<label>.noul: thetrueandfalsedescriptions must be strings ornull. A JSON description returnsmust be a string.score:criteriamust be an array of level labels, each a string. The SDK types allownullentries, but Drex returnsmust be a stringatquestions.<name>.criteria.<index>for anulllevel.
Raise the SDK timeout
The SDK's per-attempt timeout defaults to 10,000 ms. Drex waits up to 55 s for the model on a heavy request, so a large state with many questions can exceed the default and surface as an APITimeoutError. Set timeout: 60_000 on the client, or per call in the second argument to systemOne. The timeout applies per attempt, and the SDK retries APITimeoutError by default. Errors and retries covers retries in detail.
Handle 402 and 529
Drex adds 402 to TypeSafe's statuses. Handle it next to 529, which Drex returns when it cannot serve a request right now.
402with typeinsufficient_creditis new in Drex. It means the account has no spendable credit. The SDK raises this as a plainAPIErrorwithstatus402. It is not retried. Top up or add a card on Billing.402with typepayment_requiredmeans an unpaid invoice; it is not retried either, and the invoice is paid on Billing.529with typeoverloadedmeans Drex is at capacity or the model did not respond in time. It carriesretry-afterandretry-after-ms. The SDK treats it as a5xx, raisesInternalServerErrorif retries run out, and honors theretry-afterheaders while retrying.
Errors lists every type with its status.
Read legend as an array
Drex returns the score answer's legend as an array of level labels. The SDK types describe it as an object keyed by score. Index access works the same either way: answers.sentiment.legend[2] returns the third label.
What stays the same
- The endpoints,
POST /v1/systemoneandGET /v1/models. - The request shape:
state,questions, and optionalmodel. - The response shape:
model,answerskeyed by question name, andusagewithinput_tokensandoutput_tokens. Drex's body also carriesevaluation_time_msandrequest_id, which the SDK'sSystemOneResulttype does not declare. - The statuses
401,422,429, and5xx, which the SDK maps toAuthenticationError,UnprocessableEntityError,RateLimitError, andInternalServerError. - The
x-typesafe-request-idheader. The SDK reads it intoAPIError.requestIdandwithResponse().requestId. Drex sends the same value asx-request-id. - The SDK's retry behavior: 2 retries by default, backoff from 500 ms doubling to 5 s, on
408,429, and500to599, honoringretry-afterandretry-after-ms. - The
noul,choice, andscorehelpers, and the answer types they infer.
Checklist
- Set
TYPESAFE_BASE_URL,TYPESAFE_API_KEY, andTYPESAFE_DEFAULT_MODEL, or the matching constructor options. - Remove hard-coded
jev-*model names. - Make sure that every
instructionsvalue is a string. - Make sure that every criteria description is a string, and that
scorelevels contain nonull. - Raise
timeoutfor large requests. - Handle
402and529where you handle otherAPIErrorcases. - Run
client.models.list()and confirm that it includesdrex-v1.0.