@datalabrotterdam/nova-sdk
v1.3.2
Published
TypeScript client for the Nova AI API
Downloads
245
Readme
Public SDK
TypeScript client for the Nova AI API.
- one client
- typed request and response objects
- small surface area
- built-in streaming helper for chat completions
- image generation helper
- audio upload and speech helpers
- TSDoc-generated API docs in the package
docs/directory
Install
npm install @datalabrotterdam/nova-sdkJSR:
deno add jsr:@nova-ai/client-sdkCreate A Client
import {NovaAI} from "@datalabrotterdam/nova-sdk";
const client = new NovaAI({
apiKey: process.env.NOVA_API_KEY!
});If you do not pass baseUrl, the client uses https://api.nova.datalabrotterdam.nl/v1.
Example: Normal Chat
import {NovaAI} from "@datalabrotterdam/nova-sdk";
const client = new NovaAI({
apiKey: process.env.NOVA_API_KEY!
});
const completion = await client.chat.completions.create({
model: "llama3.2",
messages: [
{role: "system", content: "You are a helpful assistant."},
{role: "user", content: "Explain Nova AI in one sentence."}
],
max_tokens: 256,
temperature: 0.7
});
console.log(completion.choices[0]?.message.content);
console.log(completion.usage_source);
console.log(completion.metrics?.tokens_per_second);completion.usage_source is:
"provider"when the upstream provider reported usage"gateway_estimated"when Nova AI estimated usage to keep the contract consistent
Example: Streaming Chat
import {NovaAI, collectChatText} from "@datalabrotterdam/nova-sdk";
const client = new NovaAI({
apiKey: process.env.NOVA_API_KEY!
});
let streamedText = "";
for await (const event of client.chat.completions.stream({
model: "llama3.2",
messages: [
{role: "user", content: "List three uses for embeddings."}
]
})) {
if (event.type === "chunk") {
streamedText += event.data.choices?.[0]?.delta?.content ?? "";
}
}
console.log(streamedText);
console.log(await collectChatText(client.chat.completions.stream({
model: "llama3.2",
messages: [{role: "user", content: "Summarize vectors in one line."}]
})));Example: Embeddings
import {NovaAI} from "@datalabrotterdam/nova-sdk";
const client = new NovaAI({
apiKey: process.env.NOVA_API_KEY!
});
const embeddings = await client.embeddings.create({
model: "nomic-embed-text",
input: "Nova AI"
});
console.log(embeddings.data[0]?.embedding.length);Example: Image Generation
import {NovaAI} from "@datalabrotterdam/nova-sdk";
const client = new NovaAI({
apiKey: process.env.NOVA_API_KEY!
});
const images = await client.images.generations.create({
model: "dall-e-3",
prompt: "A minimalist line-art skyline of Rotterdam at sunrise",
response_format: "b64_json"
});
console.log(images.created, images.data.length);Example: Audio Transcription
import {NovaAI} from "@datalabrotterdam/nova-sdk";
import {readFile} from "node:fs/promises";
const client = new NovaAI({
apiKey: process.env.NOVA_API_KEY!
});
const file = new File([await readFile("./sample.wav")], "sample.wav", {
type: "audio/wav"
});
const transcript = await client.audio.transcriptions.create({
model: "whisper-1",
file,
language: "nl"
});
console.log(transcript.text);Example: Speech Synthesis
import {NovaAI} from "@datalabrotterdam/nova-sdk";
import {writeFile} from "node:fs/promises";
const client = new NovaAI({
apiKey: process.env.NOVA_API_KEY!
});
const audio = await client.audio.speech.create({
model: "Qwen/Qwen3-TTS-12Hz-1.7B-Base",
input: "Hello from Nova AI",
task_type: "Base",
ref_audio: "https://example.com/reference.wav",
ref_text: "Exact transcript of the reference audio.",
response_format: "wav"
});
await writeFile("speech.wav", Buffer.from(await audio.arrayBuffer()));Included Methods
providers.list()models.list({ limit, after })chat.completions.create(request, options?)chat.completions.stream(request, options?)embeddings.create(request, options?)images.generations.create(request, options?)audio.transcriptions.create(request, options?)audio.translations.create(request, options?)audio.speech.create(request, options?)audio.voices.list(options?)
Runtime Scope Notes
audio.transcriptions.create()andaudio.translations.create()sendmultipart/form-data.audio.speech.create()returns aBlob.audio.voices.list()only exposes non-uploaded voices returned by the gateway.- The gateway enforces runtime scopes:
images:generateaudio:transcribeaudio:translateaudio:synthesis
Request Options
Use configId to force one runtime configuration and requestId to pass your own trace id.
await client.chat.completions.create(
{
model: "llama3.2",
messages: [{role: "user", content: "hello"}]
},
{
configId: "cfg_123",
requestId: "req_abc"
}
);Errors
Failed requests throw NovaAIError.
import {NovaAIError} from "@datalabrotterdam/nova-sdk";
try {
await client.models.list();
} catch (error) {
if (error instanceof NovaAIError) {
console.error(error.status, error.message, error.requestId);
}
}Streaming
chat.completions.stream() returns an async iterable of server-sent events, so you can use it directly with for await.
for await (const event of client.chat.completions.stream({
model: "llama3.2",
messages: [{role: "user", content: "Write a short poem."}]
})) {
if (event.type === "chunk") {
process.stdout.write(event.data.choices?.[0]?.delta?.content ?? "");
}
}Build Docs
cd packages/typescript/client-sdk
npm install
npm run docs