@oruk-ai/sdk
v0.2.10
Published
Official TypeScript client for the oruk Speech API: English transcription, multilabel emotion and speaking-style scores, and unified audio analysis.
Maintainers
Readme
oruk TypeScript SDK
Official TypeScript client for the oruk Speech API.
oruk analyses prerecorded speech and returns three things from the audio itself: an English transcript, emotion scores across 15 labels, and speaking-style scores across 16 labels. The emotion and style scores are acoustic — they are derived from how something was said, not inferred from the transcript text.
npm install @oruk-ai/[email protected]Requires Node 18 or newer (the client uses the platform fetch, FormData, and
Blob). Works in any runtime that provides those, including Deno, Bun, and
Cloudflare Workers.
Quickstart
import { readFile } from 'node:fs/promises'
import { Oruk } from '@oruk-ai/sdk'
const apiKey = process.env.ORUK_API_KEY
if (!apiKey) throw new Error('Set ORUK_API_KEY in your environment.')
const oruk = new Oruk({ apiKey })
const bytes = await readFile('sample.wav')
const result = await oruk.analyze({
file: new Blob([new Uint8Array(bytes)], { type: 'audio/wav' }),
filename: 'sample.wav',
model: 'oruk-resonance',
})
console.log(result.text) // actual transcript from this recording
console.log(result.emotions) // selected labels and scores
console.log(result.styles) // selected styles; can be emptyCreate an account at oruk.ai/auth/signup, choose a subscription plan, and create your API key. Standard self-serve plans begin with a 7-day trial: a card is required, $0 is charged today, and you can cancel before it ends.
Methods
The first five methods take AudioRequest and return SpeechResult.
proficiency takes ProficiencyRequest (including an optional transcript)
and returns ProficiencyResult, which also contains proficiency and check.
One audio minute uses one plan minute per request; separate calls process and
meter the file separately. See the endpoint reference
for each model and endpoint's output scope.
| Method | Returns |
| --- | --- |
| oruk.transcribe(req) | Transcript only |
| oruk.emotions(req) | Emotion scores only |
| oruk.styles(req) | Speaking-style scores only |
| oruk.affect(req) | Emotion and style scores |
| oruk.analyze(req) | Transcript, emotion, and style in one call |
| oruk.proficiency(req) | English speaking proficiency: CEFR band, 0–5 score, fluency; optional transcript; preview |
type AudioRequest = {
file: Blob // the audio
filename?: string // defaults to 'audio.wav'
model?: OrukModel // defaults to oruk-resonance on the first five endpoints
requestId?: string // defaults to a generated UUID
diarize?: boolean // oruk-resonance only: label speakers, one segment per speaker turn
numSpeakers?: number // known speaker count (1-32); omit to detect automatically
}On the first five endpoints, model: 'oruk-resonance', diarize: true makes
segments correspond to speaker turns. Fields still follow the endpoint:
emotion-only output does not gain a transcript. Segments carry speaker
(speaker_0, speaker_1, ...) and the result carries diarized and
speakers. Labels are local to the recording, not identities or roles.
Diarization is included in plan minutes. Proficiency uses the separate
oruk-proficiency-1 model, not Resonance or Fourier; it does not take diarization
parameters. A supplied proficiency transcript skips built-in transcription.
analyze returns transcript and affect in one request, avoiding separate
transcription and affect calls over the same audio. When you only want emotion, emotions on Resonance runs the encoder and
affect head and never invokes the transcription decoder, so no transcript is
produced (text is null). Either endpoint uses one plan minute per minute of audio.
const tone = await oruk.emotions({ file, filename: 'call.wav', model: 'oruk-resonance' })
console.log(tone.emotions) // actual selected labels and scores
console.log(tone.text) // nullResults
type SpeechResult = {
id: string
task: string
model: string
text?: string | null
tagged_text?: string | null
language?: string | null
duration: number
emotions: Score[] // { label: string; score: number }
styles: Score[]
segments: SpeechSegment[] // per-segment timings, text, and scores
usage: SpeechUsage // audio duration and reference pricing fields
}The 15 emotion labels and 16 speaking-style labels are model vocabularies, not a guarantee that every response contains every label. Outputs are selected by model thresholds. If no emotion clears its threshold, the highest-scoring emotion is returned; styles can be empty. Several labels may be high and scores need not sum to one. These thresholds do not establish calibrated probabilities of a person's private feelings. Evaluate representative audio before choosing application thresholds. See label interpretation and the scope of the evaluations.
usage includes measured and billable audio duration and may carry reference
fields such as rate_per_minute_usd, estimated_cost_usd, or pricing_version.
Those reference estimates are not your subscription invoice. Actual charges
follow the plan allowance, overage terms, and billing records.
Errors and retries
Failures throw OrukApiError, which carries the HTTP status, the API error
code, and the request ID to quote in a support thread.
import { Oruk, OrukApiError } from '@oruk-ai/sdk'
try {
await oruk.analyze({ file, filename: 'sample.wav' })
} catch (error) {
if (error instanceof OrukApiError) {
console.error(error.status, error.code, error.requestId)
}
}The client retries HTTP 429, 500, 502, 503, and 504, as well as network and
timeout failures, with jittered backoff (two retries by default). Every attempt reuses one X-Request-ID, so a
retried call stays a single traceable request on both ends. Client errors such
as 400 and 401 throw immediately rather than burning attempts.
Configuration
const oruk = new Oruk({
apiKey: process.env.ORUK_API_KEY!,
baseUrl: 'https://speech-api.oruk.ai', // override for a private deployment
timeoutMs: 120_000, // per attempt
maxRetries: 2,
fetch: customFetch, // inject your own, e.g. for tracing
})Runnable file workflows
Set ORUK_API_KEY in your environment and download the complete
TypeScript example. These commands
use Node.js 22+ and the tsx TypeScript runner:
npm install --save-dev tsx
curl --fail -O https://oruk.ai/examples/analyze-file.mts
npx tsx analyze-file.mts sample.wav --task analysis > result.json
npx tsx analyze-file.mts support-call.wav --task analysis --diarize > speakers.json
npx tsx analyze-file.mts speaking-sample.wav --task proficiency > proficiency.jsonEach command makes one logical request, with bounded retries if needed.
Running multiple commands processes the audio separately. --help describes
model selection, known speaker count, and an optional proficiency transcript
file. The program writes the complete API response to stdout and errors to
stderr. It does not fabricate missing labels or assume every proficiency
request was scored. For proficiency, use 30–60 seconds of spontaneous English
and inspect check.status; insufficient_audio does not establish a CEFR
level. A model estimate is not a language certificate. Keep API keys in
server-side code. These file methods do not implement the separate
realtime WebSocket workflow.
Scope
Worth knowing before you build on it:
- English file API. Resonance is the flagship speech recognition model for recorded English audio.
- Separate realtime preview. Multilingual transcription in 32 locales and phrase-level emotion scores are available over WebSocket; see the realtime reference.
- Up to 30 MB and 60 minutes per request.
- Speech in, labels out. No speaker identification, no diarisation by identity, no video or face analysis, no lie detection.
See oruk.ai/capabilities for the full statement of what the API does and does not do.
Links
License
MIT
Orukeet
Use model oruk-orukeet for English transcription up to 60 seconds / 4 MiB. Every subscription includes an Orukeet allowance; see current plans and task rates. Optional emotion detection and speaker diarization draw from that same allowance. Extra usage shares the plan spending cap. See the Orukeet contract for availability, outputs, streaming, and limits.
