SDK
@vovix/llm is a small typed client for Node.js services. It depends on nothing but fetch: no AWS SDK, no credentials, so it runs anywhere your service does.
Official SDK
TypeScript · @vovix/llm
Typed client for Node.js services. Uses fetch only — no AWS SDK, no IAM — and retries gateway throttles for you.
Install
npm install @vovix/llm
It has no runtime dependencies and needs Node.js 18 or later for fetch. The package is published from packages/sdk of the vovix-core repository: npmjs.com/package/@vovix/llm.
Create a client
import { createClient } from '@vovix/llm'
const llm = createClient({
baseUrl: 'https://api.llm.vovix.io/v1',
apiKey: process.env.LLM_API_KEY!,
})
- Name
baseUrl- Type
- string
- Description
The API base URL, including
/v1and without a trailing slash.
- Name
apiKey- Type
- string
- Description
Your service's API key. Sent as
x-api-key.
- Name
timeoutMs- Type
- number
- Description
Budget for the whole call, every retry included. Default
120000.
- Name
maxAttempts- Type
- number
- Description
Total attempts, including the first. Default
3.
- Name
fetch- Type
- typeof fetch
- Description
Replace the fetch implementation — for tests, or to pin DNS.
Methods
| Method | Endpoint |
|---|---|
llm.chat(req) | POST /v1/chat |
llm.object<T>(req) | POST /v1/objects |
llm.runAgent(req) | POST /v1/agents/run, once per turn — see Agents |
llm.agent(req) | POST /v1/agents/run, one turn |
llm.usage() | GET /v1/usage |
llm.models() | GET /v1/models |
Retries
The SDK retries what the gateway returns when it is busy: 429 from your usage plan, and 500, 502, 503, 504. It waits with exponential backoff and full jitter, honours retry-after (capped at 20 seconds), and gives up when maxAttempts or timeoutMs runs out.
It never retries BUDGET_EXCEEDED: the daily budget resets at midnight UTC, so another attempt only spends another request. Upstream model throttling is already retried inside the API before you see an error.
Errors
Every failure is a VovixLlmError — including timeouts and network failures, which never reach the API. Branch on code, not message: messages can change.
import { VovixLlmError } from '@vovix/llm'
try {
await llm.chat({ model: 'google.gemma-3-12b-it', messages, maxTokens: 400 })
} catch (e) {
if (e instanceof VovixLlmError && e.code === 'BUDGET_EXCEEDED') {
// degrade gracefully until midnight UTC
}
throw e
}
- Name
status- Type
- number
- Description
HTTP status of the final attempt, or
0when no response was received.
- Name
code- Type
- string
- Description
The API error code — see Errors — or one of the client-side codes below.
- Name
requestId- Type
- string
- Description
Upstream request id, when the model was reached. Include it when reporting a problem.
Client-side codes
These are raised by the SDK itself, when there is no API response to read a code from.
| Status | Code | Meaning |
|---|---|---|
0 | TIMEOUT | The whole call, every retry included, ran past timeoutMs. |
0 | NETWORK | The request couldn't reach the API (DNS, TLS, connection reset) after retries. |
| any | BAD_RESPONSE | The API answered with a success status but a body that isn't JSON. |