@octoryn/sdk
v0.1.0
Published
Octoryn LLM TypeScript SDK — OpenAI-compatible client for Octoryn Gateway with audit, usage, and BYOK extensions.
Readme
@octoryn/sdk
Octoryn LLM TypeScript SDK — OpenAI-compatible client for Octoryn Gateway.
@octoryn/sdk ships an Octoryn client that talks to the Octoryn Gateway
(default: https://api.octopusos.dev/v1). The chat / images / audio /
embeddings / moderations surface is byte-for-byte OpenAI-compatible, plus
first-class extensions for audit, usage, and BYOK. Runs in Node 18+, Bun,
Cloudflare Workers, Deno, and browsers.
Install
npm install @octoryn/sdk
# or
pnpm add @octoryn/sdk
yarn add @octoryn/sdk
bun add @octoryn/sdkNode 18+ (or any runtime with global fetch and FormData).
Quickstart
import { Octoryn } from "@octoryn/sdk";
const client = new Octoryn({ apiKey: process.env.OCTORYN_API_KEY! });
const out = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Hello, Octoryn" }],
});
console.log(out.choices[0]?.message.content, "run:", out.octoryn.runId);Authentication
API key (recommended)
Get a key in the web console at https://app.octopusos.dev:
const client = new Octoryn({ apiKey: process.env.OCTORYN_API_KEY! });OIDC (refresh-token)
For human / SSO flows the SDK ships an OAuth2 refresh-token credential. It
auto-refreshes the short-lived access token (single-flight) and lets you
persist the new token-set with onRefresh:
import { Octoryn, createOidcCredential } from "@octoryn/sdk";
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
import { join } from "node:path";
const path = join(process.env.HOME!, ".octoryn", "tokens.json");
const cached = existsSync(path) ? JSON.parse(readFileSync(path, "utf8")) : {};
const credential = createOidcCredential({
identityUrl: "https://identity.octopusos.dev",
clientId: "cli",
refreshToken: cached.refresh_token ?? process.env.OCTORYN_REFRESH_TOKEN!,
accessToken: cached.access_token,
expiresAt: cached.expires_at,
onRefresh: (set) => {
mkdirSync(join(process.env.HOME!, ".octoryn"), { recursive: true });
writeFileSync(path, JSON.stringify(set));
},
});
const client = new Octoryn({ credential });Pass either apiKey or credential, never both.
Use the official openai SDK (drop-in)
The Octoryn Gateway is bytewise OpenAI-compatible for chat, images, audio,
embeddings, and moderations. Point the official openai SDK at the Octoryn
base URL and existing code keeps working:
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.octopusos.dev/v1",
apiKey: process.env.OCTORYN_API_KEY!,
});
await client.chat.completions.create({ model: "gpt-4o-mini", messages: [...] });If you'd rather import from @octoryn/sdk while keeping the OpenAI surface,
use the bundled alias:
import { OpenAI } from "@octoryn/sdk/openai-compat";Modality cookbook
Chat
const out = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Summarize the OSI model in one sentence." }],
});
console.log(out.choices[0]?.message.content);
// Stream
const stream = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Count 1..5" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta.content ?? "");
}Images
const res = await client.images.generate({
model: "dall-e-3",
prompt: "a small octopus drawing a vector logo, flat illustration",
size: "1024x1024",
});
console.log(res.data[0]?.url);Speech (TTS)
import { writeFile } from "node:fs/promises";
const speech = await client.audio.speech.create({
model: "tts-1",
voice: "alloy",
input: "Octoryn is online.",
response_format: "mp3",
});
await writeFile("out.mp3", new Uint8Array(await speech.arrayBuffer()));Transcription (STT)
import { readFile } from "node:fs/promises";
const data = await readFile("meeting.mp3");
const tx = await client.audio.transcriptions.create({
file: { data, name: "meeting.mp3", type: "audio/mpeg" },
model: "whisper-1",
});
// In browsers / Workers, pass a `Blob` or `File` directly:
// file: new File([blob], "clip.webm", { type: "audio/webm" })
console.log(tx.text);Video
const job = await client.videos.generateAndWait(
{
model: "veo-3",
prompt: "a slow-motion octopus opening a jar underwater",
duration_seconds: 5,
resolution: "720p",
aspect_ratio: "16:9",
},
{ pollIntervalMs: 2000, timeoutMs: 600_000 },
);
console.log(job.status, job.video_url);Realtime
const session = await client.realtime.sessions.create({
provider: "openai",
model: "gpt-4o-realtime-preview",
voice: "verse",
});
console.log(session.ws_url, session.audio_sample_rate);
// The SDK does not bundle a WebSocket/WebRTC client. Connect to ws_url
// with the transport of your choice (browser WebSocket, ws, undici, ...).Embeddings
const emb = await client.embeddings.create({
model: "text-embedding-3-small",
input: ["hello", "world"],
});
console.log(emb.data.length);Moderations
const mod = await client.moderations.create({ input: "I love clean code." });
console.log(mod.results[0]?.flagged);Models
const list = await client.models.list();
for (const m of list.data) console.log(m.id);Browser & Edge runtimes
@octoryn/sdk is a thin wrapper over fetch and FormData. It works
unmodified in Node 18+, Bun, Cloudflare Workers, Deno, and modern browsers.
For audio.transcriptions you can pass a native Blob or File directly
(no buffer wrapper needed).
Errors & retries
| Class | When |
| ------------------------------ | ------------------------------------------------- |
| OctorynAuthError | 401 / 403 |
| OctorynRateLimitedError | 429 rate_limited — exposes retryAfterS |
| OctorynQuotaExceededError | 429 quota_exceeded |
| OctorynKsiBlockedError | 422 ksi_blocked — exposes reasons, channel |
| OctorynUpstreamError | 503 upstream — exposes upstream |
| OctorynAPIError | base class for all of the above |
import { OctorynRateLimitedError } from "@octoryn/sdk";
try {
await client.chat.completions.create({ model: "gpt-4o-mini", messages: [...] });
} catch (e) {
if (e instanceof OctorynRateLimitedError) {
await new Promise((r) => setTimeout(r, (e.retryAfterS ?? 1) * 1000));
} else {
throw e;
}
}The client retries 5xx and network errors up to 3 times with exponential
backoff. Override per-client with new Octoryn({ apiKey, retry: { maxAttempts: 5, backoffMs: 200 } }).
BYOK / Audit / Usage
const created = await client.byok.create({
provider: "openai",
api_key: "sk-...",
label: "prod",
});
console.log(created.id, created.plaintext_key); // plaintext shown ONCE
const runs = await client.audit.list({ limit: 20 });
const detail = await client.audit.get(runs.data[0]!.id);
const verified = await client.audit.verify(detail.id);
const summary = await client.usage.get();
const records = await client.usage.records({ limit: 50 });Versioning & License
Octoryn follows semver from 1.0.0. Pre-1.0 minor bumps may break.
See CHANGELOG.md (added in Phase 7) and https://octopusos.dev.
Proprietary © Octopus Core Pty Ltd (ACN 696 931 236). Octoryn™ is a trademark of Octopus Core Pty Ltd.
