///
export declare const VERSION: string;
export declare const DEFAULT_BASE_URL: string;
export declare const TASKS: readonly ['general', 'connected_speech', 'isolated_word'];
export type Task = (typeof TASKS)[number];
/** review / uncertain / no_correction are the feedback decisions; unassessable, partial and
* legacy_correction also occur. Unknown values should be treated as "no decision". */
export type Decision = 'review' | 'uncertain' | 'no_correction' | 'unassessable' | 'partial' | 'legacy_correction' | (string & {});
export declare const DECISIONS: readonly ['review', 'uncertain', 'no_correction', 'unassessable', 'partial', 'legacy_correction'];
/** Error codes after which the SAME Idempotency-Key must be retried. */
export declare const RETRY_SAME_KEY_CODES: ReadonlySet;
/** One sound (phoneme, or Mandarin initial/final/tone) under a word. Extra evidence fields may be present. */
export interface Phone {
dimension?: 'initial' | 'final' | 'tone' | string;
expected: string | number | null;
heard: string | number | null;
score: number | null;
decision?: Decision;
[key: string]: unknown;
}
/** One word or Mandarin character. */
export interface Word {
word: string;
score: number | null;
start_ms?: number;
end_ms?: number;
pinyin?: string;
decision?: Decision;
phones?: Phone[];
[key: string]: unknown;
}
export interface Billing { credits: number; balance_credits: number; free_credits?: number; source?: string; credits_waived?: number }
/** Full JSON body of POST /v1/assess. Fields beyond these are returned as-is. */
export interface AssessmentData {
assessable: boolean;
reason?: string;
scores: Record & { overall?: number | null };
words?: Word[];
syllables?: unknown[];
billing: Billing;
model_version?: string;
id?: string;
[key: string]: unknown;
}
export interface CapabilityLanguage {
locale: string;
available?: boolean;
status?: string;
dimensions?: string[];
correction_level?: string;
limitations?: unknown;
[key: string]: unknown;
}
export interface Capabilities {
model_version?: string;
languages: CapabilityLanguage[];
limits?: { max_audio_seconds: number; max_upload_bytes: number; concurrent_requests_per_account: number };
score_scale?: Record;
[key: string]: unknown;
}
export declare class TonePerfectError extends Error {
/** HTTP status, or 0 for a transport failure (code `timeout` / `network_error`). */
status: number;
/** API error code: invalid_api_key, subscription_required, insufficient_credits, invalid_audio, rate_limit, ... */
code: string;
detail: string;
requestId: string | null;
retryAfter: number | null;
/** The error body as returned by the API. */
body: Record;
/** `req_…` result id when the failure was finalized under your Idempotency-Key. */
id: string | null;
billing: Billing | null;
/** The failure is a stored result for this key (id + billing present); the same key replays it for 24 h. */
readonly finalized: boolean;
/** Start a new attempt with a new Idempotency-Key. */
readonly needsNewKey: boolean;
/** Nothing was finalized: retry later with the SAME Idempotency-Key. */
readonly retryable: boolean;
constructor(status: number, code: string, message: string, requestId?: string | null, retryAfter?: number | null, body?: Record | null);
}
/** One sound in the compact schema (toneperfect.compact.v1). */
export interface CompactUnit {
dimension: 'initial' | 'final' | 'tone' | 'phoneme' | (string & {});
/** Always a string or null in compact (tones as "3"). */
expected: string | null;
heard: string | null;
score: number | null;
decision: Decision | null;
}
/** One word or Mandarin character in the compact schema. */
export interface CompactWord {
text: string | null;
pinyin: string | null;
score: number | null;
start_ms: number | null;
end_ms: number | null;
decision: Decision | null;
units: CompactUnit[];
}
/** Body of POST /v1/assess with `compact: true` (TonePerfect-Response: compact). Every field is always present. */
export interface CompactAssessmentData {
schema: 'toneperfect.compact.v1';
id: string | null;
assessable: boolean;
reason: string | null;
locale: string | null;
task: string | null;
model_version: string | null;
scores: Record<'overall' | 'pronunciation' | 'initial' | 'final' | 'tone' | 'fluency' | 'rhythm' | 'completeness', number | null>;
words: CompactWord[];
warnings: string[];
billing: Billing;
/** Present (true) on a test-key response. */
test?: boolean;
}
/** One POST /v1/assess result. `D`/`W` are the body and word shapes: full by default, compact with `compact: true`. */
export declare class Assessment {
data: D;
status: number;
headers: Headers;
requestId: string | null;
/** True when the result was replayed from a previous identical request (not charged again). */
replayed: boolean;
constructor(data: D, status: number, headers: Headers);
readonly assessable: boolean;
readonly reason: string | null;
readonly scores: Record;
readonly overall: number | null;
readonly words: W[];
readonly billing: Billing | Record;
readonly modelVersion: string | null;
readonly id: string | null;
/** True for a fixed example response from a tp_test_ key (no audio scored, nothing charged). */
readonly test: boolean;
}
/** Result of `assess(..., { compact: true })`: words carry `text` and `units`, not `word` and `phones`. */
export type CompactAssessment = Assessment;
/** Result of `assess(...)` without `compact`. */
export type FullAssessment = Assessment;
export type TestOutcome = 'success' | 'unassessable' | 'invalid_assessment' | 'engine_unavailable' | 'subscription_required' | 'insufficient_credits' | 'account_busy' | 'rate_limit';
export interface LintIssue { code: string; message: string; field: 'text' | 'pinyin' | string; severity: 'error' | 'warning' }
export interface LintResult { ok: boolean; language: string; issues: LintIssue[]; units: number }
export interface LintOptions { language?: string; pinyin?: string; task?: Task }
export interface StatusWindow { checks: number; ok: number; success_rate: number | null; p50_latency_ms: number | null; p90_latency_ms: number | null; busy_skipped?: number }
export interface ServiceStatus {
status: 'operational' | 'degraded' | 'down' | 'unknown' | 'not_monitored';
reason: string | null;
reason_code: 'probe_disabled' | 'consecutive_failures' | 'engine_saturated' | 'no_recent_assessment' | 'recent_failures' | null;
consecutive_busy: number;
generated_at: string;
probe: { enabled: boolean; interval_seconds: number | null; what: string; pass_means: string };
last_check: { checked_at: string; ok: boolean; http_status: number; latency_ms: number; model_version: string | null; error: string | null } | null;
consecutive_failures: number;
last_24h: StatusWindow;
last_7d: StatusWindow;
}
export type AudioInput = string | URL | Buffer | Uint8Array | ArrayBuffer | Blob;
export interface AssessFields {
/** Reference text the learner read. Required unless `pinyin` is given (Mandarin). */
text?: string;
/** Locale such as zh-CN, en-US, fr-FR. Default zh-CN. See capabilities(). */
language?: string;
/** Explicit Mandarin reading with numbered tones, e.g. "ni3 hao3". */
pinyin?: string;
/** general (default), connected_speech or isolated_word. */
task?: Task;
/** Unique per attempt; generated when omitted. Reuse for retries of an unknown outcome. */
idempotencyKey?: string;
filename?: string;
contentType?: string;
/** Ask for the stable compact schema (toneperfect.compact.v1). */
compact?: boolean;
/** tp_test_ keys only: which outcome the fixed response rehearses. */
testOutcome?: TestOutcome;
}
export interface TonePerfectOptions {
apiKey?: string;
baseUrl?: string;
timeoutMs?: number;
fetch?: typeof fetch;
userAgent?: string;
}
export declare class TonePerfect {
apiKey: string;
baseUrl: string;
timeoutMs: number;
constructor(options?: TonePerfectOptions);
/** GET /v1/capabilities (no key needed). */
capabilities(): Promise;
/** POST /v1/assess. Throws TonePerfectError for every non-2xx status. */
assess(audio: AudioInput, fields: AssessFields & { compact: true }): Promise;
assess(audio: AudioInput, fields?: AssessFields & { compact?: false | undefined }): Promise;
/** `compact` known only at run time: narrow on `result.data.schema === 'toneperfect.compact.v1'`. */
assess(audio: AudioInput, fields: AssessFields): Promise;
/** POST /v1/lint (no key): check a reference text before recording. */
lint(text: string, options?: LintOptions): Promise;
/** GET /v1/status (no key): synthetic engine probe summary. */
status(): Promise;
}
export declare function newIdempotencyKey(prefix?: string): string;
declare const _default: {
TonePerfect: typeof TonePerfect;
TonePerfectError: typeof TonePerfectError;
Assessment: typeof Assessment;
newIdempotencyKey: typeof newIdempotencyKey;
DEFAULT_BASE_URL: string;
TASKS: typeof TASKS;
DECISIONS: typeof DECISIONS;
RETRY_SAME_KEY_CODES: typeof RETRY_SAME_KEY_CODES;
VERSION: string;
};
export default _default;