@vocalstack/js-sdk 2.0.0-alpha.1 → 2.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -3
- package/SKILL.md +7 -23
- package/dist/index.d.ts +5 -6
- package/dist/index.js +2 -4
- package/dist/realtime.d.ts +5 -0
- package/dist/realtime.js +8 -3
- package/dist/resources.d.ts +27 -29
- package/dist/resources.js +32 -70
- package/package.json +3 -7
- package/skills/vocalstack/SKILL.md +7 -23
package/README.md
CHANGED
|
@@ -31,8 +31,8 @@ npm install @vocalstack/js-sdk
|
|
|
31
31
|
|
|
32
32
|
- [Quickstart](https://github.com/vocalstack/js-sdk/blob/main/docs/quickstart.md): An API key, the SDK, and your first transcript in a dozen lines.
|
|
33
33
|
- [Transcribe a file](https://github.com/vocalstack/js-sdk/blob/main/docs/transcribe-a-file.md): Upload a file, wait for its job, read the transcript and export it. Asynchronous; nothing to keep open.
|
|
34
|
-
- [Transcribe a link](https://github.com/vocalstack/js-sdk/blob/main/docs/transcribe-a-link.md): The server fetches a public URL or a
|
|
35
|
-
- [
|
|
34
|
+
- [Transcribe a link](https://github.com/vocalstack/js-sdk/blob/main/docs/transcribe-a-link.md): The server fetches a public URL, a YouTube video or a recording on a video or audio site, then transcribes it like a file. Asynchronous, with a fetch phase first.
|
|
35
|
+
- [Search your transcriptions](https://github.com/vocalstack/js-sdk/blob/main/docs/search-transcriptions.md): Find transcriptions by words in their name or link, site, language, kind, tag and date, keep the shared ones, and play each with its share code.
|
|
36
36
|
- [Stream a microphone](https://github.com/vocalstack/js-sdk/blob/main/docs/stream-a-microphone.md): Live audio in, transcript events out, over one WebSocket. Drafts replace the live tail; commits append.
|
|
37
37
|
- [Stream a file at full speed](https://github.com/vocalstack/js-sdk/blob/main/docs/stream-a-file.md): Send a file's bytes through a realtime session and read the transcript as fast as the server makes it. Nothing is stored.
|
|
38
38
|
- [Go live](https://github.com/vocalstack/js-sdk/blob/main/docs/go-live.md): Publish live audio to a stored transcription, and put it on a broadcast link listeners open. Control is HTTP; audio is one WebSocket.
|
|
@@ -52,7 +52,6 @@ npm install @vocalstack/js-sdk
|
|
|
52
52
|
| transcription | a file, link, microphone or stream, transcribed and stored (`live: true` for a microphone or stream) | `vs.transcriptions` |
|
|
53
53
|
| recording | one take inside a broadcast or a transcription | `vs.recordings` |
|
|
54
54
|
| job | a file or link recording's transcription work | `vs.jobs` |
|
|
55
|
-
| batch | many files transcribed together, for less than one transcription each | `vs.batches` |
|
|
56
55
|
|
|
57
56
|
The other groups: `vs.corrections`, `vs.shareLinks`, `vs.voices`,
|
|
58
57
|
`vs.search`, `vs.me`, `vs.languages`, `vs.rtmp`, `vs.events`, `vs.meta`.
|
package/SKILL.md
CHANGED
|
@@ -19,9 +19,6 @@ one group per resource: `vs.transcriptions`, `vs.jobs`, `vs.recordings`,
|
|
|
19
19
|
- A **job** is a file or link recording's transcription work:
|
|
20
20
|
`vs.jobs.wait(recordingId)` polls it until it is `done` or `failed`
|
|
21
21
|
(`{ onProgress: (job) => … }` sees each read: `job.state`, `job.progress.pct`).
|
|
22
|
-
- A **batch** is many files transcribed together for less than one
|
|
23
|
-
transcription each: `vs.batches.create()`, `addFile`/`addUrl` per file,
|
|
24
|
-
`start`, then `wait` (section 6).
|
|
25
22
|
- A **broadcast** is a link listeners open to follow a live recording.
|
|
26
23
|
- A **realtime session** (`vs.realtime`) is a socket you stream audio into and
|
|
27
24
|
read the transcript from. Nothing is stored. "Session" means only this.
|
|
@@ -179,26 +176,14 @@ for (const h of hits) {
|
|
|
179
176
|
}
|
|
180
177
|
```
|
|
181
178
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
`settings.speakerLabels` asks), never a summary, title, share link or voices.
|
|
179
|
+
By meaning, in any language, even with none of the words (vector search). Each
|
|
180
|
+
hit's `passage` is its whole paragraph: give the passages to your own model as
|
|
181
|
+
context to answer questions about what was said (RAG). Pay as you go counts 60
|
|
182
|
+
an hour (`allowance`); Premium and Enterprise have no limit.
|
|
187
183
|
|
|
188
184
|
```js
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
const batch = await vs.batches.create({ name: 'Interviews', settings: { spokenLanguage: 'en' } });
|
|
193
|
-
for (const path of process.argv.slice(2)) await vs.batches.addFile(batch.id, { file: path }); // or addUrl(batch.id, url)
|
|
194
|
-
await vs.batches.start(batch.id);
|
|
195
|
-
const done = await vs.batches.wait(batch.id); // done (or stopped); check done.counts.failed
|
|
196
|
-
for (const tx of done.files ?? []) {
|
|
197
|
-
if (tx.job?.state !== 'done') continue;
|
|
198
|
-
const { sentences } = await vs.recordings.transcript(tx.job.recordingId);
|
|
199
|
-
console.log(tx.name, sentences.map((s) => s.text).join(' '));
|
|
200
|
-
}
|
|
201
|
-
// vs.batches.export(batch.id, 'srt') is every finished transcript in one zip.
|
|
185
|
+
const { hits: found } = await vs.search.vector('how much does housing cost');
|
|
186
|
+
for (const h of found) console.log(h.similarity.toFixed(2), h.passage.mediaStart, h.passage.text);
|
|
202
187
|
```
|
|
203
188
|
|
|
204
189
|
## Errors
|
|
@@ -238,8 +223,7 @@ try {
|
|
|
238
223
|
- The other groups: `vs.transcriptions` (`list`, `get`, `update`, `archive`,
|
|
239
224
|
`restore`, `purge`, `listShareLinks`, `createShareLink`), `vs.recordings`
|
|
240
225
|
(`get`, `summarize`, `summary`, `translation`, `audioUrl`, `subscribe` for a
|
|
241
|
-
live transcript feed), `vs.
|
|
242
|
-
`wait`, `get`, `list`, `export`, `stop`, `delete`), `vs.corrections`, `vs.shareLinks`, `vs.voices`,
|
|
226
|
+
live transcript feed), `vs.corrections`, `vs.shareLinks`, `vs.voices`,
|
|
243
227
|
`vs.broadcasts`, `vs.me` (`get`, `usage`), `vs.languages`,
|
|
244
228
|
`vs.rtmp`, `vs.events`, `vs.meta`. Each method is named after its operation
|
|
245
229
|
in the OpenAPI document (`transcriptions.create` is `POST /v1/transcriptions`).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { type ClientOptions } from './http.js';
|
|
2
2
|
import { Realtime } from './realtime.js';
|
|
3
|
-
import {
|
|
3
|
+
import { Billing, Broadcasts, Connect, Corrections, Events, Jobs, Languages, MeApi, Meta, Recordings, Rtmp, Search, ShareLinks, Tags, Transcriptions, Voices } from './resources.js';
|
|
4
4
|
export type { ClientOptions, CallOptions, ReadAs, Query } from './http.js';
|
|
5
5
|
export { DEFAULT_BASE_URL } from './http.js';
|
|
6
6
|
export { VocalStackError } from './errors.js';
|
|
7
7
|
export type { ConnectTokenErrorCode, VocalStackErrorOptions } from './errors.js';
|
|
8
8
|
export { RealtimeSession } from './realtime.js';
|
|
9
|
-
export type { ConnectParams, SessionEvent } from './realtime.js';
|
|
9
|
+
export type { ConnectParams, QueuedInfo, SessionEvent } from './realtime.js';
|
|
10
10
|
export type { FileInput } from './file.js';
|
|
11
11
|
export { Realtime, applySentence } from './realtime.js';
|
|
12
12
|
export type { ConnectOptions, TranscribeOptions, RealtimeTranscript } from './realtime.js';
|
|
13
13
|
export { Subscription } from './subscription.js';
|
|
14
14
|
export type { SubscribeOptions, SubscriptionEvent } from './subscription.js';
|
|
15
15
|
export type { WsLike } from './ws.js';
|
|
16
|
-
export {
|
|
17
|
-
export type { AudioKind, MediaAuth, MediaFormat, RenderVoice } from './resources.js';
|
|
18
|
-
export type {
|
|
16
|
+
export { Broadcasts, Connect, Corrections, Events, Jobs, Languages, MeApi, Meta, Recordings, Rtmp, Search, ShareLinks, Tags, Transcriptions, Voices, playableFormat, } from './resources.js';
|
|
17
|
+
export type { AudioKind, MediaAuth, MediaFormat, RenderVoice, VoicesChoice } from './resources.js';
|
|
18
|
+
export type { ConnectToken, ConnectTokenInput, CreateTranscriptionInput, LifecycleFilter, Me, PageOptions, RecordingPageOptions, SearchOptions, TranscriptionFilters, Upload, UsageChargesOptions, UsageOptions } from './resources.js';
|
|
19
19
|
export type * from '@vocalstack/contract';
|
|
20
20
|
export type VocalStackOptions = ClientOptions;
|
|
21
21
|
export declare class VocalStack {
|
|
@@ -23,7 +23,6 @@ export declare class VocalStack {
|
|
|
23
23
|
readonly realtime: Realtime;
|
|
24
24
|
readonly broadcasts: Broadcasts;
|
|
25
25
|
readonly transcriptions: Transcriptions;
|
|
26
|
-
readonly batches: Batches;
|
|
27
26
|
readonly recordings: Recordings;
|
|
28
27
|
readonly jobs: Jobs;
|
|
29
28
|
readonly corrections: Corrections;
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { Transport } from './http.js';
|
|
2
2
|
import { Realtime } from './realtime.js';
|
|
3
|
-
import {
|
|
3
|
+
import { Billing, Broadcasts, Connect, Corrections, Events, Jobs, Languages, MeApi, Meta, Recordings, Rtmp, Search, ShareLinks, Tags, Transcriptions, Voices, } from './resources.js';
|
|
4
4
|
export { DEFAULT_BASE_URL } from './http.js';
|
|
5
5
|
export { VocalStackError } from './errors.js';
|
|
6
6
|
export { RealtimeSession } from './realtime.js';
|
|
7
7
|
export { Realtime, applySentence } from './realtime.js';
|
|
8
8
|
export { Subscription } from './subscription.js';
|
|
9
|
-
export {
|
|
9
|
+
export { Broadcasts, Connect, Corrections, Events, Jobs, Languages, MeApi, Meta, Recordings, Rtmp, Search, ShareLinks, Tags, Transcriptions, Voices, playableFormat, } from './resources.js';
|
|
10
10
|
export class VocalStack {
|
|
11
11
|
baseUrl;
|
|
12
12
|
realtime;
|
|
13
13
|
broadcasts;
|
|
14
14
|
transcriptions;
|
|
15
|
-
batches;
|
|
16
15
|
recordings;
|
|
17
16
|
jobs;
|
|
18
17
|
corrections;
|
|
@@ -33,7 +32,6 @@ export class VocalStack {
|
|
|
33
32
|
this.realtime = new Realtime(t);
|
|
34
33
|
this.broadcasts = new Broadcasts(t);
|
|
35
34
|
this.transcriptions = new Transcriptions(t);
|
|
36
|
-
this.batches = new Batches(t);
|
|
37
35
|
this.recordings = new Recordings(t);
|
|
38
36
|
this.jobs = new Jobs(t);
|
|
39
37
|
this.corrections = new Corrections(t);
|
package/dist/realtime.d.ts
CHANGED
|
@@ -11,8 +11,12 @@ export interface ConnectParams {
|
|
|
11
11
|
options?: SessionOptions;
|
|
12
12
|
readyDeadlineMs?: number;
|
|
13
13
|
onLoading?(stage: 'download' | 'model'): void;
|
|
14
|
+
onQueued?(queued: QueuedInfo): void;
|
|
14
15
|
onFrame?(frame: ServerFrame): void;
|
|
15
16
|
}
|
|
17
|
+
export type QueuedInfo = Omit<Extract<ServerFrame, {
|
|
18
|
+
type: 'session.queued';
|
|
19
|
+
}>, 'type'>;
|
|
16
20
|
export declare function engineLost(closeCode?: number): VocalStackError;
|
|
17
21
|
export declare function upgradeRefused(status: number | undefined, body: string): VocalStackError;
|
|
18
22
|
export declare const RESTART_CLOSE = 1012;
|
|
@@ -41,6 +45,7 @@ export declare class RealtimeSession implements AsyncIterable<SessionEvent> {
|
|
|
41
45
|
}
|
|
42
46
|
export interface ConnectOptions {
|
|
43
47
|
onLoading?(stage: 'download' | 'model'): void;
|
|
48
|
+
onQueued?(queued: QueuedInfo): void;
|
|
44
49
|
onFrame?(frame: ServerFrame): void;
|
|
45
50
|
readyDeadlineMs?: number;
|
|
46
51
|
}
|
package/dist/realtime.js
CHANGED
|
@@ -29,7 +29,7 @@ export function upgradeRefused(status, body) {
|
|
|
29
29
|
export const RESTART_CLOSE = 1012;
|
|
30
30
|
const RESTART_TRIES = 3;
|
|
31
31
|
const MAX_PENDING_BYTES = 4 * 1024 * 1024;
|
|
32
|
-
async function openReady(url, deadlineMs, onLoading, onFrame, attach) {
|
|
32
|
+
async function openReady(url, deadlineMs, onLoading, onQueued, onFrame, attach) {
|
|
33
33
|
const ws = await openWebSocket(url);
|
|
34
34
|
ws.binaryType = 'arraybuffer';
|
|
35
35
|
await new Promise((resolve, reject) => {
|
|
@@ -77,6 +77,10 @@ async function openReady(url, deadlineMs, onLoading, onFrame, attach) {
|
|
|
77
77
|
if (frame.type === 'session.loading') {
|
|
78
78
|
onLoading?.(frame.stage);
|
|
79
79
|
}
|
|
80
|
+
else if (frame.type === 'session.queued') {
|
|
81
|
+
const { type: _type, ...queued } = frame;
|
|
82
|
+
onQueued?.(queued);
|
|
83
|
+
}
|
|
80
84
|
else if (frame.type === 'session.ready') {
|
|
81
85
|
if (!settled) {
|
|
82
86
|
settled = true;
|
|
@@ -142,7 +146,7 @@ export class RealtimeSession {
|
|
|
142
146
|
const deadlineMs = params.readyDeadlineMs ?? DEFAULT_READY_DEADLINE_MS;
|
|
143
147
|
const early = [];
|
|
144
148
|
let session;
|
|
145
|
-
await openReady(params.streamUrl, deadlineMs, params.onLoading, (f) => early.push(f), (ws) => {
|
|
149
|
+
await openReady(params.streamUrl, deadlineMs, params.onLoading, params.onQueued, (f) => early.push(f), (ws) => {
|
|
146
150
|
const s = new RealtimeSession(params.sessionId, ws, params);
|
|
147
151
|
for (const f of early)
|
|
148
152
|
s.#frame(f);
|
|
@@ -160,7 +164,7 @@ export class RealtimeSession {
|
|
|
160
164
|
if (this.#closedByUs)
|
|
161
165
|
break;
|
|
162
166
|
try {
|
|
163
|
-
await openReady(this.#params.streamUrl, deadlineMs, this.#params.onLoading, (f) => this.#frame(f), (ws) => {
|
|
167
|
+
await openReady(this.#params.streamUrl, deadlineMs, this.#params.onLoading, this.#params.onQueued, (f) => this.#frame(f), (ws) => {
|
|
164
168
|
this.#ws = ws;
|
|
165
169
|
this.#reconnecting = false;
|
|
166
170
|
this.#listen();
|
|
@@ -389,6 +393,7 @@ export class Realtime {
|
|
|
389
393
|
options,
|
|
390
394
|
readyDeadlineMs: connect.readyDeadlineMs ?? this.#t.opts.readyDeadlineMs,
|
|
391
395
|
onLoading: connect.onLoading,
|
|
396
|
+
onQueued: connect.onQueued,
|
|
392
397
|
onFrame: connect.onFrame,
|
|
393
398
|
});
|
|
394
399
|
}
|
package/dist/resources.d.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
|
-
import type { AppStreamKey, AudioSourceCheck,
|
|
1
|
+
import type { AppStreamKey, AudioSourceCheck, AutoTopUpSettings, AutoTopUpView, BillingDetails, BillingView, CheckPromoCode, CheckoutView, CreateCheckout, LowBalanceAlertSettings, LowBalanceAlertView, PaymentDocument, PaymentMethodList, PaymentPage, PortalLink, PromoCodeView, SetInterval, SubscriptionView, YearlyOptIn, UsageChargePage, UsageReport, Broadcast, Correction, CorrectionBatch, CorrectionPage, CreateAppStreamKey, CreateAudioSource, CreateLiveTranscription, CreateBroadcast, CreateShareLink, CreateTag, CreateTranscription, CreateVoiceFromSpeaker, CreatedAppStreamKey, ChargePage, ExportFormat, ClientApp, ClientPolicy, Health, LanguageLists, LedgerPage, LinkProbe, PatchVoice, PlanList, PublishGrant, Recording, RecordingEvent, RecordingEvents, RecordingPage, RetranscribeLine, RtmpStatus, SearchResults, VectorResults, SettingsPatch, ShareLink, SharedTranscript, StartRecording, StartJob, SummaryStatus, Tag, TagTranscriptions, Transcription, TranscriptionJob, TranscriptionPage, TranscriptSnapshot, UpdateBroadcast, UpdateTag, UpdateTranscription, UpdateShareLink, UploadRecording, Voice, VoiceCatalogue, VoiceTrack, ManyResult } from '@vocalstack/contract';
|
|
2
2
|
import { Transport, type CallOptions, type ReadAs } from './http.js';
|
|
3
3
|
import { RealtimeSession, type ConnectOptions } from './realtime.js';
|
|
4
4
|
import { Subscription, type SubscribeOptions } from './subscription.js';
|
|
5
5
|
import { type WsLike } from './ws.js';
|
|
6
6
|
export type Upload = Blob | Uint8Array | ArrayBuffer | string;
|
|
7
7
|
export type LifecycleFilter = 'active' | 'archived' | 'all';
|
|
8
|
+
export interface TranscriptionFilters {
|
|
9
|
+
lifecycle?: LifecycleFilter;
|
|
10
|
+
q?: string;
|
|
11
|
+
site?: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
kind?: 'file' | 'link' | 'live';
|
|
14
|
+
origin?: Transcription['origin'];
|
|
15
|
+
tag?: string;
|
|
16
|
+
broadcast?: string;
|
|
17
|
+
createdAfter?: string | Date;
|
|
18
|
+
createdBefore?: string | Date;
|
|
19
|
+
shared?: boolean;
|
|
20
|
+
}
|
|
8
21
|
export interface RecordingPageOptions extends PageOptions {
|
|
9
22
|
lifecycle?: 'active' | 'archived';
|
|
10
23
|
}
|
|
@@ -49,41 +62,17 @@ export type CreateTranscriptionInput = CreateTranscription & {
|
|
|
49
62
|
export declare class Transcriptions extends ParentResource<Transcription, UpdateTranscription> {
|
|
50
63
|
protected readonly base: "/v1/transcriptions";
|
|
51
64
|
protected readonly key: "transcriptions";
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}): Promise<TranscriptionPage>;
|
|
65
|
+
list(opts?: LifecycleFilter | TranscriptionFilters): Promise<Transcription[]>;
|
|
66
|
+
listPage(opts?: PageOptions & TranscriptionFilters): Promise<TranscriptionPage>;
|
|
55
67
|
uploadRecording(id: string, req?: UploadRecording, call?: CallOptions): Promise<PublishGrant>;
|
|
56
68
|
create(req: CreateTranscriptionInput, call?: CallOptions): Promise<Transcription>;
|
|
57
69
|
createFromUrl(url: string, opts?: Omit<CreateTranscription, 'url'>, call?: CallOptions): Promise<Transcription>;
|
|
58
70
|
createLive(req: CreateLiveTranscription, call?: CallOptions): Promise<Transcription>;
|
|
71
|
+
lookup(url: string): Promise<Transcription | null>;
|
|
59
72
|
probe(url: string): Promise<LinkProbe>;
|
|
60
73
|
attachBroadcast(id: string, broadcastId: string): Promise<Transcription>;
|
|
61
74
|
detachBroadcast(id: string, broadcastId: string): Promise<Transcription>;
|
|
62
75
|
}
|
|
63
|
-
export type AddBatchFileInput = Pick<CreateTranscription, 'url' | 'name' | 'startS' | 'endS' | 'durationS'> & {
|
|
64
|
-
file?: Upload;
|
|
65
|
-
fileName?: string;
|
|
66
|
-
};
|
|
67
|
-
export declare class Batches {
|
|
68
|
-
#private;
|
|
69
|
-
constructor(t: Transport);
|
|
70
|
-
create(req?: CreateBatch, call?: CallOptions): Promise<Batch>;
|
|
71
|
-
list(): Promise<Batch[]>;
|
|
72
|
-
listPage(opts?: PageOptions): Promise<BatchPage>;
|
|
73
|
-
get(id: string): Promise<Batch>;
|
|
74
|
-
delete(id: string): Promise<void>;
|
|
75
|
-
addFile(id: string, req: AddBatchFileInput, call?: CallOptions): Promise<Transcription>;
|
|
76
|
-
addUrl(id: string, url: string, opts?: Omit<AddBatchFileInput, 'url' | 'file' | 'fileName'>, call?: CallOptions): Promise<Transcription>;
|
|
77
|
-
start(id: string, call?: CallOptions): Promise<Batch>;
|
|
78
|
-
export(id: string, format: ExportFormat): Promise<Response>;
|
|
79
|
-
stop(id: string): Promise<Batch>;
|
|
80
|
-
wait(id: string, opts?: {
|
|
81
|
-
intervalMs?: number;
|
|
82
|
-
timeoutMs?: number;
|
|
83
|
-
signal?: AbortSignal;
|
|
84
|
-
onProgress?(batch: Batch): void;
|
|
85
|
-
}): Promise<Batch>;
|
|
86
|
-
}
|
|
87
76
|
export declare class Recordings {
|
|
88
77
|
#private;
|
|
89
78
|
constructor(t: Transport);
|
|
@@ -131,6 +120,7 @@ export declare class Recordings {
|
|
|
131
120
|
audioSrc(id: string, opts?: {
|
|
132
121
|
kind?: AudioKind;
|
|
133
122
|
lang?: string;
|
|
123
|
+
voices?: VoicesChoice;
|
|
134
124
|
voice?: string;
|
|
135
125
|
format?: MediaFormat;
|
|
136
126
|
} & MediaAuth): string;
|
|
@@ -138,6 +128,7 @@ export declare class Recordings {
|
|
|
138
128
|
clean?: boolean;
|
|
139
129
|
} & MediaAuth): string;
|
|
140
130
|
voiceClipSrc(id: string, sentence: number, lang: string, opts?: {
|
|
131
|
+
voices?: VoicesChoice;
|
|
141
132
|
voice?: string;
|
|
142
133
|
format?: 'ogg' | 'caf';
|
|
143
134
|
part?: number;
|
|
@@ -149,6 +140,7 @@ export declare class Recordings {
|
|
|
149
140
|
renderVoice(id: string, req: RenderVoice & ReadAs, call?: CallOptions): Promise<VoiceTrack[]>;
|
|
150
141
|
voiceClip(id: string, sentence: number, lang: string, opts?: {
|
|
151
142
|
format?: 'ogg' | 'caf';
|
|
143
|
+
voices?: VoicesChoice;
|
|
152
144
|
} & ReadAs): Promise<Response>;
|
|
153
145
|
mediaToken(id: string): Promise<{
|
|
154
146
|
token: string;
|
|
@@ -168,11 +160,14 @@ export declare class Recordings {
|
|
|
168
160
|
}
|
|
169
161
|
export type AudioKind = 'delivery' | 'voice' | 'background' | 'speech' | 'master' | 'source' | 'video';
|
|
170
162
|
export type MediaFormat = 'ogg' | 'caf' | 'mp3';
|
|
163
|
+
export type VoicesChoice = 'cloned' | 'synthetic';
|
|
171
164
|
export interface RenderVoice {
|
|
172
165
|
langs: string[];
|
|
166
|
+
voices?: VoicesChoice;
|
|
173
167
|
voice?: string;
|
|
174
168
|
voiceBySpeaker?: Record<string, string>;
|
|
175
169
|
near?: number;
|
|
170
|
+
consent?: boolean;
|
|
176
171
|
}
|
|
177
172
|
export interface MediaAuth extends ReadAs {
|
|
178
173
|
token?: string;
|
|
@@ -260,10 +255,11 @@ export declare class Voices {
|
|
|
260
255
|
} & ReadAs): Promise<Response>;
|
|
261
256
|
sample(id: string): Promise<Response>;
|
|
262
257
|
}
|
|
263
|
-
export interface SearchOptions {
|
|
258
|
+
export interface SearchOptions extends Omit<TranscriptionFilters, 'lifecycle' | 'q' | 'broadcast'> {
|
|
264
259
|
broadcasts?: string[];
|
|
265
260
|
listenerTokens?: string[];
|
|
266
261
|
recording?: string;
|
|
262
|
+
transcription?: string;
|
|
267
263
|
lang?: string;
|
|
268
264
|
limit?: number;
|
|
269
265
|
}
|
|
@@ -271,7 +267,9 @@ export declare class Search {
|
|
|
271
267
|
#private;
|
|
272
268
|
constructor(t: Transport);
|
|
273
269
|
query(q: string, opts?: SearchOptions): Promise<SearchResults>;
|
|
270
|
+
vector(q: string, opts?: VectorSearchOptions): Promise<VectorResults>;
|
|
274
271
|
}
|
|
272
|
+
export type VectorSearchOptions = Omit<SearchOptions, 'broadcasts' | 'listenerTokens' | 'lang' | 'limit'>;
|
|
275
273
|
export interface Me {
|
|
276
274
|
userId?: string | null;
|
|
277
275
|
apiKeyId?: string | null;
|
package/dist/resources.js
CHANGED
|
@@ -3,6 +3,10 @@ import { listenerQuery, seg } from './http.js';
|
|
|
3
3
|
import { RealtimeSession } from './realtime.js';
|
|
4
4
|
import { Subscription } from './subscription.js';
|
|
5
5
|
import { openWebSocket } from './ws.js';
|
|
6
|
+
function filterQuery(f) {
|
|
7
|
+
const at = (d) => (d instanceof Date ? d.toISOString() : d);
|
|
8
|
+
return { lifecycle: f.lifecycle, q: f.q, site: f.site, language: f.language, kind: f.kind, origin: f.origin, tag: f.tag, broadcast: f.broadcast, createdAfter: at(f.createdAfter), createdBefore: at(f.createdBefore), shared: f.shared };
|
|
9
|
+
}
|
|
6
10
|
async function toBlob(file) {
|
|
7
11
|
if (typeof file === 'string') {
|
|
8
12
|
const fsSpecifier = 'node:' + 'fs';
|
|
@@ -106,8 +110,15 @@ export class Broadcasts extends ParentResource {
|
|
|
106
110
|
export class Transcriptions extends ParentResource {
|
|
107
111
|
base = '/v1/transcriptions';
|
|
108
112
|
key = 'transcriptions';
|
|
113
|
+
list(opts = 'active') {
|
|
114
|
+
const f = typeof opts === 'string' ? { lifecycle: opts } : opts;
|
|
115
|
+
return allPages(async (cursor) => {
|
|
116
|
+
const r = await this.listPage({ ...f, cursor });
|
|
117
|
+
return { items: r.transcriptions, nextCursor: r.nextCursor };
|
|
118
|
+
});
|
|
119
|
+
}
|
|
109
120
|
listPage(opts = {}) {
|
|
110
|
-
return this.t.json('GET', this.base, { query: {
|
|
121
|
+
return this.t.json('GET', this.base, { query: { ...filterQuery(opts), cursor: opts.cursor, limit: opts.limit } });
|
|
111
122
|
}
|
|
112
123
|
uploadRecording(id, req = {}, call = {}) {
|
|
113
124
|
return this.t.json('POST', this.p(id, '/recordings/upload'), { body: req, ...call });
|
|
@@ -128,6 +139,10 @@ export class Transcriptions extends ParentResource {
|
|
|
128
139
|
createLive(req, call = {}) {
|
|
129
140
|
return this.t.json('POST', `${this.base}/live`, { body: req, ...call });
|
|
130
141
|
}
|
|
142
|
+
async lookup(url) {
|
|
143
|
+
const r = await this.t.json('GET', `${this.base}/lookup`, { query: { url } });
|
|
144
|
+
return r.transcription ?? null;
|
|
145
|
+
}
|
|
131
146
|
probe(url) {
|
|
132
147
|
return this.t.json('GET', `${this.base}/probe`, { query: { url } });
|
|
133
148
|
}
|
|
@@ -138,72 +153,6 @@ export class Transcriptions extends ParentResource {
|
|
|
138
153
|
return this.t.json('DELETE', this.p(id, `/broadcasts/${seg(broadcastId)}`));
|
|
139
154
|
}
|
|
140
155
|
}
|
|
141
|
-
export class Batches {
|
|
142
|
-
#t;
|
|
143
|
-
constructor(t) {
|
|
144
|
-
this.#t = t;
|
|
145
|
-
}
|
|
146
|
-
#p(id, rest = '') {
|
|
147
|
-
return `/v1/batches/${seg(id)}${rest}`;
|
|
148
|
-
}
|
|
149
|
-
create(req = {}, call = {}) {
|
|
150
|
-
return this.#t.json('POST', '/v1/batches', { body: req, ...call });
|
|
151
|
-
}
|
|
152
|
-
list() {
|
|
153
|
-
return allPages(async (cursor) => {
|
|
154
|
-
const r = await this.listPage({ cursor });
|
|
155
|
-
return { items: r.batches, nextCursor: r.nextCursor };
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
listPage(opts = {}) {
|
|
159
|
-
return this.#t.json('GET', '/v1/batches', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
160
|
-
}
|
|
161
|
-
get(id) {
|
|
162
|
-
return this.#t.json('GET', this.#p(id));
|
|
163
|
-
}
|
|
164
|
-
async delete(id) {
|
|
165
|
-
await this.#t.json('DELETE', this.#p(id));
|
|
166
|
-
}
|
|
167
|
-
async addFile(id, req, call = {}) {
|
|
168
|
-
const { file, fileName, ...options } = req;
|
|
169
|
-
if (file === undefined)
|
|
170
|
-
return this.#t.json('POST', this.#p(id, '/files'), { body: options, ...call });
|
|
171
|
-
const { blob, name } = await toBlob(file);
|
|
172
|
-
const fd = new FormData();
|
|
173
|
-
fd.append('options', JSON.stringify(options));
|
|
174
|
-
fd.append('file', blob, fileName ?? name ?? 'upload');
|
|
175
|
-
return this.#t.json('POST', this.#p(id, '/files'), { body: fd, ...call });
|
|
176
|
-
}
|
|
177
|
-
addUrl(id, url, opts = {}, call = {}) {
|
|
178
|
-
return this.addFile(id, { ...opts, url }, call);
|
|
179
|
-
}
|
|
180
|
-
start(id, call = {}) {
|
|
181
|
-
return this.#t.json('POST', this.#p(id, '/start'), call);
|
|
182
|
-
}
|
|
183
|
-
export(id, format) {
|
|
184
|
-
return this.#t.raw('GET', this.#p(id, '/export'), { headers: { accept: '*/*' }, query: { format } });
|
|
185
|
-
}
|
|
186
|
-
stop(id) {
|
|
187
|
-
return this.#t.json('POST', this.#p(id, '/stop'));
|
|
188
|
-
}
|
|
189
|
-
async wait(id, opts = {}) {
|
|
190
|
-
const interval = opts.intervalMs ?? 5000;
|
|
191
|
-
const deadline = opts.timeoutMs ? Date.now() + opts.timeoutMs : Infinity;
|
|
192
|
-
for (;;) {
|
|
193
|
-
const batch = await this.get(id);
|
|
194
|
-
opts.onProgress?.(batch);
|
|
195
|
-
if ((batch.state === 'done' || batch.state === 'stopped') && batch.finishedAt)
|
|
196
|
-
return batch;
|
|
197
|
-
if (batch.state === 'open')
|
|
198
|
-
throw new VocalStackError('invalid_state', 'the batch is open: start it before waiting for it');
|
|
199
|
-
if (Date.now() + interval > deadline)
|
|
200
|
-
throw new VocalStackError('timeout', `batch still ${batch.state} after ${opts.timeoutMs} ms`, { retryable: true });
|
|
201
|
-
if (opts.signal?.aborted)
|
|
202
|
-
throw opts.signal.reason ?? new Error('aborted');
|
|
203
|
-
await new Promise((r) => setTimeout(r, interval));
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
156
|
export class Recordings {
|
|
208
157
|
#t;
|
|
209
158
|
constructor(t) {
|
|
@@ -238,6 +187,7 @@ export class Recordings {
|
|
|
238
187
|
streamUrl: grant.streamUrl,
|
|
239
188
|
readyDeadlineMs: opts.readyDeadlineMs ?? this.#t.opts.readyDeadlineMs,
|
|
240
189
|
onLoading: opts.onLoading,
|
|
190
|
+
onQueued: opts.onQueued,
|
|
241
191
|
onFrame: opts.onFrame,
|
|
242
192
|
});
|
|
243
193
|
}
|
|
@@ -292,7 +242,7 @@ export class Recordings {
|
|
|
292
242
|
return location;
|
|
293
243
|
}
|
|
294
244
|
audioSrc(id, opts = {}) {
|
|
295
|
-
return this.#t.url(this.#p(id, '/audio'), { kind: opts.kind ?? 'delivery', lang: opts.lang, format: opts.format, voice: opts.voice, ...mediaQuery(opts) });
|
|
245
|
+
return this.#t.url(this.#p(id, '/audio'), { kind: opts.kind ?? 'delivery', lang: opts.lang, format: opts.format, voices: opts.voices, voice: opts.voice, ...mediaQuery(opts) });
|
|
296
246
|
}
|
|
297
247
|
clipSrc(id, startS, endS, opts = {}) {
|
|
298
248
|
return this.#t.url(this.#p(id, '/audio/clip'), { startS: startS.toFixed(2), endS: endS.toFixed(2), clean: opts.clean ? 'true' : undefined, ...mediaQuery(opts) });
|
|
@@ -300,6 +250,7 @@ export class Recordings {
|
|
|
300
250
|
voiceClipSrc(id, sentence, lang, opts = {}) {
|
|
301
251
|
return this.#t.url(this.#p(id, `/voice/${seg(sentence)}`), {
|
|
302
252
|
lang,
|
|
253
|
+
voices: opts.voices,
|
|
303
254
|
voice: opts.voice,
|
|
304
255
|
format: opts.format,
|
|
305
256
|
part: opts.part ? opts.part : undefined,
|
|
@@ -317,12 +268,12 @@ export class Recordings {
|
|
|
317
268
|
const r = Array.isArray(req) ? { langs: req } : req;
|
|
318
269
|
return this.#t.json('POST', this.#p(id, '/voice'), {
|
|
319
270
|
query: listenerQuery(r),
|
|
320
|
-
body: { langs: r.langs, voice: r.voice, voiceBySpeaker: r.voiceBySpeaker, near: r.near },
|
|
271
|
+
body: { langs: r.langs, voices: r.voices, voice: r.voice, voiceBySpeaker: r.voiceBySpeaker, near: r.near, consent: r.consent },
|
|
321
272
|
...call,
|
|
322
273
|
});
|
|
323
274
|
}
|
|
324
275
|
voiceClip(id, sentence, lang, opts = {}) {
|
|
325
|
-
return this.#t.raw('GET', this.#p(id, `/voice/${seg(sentence)}`), { headers: { accept: '*/*' }, query: { ...listenerQuery(opts), lang, format: opts.format } });
|
|
276
|
+
return this.#t.raw('GET', this.#p(id, `/voice/${seg(sentence)}`), { headers: { accept: '*/*' }, query: { ...listenerQuery(opts), lang, format: opts.format, voices: opts.voices } });
|
|
326
277
|
}
|
|
327
278
|
mediaToken(id) {
|
|
328
279
|
return this.#t.json('GET', this.#p(id, '/media-token'));
|
|
@@ -542,15 +493,26 @@ export class Search {
|
|
|
542
493
|
}
|
|
543
494
|
query(q, opts = {}) {
|
|
544
495
|
const query = {
|
|
496
|
+
...filterQuery({ site: opts.site, language: opts.language, kind: opts.kind, origin: opts.origin, tag: opts.tag, createdAfter: opts.createdAfter, createdBefore: opts.createdBefore, shared: opts.shared }),
|
|
545
497
|
q,
|
|
546
498
|
broadcasts: opts.broadcasts?.join(','),
|
|
547
499
|
listenerTokens: opts.listenerTokens?.join(','),
|
|
548
500
|
recording: opts.recording,
|
|
501
|
+
transcription: opts.transcription,
|
|
549
502
|
lang: opts.lang,
|
|
550
503
|
limit: opts.limit,
|
|
551
504
|
};
|
|
552
505
|
return this.#t.json('GET', '/v1/search', { query });
|
|
553
506
|
}
|
|
507
|
+
vector(q, opts = {}) {
|
|
508
|
+
const query = {
|
|
509
|
+
...filterQuery({ site: opts.site, language: opts.language, kind: opts.kind, origin: opts.origin, tag: opts.tag, createdAfter: opts.createdAfter, createdBefore: opts.createdBefore, shared: opts.shared }),
|
|
510
|
+
q,
|
|
511
|
+
recording: opts.recording,
|
|
512
|
+
transcription: opts.transcription,
|
|
513
|
+
};
|
|
514
|
+
return this.#t.json('GET', '/v1/search/vector', { query });
|
|
515
|
+
}
|
|
554
516
|
}
|
|
555
517
|
export class MeApi {
|
|
556
518
|
#t;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vocalstack/js-sdk",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.3",
|
|
4
4
|
"description": "VocalStack speech API SDK for Node and browsers: one client (VocalStack) grouped by resource, typed from the generated contract, with realtime WebSocket streaming.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,20 +25,16 @@
|
|
|
25
25
|
"skills"
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
|
-
"build": "
|
|
28
|
+
"build": "tsc -p tsconfig.json",
|
|
29
29
|
"test": "node --test test/*.test.mjs"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@vocalstack/contract": "1.0.0-alpha.
|
|
32
|
+
"@vocalstack/contract": "1.0.0-alpha.3",
|
|
33
33
|
"ws": "^8.18.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^22.0.0",
|
|
37
37
|
"@types/ws": "^8.5.0",
|
|
38
38
|
"typescript": "^5.6.0"
|
|
39
|
-
},
|
|
40
|
-
"repository": {
|
|
41
|
-
"type": "git",
|
|
42
|
-
"url": "git+https://github.com/vocalstack/js-sdk.git"
|
|
43
39
|
}
|
|
44
40
|
}
|
|
@@ -19,9 +19,6 @@ one group per resource: `vs.transcriptions`, `vs.jobs`, `vs.recordings`,
|
|
|
19
19
|
- A **job** is a file or link recording's transcription work:
|
|
20
20
|
`vs.jobs.wait(recordingId)` polls it until it is `done` or `failed`
|
|
21
21
|
(`{ onProgress: (job) => … }` sees each read: `job.state`, `job.progress.pct`).
|
|
22
|
-
- A **batch** is many files transcribed together for less than one
|
|
23
|
-
transcription each: `vs.batches.create()`, `addFile`/`addUrl` per file,
|
|
24
|
-
`start`, then `wait` (section 6).
|
|
25
22
|
- A **broadcast** is a link listeners open to follow a live recording.
|
|
26
23
|
- A **realtime session** (`vs.realtime`) is a socket you stream audio into and
|
|
27
24
|
read the transcript from. Nothing is stored. "Session" means only this.
|
|
@@ -179,26 +176,14 @@ for (const h of hits) {
|
|
|
179
176
|
}
|
|
180
177
|
```
|
|
181
178
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
`settings.speakerLabels` asks), never a summary, title, share link or voices.
|
|
179
|
+
By meaning, in any language, even with none of the words (vector search). Each
|
|
180
|
+
hit's `passage` is its whole paragraph: give the passages to your own model as
|
|
181
|
+
context to answer questions about what was said (RAG). Pay as you go counts 60
|
|
182
|
+
an hour (`allowance`); Premium and Enterprise have no limit.
|
|
187
183
|
|
|
188
184
|
```js
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
const batch = await vs.batches.create({ name: 'Interviews', settings: { spokenLanguage: 'en' } });
|
|
193
|
-
for (const path of process.argv.slice(2)) await vs.batches.addFile(batch.id, { file: path }); // or addUrl(batch.id, url)
|
|
194
|
-
await vs.batches.start(batch.id);
|
|
195
|
-
const done = await vs.batches.wait(batch.id); // done (or stopped); check done.counts.failed
|
|
196
|
-
for (const tx of done.files ?? []) {
|
|
197
|
-
if (tx.job?.state !== 'done') continue;
|
|
198
|
-
const { sentences } = await vs.recordings.transcript(tx.job.recordingId);
|
|
199
|
-
console.log(tx.name, sentences.map((s) => s.text).join(' '));
|
|
200
|
-
}
|
|
201
|
-
// vs.batches.export(batch.id, 'srt') is every finished transcript in one zip.
|
|
185
|
+
const { hits: found } = await vs.search.vector('how much does housing cost');
|
|
186
|
+
for (const h of found) console.log(h.similarity.toFixed(2), h.passage.mediaStart, h.passage.text);
|
|
202
187
|
```
|
|
203
188
|
|
|
204
189
|
## Errors
|
|
@@ -238,8 +223,7 @@ try {
|
|
|
238
223
|
- The other groups: `vs.transcriptions` (`list`, `get`, `update`, `archive`,
|
|
239
224
|
`restore`, `purge`, `listShareLinks`, `createShareLink`), `vs.recordings`
|
|
240
225
|
(`get`, `summarize`, `summary`, `translation`, `audioUrl`, `subscribe` for a
|
|
241
|
-
live transcript feed), `vs.
|
|
242
|
-
`wait`, `get`, `list`, `export`, `stop`, `delete`), `vs.corrections`, `vs.shareLinks`, `vs.voices`,
|
|
226
|
+
live transcript feed), `vs.corrections`, `vs.shareLinks`, `vs.voices`,
|
|
243
227
|
`vs.broadcasts`, `vs.me` (`get`, `usage`), `vs.languages`,
|
|
244
228
|
`vs.rtmp`, `vs.events`, `vs.meta`. Each method is named after its operation
|
|
245
229
|
in the OpenAPI document (`transcriptions.create` is `POST /v1/transcriptions`).
|