ultrasafeai
v1.1.4
Published
UltrasafeAI REST API with comprehensive endpoints for AI services
Maintainers
Readme
UltrasafeAI TypeScript SDK
The official TypeScript / JavaScript SDK for the UltrasafeAI API. Provides access to chat completions, vision, embeddings, reranking, image generation, speech-to-text, text-to-speech, real-time audio streaming, vector stores, assistants, threads, and more.
Works in Node.js 18+, Deno, Bun, and modern browsers via native fetch.
Base URL: https://api.us.tech/v1
Installation
npm install ultrasafeai
# or
yarn add ultrasafeaiClient Setup
The client reads ULTRASAFEAI_API_KEY from the environment automatically if apiKey is not passed.
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({
apiKey: process.env.ULTRASAFEAI_API_KEY,
});Options:
| Parameter | Type | Description |
|---|---|---|
| apiKey | string | Your UltrasafeAI API key |
| environment | string \| object | Environment override |
| baseUrl | string | Override the base URL |
| timeoutInSeconds | number | Request timeout (default: 60) |
| maxRetries | number | Max retry attempts |
| fetch | FetchFunction | Custom fetch implementation |
Chat Completions
Non-Streaming
Method: client.chat.completions.create(request)
Endpoint: POST /chat/completions
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const response = await client.chat.completions.create({
model: "usf-mini",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello!" }
]
});
console.log(response.choices[0].message.content);Payload: CreateCompletionsRequest
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | Model ID (e.g. "usf-mini") |
| messages | array | Yes | Conversation history. Roles: "system", "user", "assistant", "tool" |
| tools | array | No | Function or custom tools the model may call |
| tool_choice | string \| object | No | "none", "auto", "required", or a specific tool |
| parallel_tool_calls | boolean | No | Allow parallel tool calls (default: true) |
| reasoning_effort | string | No | Controls reasoning depth: "none", "low", "medium", "high" (default: "none") |
| web_search | boolean | No | Enable web search (default: false) |
| response_format | object | No | {type: "text"}, {type: "json_object"}, or {type: "json_schema", json_schema: {...}} |
| max_tokens | number | No | Max tokens to generate |
| temperature | number | No | Sampling temperature 0–2 |
| top_p | number | No | Nucleus sampling probability mass |
| n | number | No | Number of completions to generate |
| stop | string \| string[] | No | Stop sequences (up to 4) |
| presence_penalty | number | No | Penalty for repeated tokens (-2.0 to 2.0) |
| frequency_penalty | number | No | Frequency-based penalty (-2.0 to 2.0) |
| seed | number | No | Seed for deterministic sampling |
| store | boolean | No | Store conversation for retrieval |
| conversation_id | string | No | Continue an existing stored conversation |
| user | string | No | Stable end-user identifier |
Response: ChatCompletion
{
id: "chatcmpl-abc123",
object: "chat.completion",
created: 1700000000,
model: "usf-mini",
conversation_id: "conv_xyz", // present when store=true
choices: [
{
index: 0,
message: {
role: "assistant",
content: "Hello! How can I help you?",
tool_calls: null, // array of tool calls when finish_reason="tool_calls"
refusal: null
},
finish_reason: "stop" // "stop", "length", "tool_calls", "content_filter"
}
],
usage: {
prompt_tokens: 12,
completion_tokens: 10,
total_tokens: 22
}
}Streaming
Method: client.chat.completions.create(request) with stream: true
Endpoint: POST /chat/completions (with stream: true)
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const stream = await client.chat.completions.create({
model: "usf-mini",
messages: [{ role: "user", content: "Tell me a joke" }],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices?.[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}Payload: CreateCompletionsStreamRequest — same fields as non-streaming.
Response: Stream<ChatCompletionChunk>
Each chunk:
{
id: "chatcmpl-abc123",
object: "chat.completion.chunk",
created: 1700000000,
model: "usf-mini",
choices: [
{
index: 0,
delta: {
role: "assistant", // only on first chunk
content: "Hello", // incremental text; concatenate across chunks
reasoning_content: null, // chain-of-thought when available
tool_calls: null // incremental tool call data
},
finish_reason: null // non-null only on the final chunk
}
],
usage: null // present only on last chunk when stream_options.include_usage=true
}Vision
Vision uses the same chat.completions.create / createStream methods. Pass an array of content parts instead of a plain string for the content field of a user message.
Non-Streaming
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const response = await client.chat.completions.create({
model: "usf-mini",
messages: [
{
role: "user",
content: [
{ type: "text", text: "What is in this image?" },
{
type: "image_url",
image_url: { url: "https://example.com/image.jpg" }
}
]
}
]
});
console.log(response.choices[0].message.content);Base64 image:
import fs from "fs";
const imageData = fs.readFileSync("image.jpg").toString("base64");
const response = await client.chat.completions.create({
model: "usf-mini",
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe this image." },
{
type: "image_url",
image_url: { url: `data:image/jpeg;base64,${imageData}` }
}
]
}
]
});Streaming
const stream = await client.chat.completions.create({
model: "usf-mini",
messages: [
{
role: "user",
content: [
{ type: "text", text: "What's in this image?" },
{ type: "image_url", image_url: { url: "https://example.com/image.jpg" } }
]
}
],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices?.[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}Content part types:
| Type | Fields | Description |
|---|---|---|
| "text" | text: string | Plain text content |
| "image_url" | image_url: { url: string } | URL or data:image/...;base64,... string |
Response: Same ChatCompletion / ChatCompletionChunk as standard chat completions.
Embeddings
Method: client.embeddings.create(request)
Endpoint: POST /embeddings
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
// Single string
const response = await client.embeddings.create({
model: "usf-embed",
input: "The quick brown fox"
});
console.log(response.data[0].embedding); // number[]
// Multiple strings
const batchResponse = await client.embeddings.create({
model: "usf-embed",
input: ["First sentence", "Second sentence"],
dimensions: 512
});Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | Embedding model ID (e.g. "usf-embed") |
| input | string \| string[] \| number[] \| number[][] | Yes | Text or token arrays to embed. Max 8192 tokens per input, 300k tokens total |
| dimensions | number | No | Output embedding dimensions (supported on usf-embed and later) |
| encoding_format | string | No | "float" (default) or "base64" |
| user | string | No | End-user identifier |
Response: EmbeddingResponse
{
object: "list",
data: [
{
object: "embedding",
index: 0,
embedding: [0.0023, -0.0142, ...] // number[]
}
],
model: "usf-embed",
usage: {
prompt_tokens: 8,
total_tokens: 8
}
}Reranker
Method: client.rerank.create(request)
Endpoint: POST /rerank
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const response = await client.rerank.create({
model: "usf-rerank",
query: "What is machine learning?",
texts: [
"Machine learning is a subset of AI.",
"The weather is sunny today.",
"Deep learning uses neural networks."
],
top_n: 2
});
for (const result of response.results) {
console.log(result.index, result.relevance_score, result.text);
}Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | Rerank model ID (e.g. "usf-rerank") |
| query | string | Yes | Search query to rank documents against |
| texts | string[] | Yes | Documents to rerank |
| top_n | number | No | Number of top results to return |
Response:
{
results: [
{
index: 0,
relevance_score: 0.97,
text: "Machine learning is a subset of AI."
},
{
index: 2,
relevance_score: 0.85,
text: "Deep learning uses neural networks."
}
]
}Image Generation
Generate
Method: client.images.generate(request)
Endpoint: POST /images/generations
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const response = await client.images.generate({
model: "usf-mini-image",
prompt: "A futuristic city at sunset",
size: "1024x1024",
n: 1,
response_format: "url"
});
console.log(response.data[0].url);Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | Image model ID (e.g. "usf-mini-image") |
| prompt | string | Yes | Text description of the image to generate |
| size | string | No | "256x256", "512x512", "1024x1024" |
| n | number | No | Number of images to generate |
| response_format | string | No | "url" (default) or "b64_json" |
Response: ImageResponse
{
created: 1700000000,
images: [
{ url: "https://..." }, // when response_format="url"
{ b64_json: "iVBORw..." } // when response_format="b64_json"
],
data: [...] // same contents as images; present for OpenAI-compat clients
}Access via either field — both contain typed Image objects:
console.log(response.images[0].url); // preferred
console.log(response.data[0].url); // OpenAI-compat aliasSpeech to Text
Method: client.audio.transcriptions.create(request)
Endpoint: POST /audio/transcribe
import fs from "fs";
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const response = await client.audio.transcriptions.create({
file: fs.createReadStream("audio.mp3"),
model: "usf-mini-asr",
language: "en",
response_format: "json"
});
console.log(response.text);Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | Audio file (mp3, mp4, wav, flac, ogg, webm, etc.) |
| model | string | Yes | ASR model ID (e.g. "usf-mini-asr") |
| language | string | No | ISO 639-1 language code (e.g. "en", "es") |
| response_format | string | No | "json" (default), "text", "srt", "verbose_json", "vtt" |
Response: TranscriptionResponse
{
text: "Hello, this is a transcription.",
language: "en",
duration: 3.5
}Live ASR (WebSocket)
Live ASR uses a WebSocket-based client. Access it via client.audio.stream.connect().
Class: AudioStreamSession
Endpoint: wss://api.us.tech/v1/audio/stream
import { UltrasafeAI } from "ultrasafeai";
import fs from "fs";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const session = await client.audio.stream.connect({
model: "usf-mini-asr",
sampleRate: 16000,
audioFormat: "pcm_s16le",
enableVad: false,
partialResults: true,
interimMinDurationMs: 500,
fullContextRetranscription: true
});
session.on("ready", (event) => {
console.log("Connected — streaming audio");
});
session.on("transcript", (event) => {
console.log(event.full_text);
});
session.on("close", (code, reason) => {
console.log(`Closed: ${code} ${reason}`);
});
// Send PCM audio frames as Uint8Array or ArrayBuffer
const audioBuffer = fs.readFileSync("audio.raw");
session.send(new Uint8Array(audioBuffer));
// Close when done
session.close();Connect options:
| Parameter | Type | Default | Description |
|---|---|---|---|
| model | string | "usf-mini-asr" | ASR model ID |
| sampleRate | number | 16000 | Audio sample rate in Hz |
| audioFormat | string | "pcm_s16le" | "pcm_s16le" or "pcm_f32le" |
| enableVad | boolean | false | Enable voice activity detection |
| partialResults | boolean | true | Emit partial results before segment is final |
| interimMinDurationMs | number | 500 | Min audio duration (ms) before emitting interim |
| fullContextRetranscription | boolean | true | Re-transcribe with full audio context for accuracy |
Session methods:
| Method | Description |
|---|---|
| send(audio: Uint8Array \| ArrayBufferLike) | Send a PCM audio frame |
| on(event, handler) | Subscribe to a server event |
| off(event, handler) | Unsubscribe from a server event |
| close() | Close the session gracefully |
Session events:
| Event | Handler type | Description |
|---|---|---|
| ready | (event: TranscriptEvent & { type: "ready" }) => void | Server ready to receive audio |
| transcript | (event: TranscriptEvent & { type: "transcript" }) => void | Transcription result (partial or final) |
| speech_activity | (event: TranscriptEvent & { type: "speech_activity" }) => void | VAD speech start/end |
| control | (event: TranscriptEvent & { type: "control" }) => void | Lifecycle signal (action: "stop") |
| error | (event: TranscriptEvent & { type: "error" }) => void | Server-side error |
| close | (code: number, reason: string) => void | Connection closed |
| ws_error | (error: Error) => void | WebSocket error |
| parse_error | (error: Error, raw: string) => void | A frame could not be decoded/parsed (surfaced, not swallowed) |
TranscriptEvent shape:
{
type: "transcript", // "ready" | "speech_activity" | "transcript" | "control" | "error"
request_id: "req_abc",
is_final: true,
full_text: "Hello world this is a test",
committed_text: "Hello world",
segment: {
id: 3,
text: "this is a test",
is_final: true,
start: 1.2,
end: 2.8,
confidence: 0.95
}
}Vector Stores
Access: client.vectorStores
Create Vector Store
Method: client.vectorStores.create(request)
Endpoint: POST /vector_stores
import fs from "fs";
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const store = await client.vectorStores.create({
name: "My Knowledge Base",
files: [fs.createReadStream("document.pdf")]
});
console.log(store.id); // e.g. "vs_abc123"
console.log(store.status); // poll until "ready"Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name for the vector store |
| files | File[] | No | Files to upload and index immediately |
Response: VectorStore
{
id: "vs_abc123",
object: "vector_store",
created_at: 1700000000,
name: "My Knowledge Base",
status: "ready",
file_counts: { in_progress: 0, completed: 1, failed: 0, cancelled: 0, total: 1 }
}List Vector Stores
const response = await client.vectorStores.list({ limit: 20 });
for (const store of response.data) {
console.log(store.id, store.name, store.status);
}Retrieve Vector Store
const store = await client.vectorStores.retrieve("vs_abc123");
console.log(store.status);Delete Vector Store
const result = await client.vectorStores.delete("vs_abc123");
console.log(result.deleted); // trueSearch Vector Store
Method: client.vectorStores.search(vectorStoreId, request)
Endpoint: POST /vector_stores/{vector_store_id}/search
const results = await client.vectorStores.search("vs_abc123", {
query: "What is the refund policy?"
});
for (const item of results.data) {
console.log(item);
}File Management
Upload File to Vector Store
const file = await client.vectorStores.uploadFile("vs_abc123", {
file: fs.createReadStream("doc.pdf")
});List Vector Store Files
const files = await client.vectorStores.listFiles("vs_abc123", { limit: 20 });
for (const f of files.data) {
console.log(f.id);
}Retrieve / Delete Vector Store File
const file = await client.vectorStores.retrieveFile("vs_abc123", "file_xyz");
const result = await client.vectorStores.deleteFile("vs_abc123", "file_xyz");
console.log(result.deleted); // trueFile Batches
// Create a batch of files by ID
const batch = await client.vectorStores.createFileBatch("vs_abc123", {
file_ids: ["file_abc", "file_def"]
});
// Retrieve batch status
const status = await client.vectorStores.retrieveFileBatch("vs_abc123", batch.id);
// Cancel a running batch
await client.vectorStores.cancelFileBatch("vs_abc123", batch.id);
// List files in a batch
const batchFiles = await client.vectorStores.listBatchFiles("vs_abc123", batch.id);Assistants
Access: client.assistants
Create Assistant
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const assistant = await client.assistants.create({
model: "usf-mini",
name: "My Assistant",
description: "A helpful customer support bot",
instructions: "You are a customer support agent. Be concise and friendly.",
tools: [{ type: "code_interpreter" }],
temperature: 0.5
});
console.log(assistant.id);Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | Model ID |
| name | string | No | Assistant name |
| description | string | No | Short description |
| instructions | string | No | System prompt / instructions |
| tools | object[] | No | Tool definitions (e.g. [{ type: "code_interpreter" }]) |
| tool_resources | object | No | Resources for tools |
| metadata | object | No | Arbitrary key-value metadata |
| temperature | number | No | Sampling temperature |
| top_p | number | No | Nucleus sampling |
| response_format | string | No | Response format |
Response: Assistant
{
id: "asst_abc123",
object: "assistant",
created_at: 1700000000,
name: "My Assistant",
description: "A helpful customer support bot",
model: "usf-mini",
instructions: "You are a customer support agent.",
tools: [{ type: "code_interpreter" }]
}List Assistants
const assistants = await client.assistants.list({ limit: 20 });
for (const asst of assistants.data) {
console.log(asst.id, asst.name);
}Payload:
| Parameter | Type | Description |
|---|---|---|
| limit | number | Max items to return |
| after | string | Pagination cursor |
Retrieve Assistant
const assistant = await client.assistants.retrieve("asst_abc123");
console.log(assistant.name);Delete Assistant
const result = await client.assistants.delete("asst_abc123");
console.log(result.deleted); // trueResponse: DeletedResponse
{ id: "asst_abc123", object: "assistant.deleted", deleted: true }Threads
Access: client.threads
Create Thread
Method: client.threads.create(request)
Endpoint: POST /threads
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const thread = await client.threads.create({
messages: [
{ role: "user", content: "Hello, I need help with my account." }
]
});
console.log(thread.id); // e.g. "thread_abc123"Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| messages | object[] | No | Initial messages to seed the thread |
| metadata | object | No | Arbitrary key-value metadata |
Response: Thread
{
id: "thread_abc123",
object: "thread",
created_at: 1700000000,
metadata: {}
}List Threads
const threads = await client.threads.list({ limit: 20 });
for (const t of threads.data) {
console.log(t.id, t.created_at);
}Retrieve Thread
const thread = await client.threads.retrieve("thread_abc123");
console.log(thread.id);Thread Messages
Thread messages are managed via client.threads.addMessage and client.threads.listMessages.
Add Message to Thread
Method: client.threads.addMessage(threadId, request)
Endpoint: POST /threads/{thread_id}/messages
const message = await client.threads.addMessage("thread_abc123", {
role: "user",
content: "Can you summarize my previous question?"
});
console.log(message.id); // e.g. "msg_xyz"
console.log(message.role); // "user"Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| role | string | Yes | Message role: "user" or "assistant" |
| content | string | Yes | Text content of the message |
| attachments | object[] | No | File attachments |
| metadata | object | No | Arbitrary key-value metadata |
Response: Message
{
id: "msg_xyz",
object: "thread.message",
created_at: 1700000000,
thread_id: "thread_abc123",
role: "user",
content: [{ type: "text", text: { value: "Can you summarize my previous question?" } }]
}List Messages in Thread
Method: client.threads.listMessages(threadId, params?)
Endpoint: GET /threads/{thread_id}/messages
const messages = await client.threads.listMessages("thread_abc123", { limit: 20 });
for (const msg of messages.data) {
console.log(msg.role, msg.content);
}Run Thread with Assistant
Method: client.threads.run(threadId, request)
Endpoint: POST /threads/{thread_id}/runs
const run = await client.threads.run("thread_abc123", {
assistant_id: "asst_abc123",
model: "usf-mini",
instructions: "Be concise."
});
console.log(run.id); // e.g. "run_abc"
console.log(run.status); // "queued" | "in_progress" | "completed" | "failed"Payload:
| Parameter | Type | Required | Description |
|---|---|---|---|
| assistant_id | string | Yes | Assistant to use for this run |
| model | string | No | Override the assistant's model |
| instructions | string | No | Override the assistant's instructions |
| tools | object[] | No | Override the assistant's tools |
| metadata | object | No | Arbitrary key-value metadata |
Models
Access: client.models
List Models
Method: client.models.list()
Endpoint: GET /models
import { UltrasafeAI } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const response = await client.models.list();
for (const model of response.data) {
console.log(model.id, model.type, model.description);
}Response: ListModelsResponse
{
object: "list",
data: [
{
id: "usf-mini",
object: "model",
name: "USF Mini",
type: "chat",
description: "Fast and efficient chat model",
is_active: true,
created: 1700000000,
owned_by: "ultrasafeai"
}
]
}Retrieve Model
Method: client.models.retrieve(model)
Endpoint: GET /models/{model}
const model = await client.models.retrieve("usf-mini");
console.log(model.id, model.is_active);Response: Model
{
id: "usf-mini",
object: "model",
name: "USF Mini",
type: "chat",
description: "Fast and efficient chat model",
is_active: true,
created: 1700000000,
owned_by: "ultrasafeai"
}Error Handling
import { UltrasafeAI, UltrasafeaiApi } from "ultrasafeai";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
try {
const response = await client.chat.completions.create({
model: "usf-mini",
messages: [{ role: "user", content: "Hello" }]
});
} catch (error) {
if (error instanceof UltrasafeaiApi.UnauthorizedError) {
console.error("Invalid API key:", error.message);
} else if (error instanceof UltrasafeaiApi.BadRequestError) {
console.error("Bad request:", error.message);
} else {
throw error;
}
}| Error class | HTTP Status | Description |
|---|---|---|
| UltrasafeaiApi.UnauthorizedError | 401 | Invalid or missing API key |
| UltrasafeaiApi.BadRequestError | 400 | Invalid request parameters |
| UltrasafeaiApiError | any | General API error with statusCode and body |
| UltrasafeaiApiTimeoutError | — | Request timed out |
Accessing Raw HTTP Response
const result = await client.chat.completions.create({
model: "usf-mini",
messages: [{ role: "user", content: "Hello" }]
});
// Access via .withRawResponse() for headers / status
const raw = await client.chat.completions.withRawResponse().create({
model: "usf-mini",
messages: [{ role: "user", content: "Hello" }]
});
console.log(raw.rawResponse.status);
console.log(raw.data.choices[0].message.content);Retries
The client automatically retries on connection errors, timeouts, and 429/5xx responses with exponential backoff. Default is 2 retries.
// Disable retries
const client = new UltrasafeAI({ apiKey: "...", maxRetries: 0 });
// Increase retries
const client = new UltrasafeAI({ apiKey: "...", maxRetries: 5 });
// Override per request
await client.chat.completions.create({ model: "usf-mini", messages: [...] }, { maxRetries: 0 });Timeouts
Requests time out after 60 seconds by default.
// Set globally
const client = new UltrasafeAI({ apiKey: "...", timeoutInSeconds: 30 });
// Override per request
await client.chat.completions.create({ model: "usf-mini", messages: [...] }, { timeoutInSeconds: 10 });Lib Helpers
The SDK ships ergonomics helpers as named subpath imports.
Streaming Accumulator
Import: import { ChatCompletionStream } from "ultrasafeai/lib/streaming/chat"
Wraps a raw stream: true response and accumulates deltas so you can read the final assembled message after iteration.
import { UltrasafeAI } from "ultrasafeai";
import { ChatCompletionStream } from "ultrasafeai/lib/streaming/chat";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const raw = await client.chat.completions.create({
model: "usf-mini",
messages: [{ role: "user", content: "Count from 1 to 5." }],
stream: true,
});
const stream = new ChatCompletionStream(raw);
for await (const chunk of stream) {
const delta = chunk.choices?.[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
const completion = stream.getFinalCompletion();
console.log(completion.choices[0].finish_reason); // "stop"
console.log(completion.choices[0].message.content); // full assembled textMethods:
| Method | Description |
|---|---|
| new ChatCompletionStream(stream) | Wrap a raw AsyncIterable of chunks |
| for await (chunk of stream) | Iterate and accumulate simultaneously |
| await stream.untilDone() | Drain the stream without iterating manually, returns this |
| stream.getFinalCompletion() | Returns assembled { object, choices } — call after stream is consumed |
Tool Helpers
Import: import { functionTool, normalizeTool, normalizeTools } from "ultrasafeai/lib/tools"
import { functionTool, normalizeTools } from "ultrasafeai/lib/tools";
import { zodToJsonSchema } from "zod-to-json-schema";
import { z } from "zod";
// Build a flat tool from a Zod schema
const WeatherSchema = z.object({
city: z.string().describe("City name"),
unit: z.enum(["celsius", "fahrenheit"]).optional(),
});
const weatherTool = functionTool(
"get_weather",
"Get the current weather for a city.",
zodToJsonSchema(WeatherSchema)
);
const response = await client.chat.completions.create({
model: "usf-mini",
messages: [{ role: "user", content: "What's the weather in London?" }],
tools: [weatherTool],
});API accepts flat format (name/description/parameters at top level). If you have tools in OpenAI nested format ({ type: "function", function: {...} }), normalise them first:
import { normalizeTools } from "ultrasafeai/lib/tools";
const tools = normalizeTools([
{ type: "function", function: { name: "get_weather", description: "...", parameters: {...} } }
]);
// → [{ name: "get_weather", description: "...", parameters: {...} }]Interfaces:
| Interface | Shape | Description |
|---|---|---|
| ChatFunctionTool | { name, description?, parameters?, strict? } | Flat format — what the API expects |
| FunctionTool | { type: "function", function: { name, description?, parameters? } } | Nested format — for migration from other SDKs |
Functions:
| Function | Description |
|---|---|
| functionTool(name, description, parameters, opts?) | Build a flat ChatFunctionTool |
| normalizeTool(tool) | Convert a flat or nested tool to flat |
| normalizeTools(tools[]) | Normalize an array of tools to flat |
| toolMessage({ tool_call_id, name, content }) | Build a tool role message for the conversation |
Structured Output
Import: import { parseChatCompletion } from "ultrasafeai/lib/parsing"
Parse a completion's JSON content into a typed value using a Zod schema (or any object with a .parse() method).
import { UltrasafeAI } from "ultrasafeai";
import { parseChatCompletion } from "ultrasafeai/lib/parsing";
import { z } from "zod";
const client = new UltrasafeAI({ apiKey: "YOUR_API_KEY" });
const SentimentSchema = z.object({
sentiment: z.enum(["positive", "neutral", "negative"]),
confidence: z.number(),
});
const raw = await client.chat.completions.create({
model: "usf-mini",
messages: [
{ role: "system", content: "Respond with JSON only." },
{ role: "user", content: "The product is excellent!" },
],
response_format: { type: "json_object" },
});
const result = parseChatCompletion(raw, SentimentSchema);
console.log(result.parsed.sentiment); // "positive"
console.log(result.parsed.confidence); // 0.97API: parseChatCompletion(completion, schema) — parses choices[0].message.content as JSON, calls schema.parse(data), and returns ParsedChatCompletion<T> with a .parsed field containing the typed value.
Pagination
Import: import { iterPage, collectPage } from "ultrasafeai/lib/pagination"
List endpoints return Page<T> or CursorPage<T>. These helpers make iteration ergonomic.
import { iterPage, collectPage } from "ultrasafeai/lib/pagination";
const page = await client.models.list();
// Iterate
for (const model of iterPage(page)) {
console.log(model.id);
}
// Or collect into array
const models = collectPage(page);Interfaces:
| Interface | Fields | Description |
|---|---|---|
| Page<T> | data: T[], object: string | Standard list response |
| CursorPage<T> | data, object, has_more, next_cursor? | Cursor-paginated list |
Functions:
| Function | Description |
|---|---|
| iterPage(page) | Generator that yields each item in page.data |
| collectPage(page) | Spreads page.data into a flat array |
