@spekoai/sdk 0.3.0 → 0.4.1

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +4 -3
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/lib/client.d.ts +11 -4
  6. package/dist/lib/client.d.ts.map +1 -1
  7. package/dist/lib/client.js +23 -8
  8. package/dist/lib/http.d.ts +14 -0
  9. package/dist/lib/http.d.ts.map +1 -1
  10. package/dist/lib/http.js +167 -1
  11. package/dist/lib/resources/agents.d.ts +68 -0
  12. package/dist/lib/resources/agents.d.ts.map +1 -0
  13. package/dist/lib/resources/agents.js +90 -0
  14. package/dist/lib/resources/complete.d.ts +3 -2
  15. package/dist/lib/resources/complete.d.ts.map +1 -1
  16. package/dist/lib/resources/complete.js +31 -2
  17. package/dist/lib/resources/credits.d.ts +1 -2
  18. package/dist/lib/resources/credits.d.ts.map +1 -1
  19. package/dist/lib/resources/credits.js +1 -2
  20. package/dist/lib/resources/knowledge-bases.d.ts +60 -0
  21. package/dist/lib/resources/knowledge-bases.d.ts.map +1 -0
  22. package/dist/lib/resources/knowledge-bases.js +138 -0
  23. package/dist/lib/resources/phone-numbers.d.ts.map +1 -1
  24. package/dist/lib/resources/realtime.d.ts +1 -1
  25. package/dist/lib/resources/realtime.d.ts.map +1 -1
  26. package/dist/lib/resources/realtime.js +28 -3
  27. package/dist/lib/resources/synthesize.d.ts +3 -2
  28. package/dist/lib/resources/synthesize.d.ts.map +1 -1
  29. package/dist/lib/resources/synthesize.js +32 -4
  30. package/dist/lib/resources/transcribe.d.ts +3 -2
  31. package/dist/lib/resources/transcribe.d.ts.map +1 -1
  32. package/dist/lib/resources/transcribe.js +36 -2
  33. package/dist/lib/resources/usage.d.ts +1 -1
  34. package/dist/lib/resources/usage.d.ts.map +1 -1
  35. package/dist/lib/types/index.d.ts +316 -5
  36. package/dist/lib/types/index.d.ts.map +1 -1
  37. package/package.json +3 -3
@@ -1,8 +1,7 @@
1
1
  import type { HttpClient } from '../http.js';
2
2
  import type { CreditLedgerPage, CreditLedgerQueryParams, OrganizationBalance } from '../types/index.js';
3
3
  /**
4
- * Prepaid credit balance + append-only ledger. Balance is reported in
5
- * micro-USD (`1_000_000` µ$ = $1) alongside a float USD helper.
4
+ * Prepaid credit balance + append-only ledger. Balance is reported in USD.
6
5
  */
7
6
  export declare class Credits {
8
7
  private readonly http;
@@ -1 +1 @@
1
- {"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/credits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAE3B;;;GAGG;AACH,qBAAa,OAAO;IACN,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;;OAQG;IACH,UAAU,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAI1C;;;;;OAKG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAUvE"}
1
+ {"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/credits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,qBAAa,OAAO;IACN,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;;OAQG;IACH,UAAU,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAI1C;;;;;OAKG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAQvE"}
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Prepaid credit balance + append-only ledger. Balance is reported in
3
- * micro-USD (`1_000_000` µ$ = $1) alongside a float USD helper.
2
+ * Prepaid credit balance + append-only ledger. Balance is reported in USD.
4
3
  */
5
4
  export class Credits {
6
5
  http;
@@ -0,0 +1,60 @@
1
+ import type { HttpClient } from '../http.js';
2
+ import type { KnowledgeBaseCreateParams, KnowledgeBaseDocumentCreateParams, KnowledgeBaseDocumentCreateResult, KnowledgeBaseDocumentPollOptions, KnowledgeBaseDocumentRow, KnowledgeBaseDocumentUploadParams, KnowledgeBaseListParams, KnowledgeBaseRow } from '../types/index.js';
3
+ /**
4
+ * Per-agent knowledge bases. Each KB owns documents that get embedded
5
+ * into a Chroma collection so the agent can retrieve relevant chunks
6
+ * during a call. Every agent created via {@link Agents.create}
7
+ * auto-provisions a `Default` KB; additional KBs can be created
8
+ * explicitly with {@link KnowledgeBases.create}.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const kb = await speko.knowledgeBases.create({
13
+ * agentId: agent.id,
14
+ * name: 'Product FAQ',
15
+ * });
16
+ *
17
+ * const doc = await speko.knowledgeBases.uploadDocument(kb.id, {
18
+ * filename: 'faq.md',
19
+ * contentType: 'text/markdown',
20
+ * data: await readFile('faq.md'),
21
+ * });
22
+ *
23
+ * const ready = await speko.knowledgeBases.pollDocumentReady(kb.id, doc.id);
24
+ * ```
25
+ */
26
+ export declare class KnowledgeBases {
27
+ private readonly http;
28
+ constructor(http: HttpClient);
29
+ create(params: KnowledgeBaseCreateParams): Promise<KnowledgeBaseRow>;
30
+ list(params?: KnowledgeBaseListParams): Promise<KnowledgeBaseRow[]>;
31
+ get(kbId: string): Promise<KnowledgeBaseRow>;
32
+ delete(kbId: string): Promise<{
33
+ deleted: boolean;
34
+ }>;
35
+ listDocuments(kbId: string): Promise<KnowledgeBaseDocumentRow[]>;
36
+ getDocument(kbId: string, docId: string): Promise<KnowledgeBaseDocumentRow>;
37
+ createDocument(kbId: string, params: KnowledgeBaseDocumentCreateParams): Promise<KnowledgeBaseDocumentCreateResult>;
38
+ finalizeDocument(kbId: string, docId: string): Promise<KnowledgeBaseDocumentRow>;
39
+ deleteDocument(kbId: string, docId: string): Promise<{
40
+ deleted: boolean;
41
+ }>;
42
+ /**
43
+ * Convenience wrapper: register a document, upload its bytes to the
44
+ * signed PUT URL the server mints, then call finalize. Returns the
45
+ * document with status flipped to `processing`. Call
46
+ * {@link pollDocumentReady} to wait for ingest completion.
47
+ *
48
+ * The server's signed upload URL expires 600 seconds after issuance,
49
+ * so do not hold the result of {@link createDocument} for long before
50
+ * uploading. This wrapper performs all three steps back-to-back.
51
+ */
52
+ uploadDocument(kbId: string, params: KnowledgeBaseDocumentUploadParams): Promise<KnowledgeBaseDocumentRow>;
53
+ /**
54
+ * Poll a document until it reaches `ready` or `failed` status, or the
55
+ * timeout elapses. Throws {@link SpekoApiError} on `failed` (with the
56
+ * server's `errorMessage`) or on timeout.
57
+ */
58
+ pollDocumentReady(kbId: string, docId: string, opts?: KnowledgeBaseDocumentPollOptions): Promise<KnowledgeBaseDocumentRow>;
59
+ }
60
+ //# sourceMappingURL=knowledge-bases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge-bases.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/knowledge-bases.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,yBAAyB,EACzB,iCAAiC,EACjC,iCAAiC,EACjC,gCAAgC,EAChC,wBAAwB,EACxB,iCAAiC,EACjC,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAK3B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIpE,IAAI,CAAC,MAAM,GAAE,uBAA4B,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAOvE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI5C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAMnD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAMhE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAM3E,cAAc,CACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iCAAiC,GACxC,OAAO,CAAC,iCAAiC,CAAC;IAO7C,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAOhF,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAM1E;;;;;;;;;OASG;IACG,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iCAAiC,GACxC,OAAO,CAAC,wBAAwB,CAAC;IA4BpC;;;;OAIG;IACG,iBAAiB,CACrB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,gCAAqC,GAC1C,OAAO,CAAC,wBAAwB,CAAC;CAwBrC"}
@@ -0,0 +1,138 @@
1
+ import { SpekoApiError } from '../errors.js';
2
+ const DEFAULT_POLL_INTERVAL_MS = 2_000;
3
+ const DEFAULT_POLL_TIMEOUT_MS = 120_000;
4
+ /**
5
+ * Per-agent knowledge bases. Each KB owns documents that get embedded
6
+ * into a Chroma collection so the agent can retrieve relevant chunks
7
+ * during a call. Every agent created via {@link Agents.create}
8
+ * auto-provisions a `Default` KB; additional KBs can be created
9
+ * explicitly with {@link KnowledgeBases.create}.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const kb = await speko.knowledgeBases.create({
14
+ * agentId: agent.id,
15
+ * name: 'Product FAQ',
16
+ * });
17
+ *
18
+ * const doc = await speko.knowledgeBases.uploadDocument(kb.id, {
19
+ * filename: 'faq.md',
20
+ * contentType: 'text/markdown',
21
+ * data: await readFile('faq.md'),
22
+ * });
23
+ *
24
+ * const ready = await speko.knowledgeBases.pollDocumentReady(kb.id, doc.id);
25
+ * ```
26
+ */
27
+ export class KnowledgeBases {
28
+ http;
29
+ constructor(http) {
30
+ this.http = http;
31
+ }
32
+ create(params) {
33
+ return this.http.post('/v1/knowledge-bases', params);
34
+ }
35
+ list(params = {}) {
36
+ const query = new URLSearchParams();
37
+ if (params.agentId)
38
+ query.set('agentId', params.agentId);
39
+ const qs = query.toString();
40
+ return this.http.get(`/v1/knowledge-bases${qs ? `?${qs}` : ''}`);
41
+ }
42
+ get(kbId) {
43
+ return this.http.get(`/v1/knowledge-bases/${encodeURIComponent(kbId)}`);
44
+ }
45
+ delete(kbId) {
46
+ return this.http.delete(`/v1/knowledge-bases/${encodeURIComponent(kbId)}`);
47
+ }
48
+ listDocuments(kbId) {
49
+ return this.http.get(`/v1/knowledge-bases/${encodeURIComponent(kbId)}/documents`);
50
+ }
51
+ getDocument(kbId, docId) {
52
+ return this.http.get(`/v1/knowledge-bases/${encodeURIComponent(kbId)}/documents/${encodeURIComponent(docId)}`);
53
+ }
54
+ createDocument(kbId, params) {
55
+ return this.http.post(`/v1/knowledge-bases/${encodeURIComponent(kbId)}/documents`, params);
56
+ }
57
+ finalizeDocument(kbId, docId) {
58
+ return this.http.post(`/v1/knowledge-bases/${encodeURIComponent(kbId)}/documents/${encodeURIComponent(docId)}/finalize`, {});
59
+ }
60
+ deleteDocument(kbId, docId) {
61
+ return this.http.delete(`/v1/knowledge-bases/${encodeURIComponent(kbId)}/documents/${encodeURIComponent(docId)}`);
62
+ }
63
+ /**
64
+ * Convenience wrapper: register a document, upload its bytes to the
65
+ * signed PUT URL the server mints, then call finalize. Returns the
66
+ * document with status flipped to `processing`. Call
67
+ * {@link pollDocumentReady} to wait for ingest completion.
68
+ *
69
+ * The server's signed upload URL expires 600 seconds after issuance,
70
+ * so do not hold the result of {@link createDocument} for long before
71
+ * uploading. This wrapper performs all three steps back-to-back.
72
+ */
73
+ async uploadDocument(kbId, params) {
74
+ const sizeBytes = byteLengthOf(params.data);
75
+ const { document, upload } = await this.createDocument(kbId, {
76
+ filename: params.filename,
77
+ contentType: params.contentType,
78
+ sizeBytes,
79
+ metadata: params.metadata,
80
+ });
81
+ const putResponse = await fetch(upload.url, {
82
+ method: upload.method,
83
+ headers: upload.headers,
84
+ body: toBodyInit(params.data),
85
+ });
86
+ if (!putResponse.ok) {
87
+ const text = await safeReadText(putResponse);
88
+ throw new SpekoApiError(`Document upload failed: ${putResponse.status} ${text || putResponse.statusText}`, putResponse.status, 'DOCUMENT_UPLOAD_FAILED');
89
+ }
90
+ return this.finalizeDocument(kbId, document.id);
91
+ }
92
+ /**
93
+ * Poll a document until it reaches `ready` or `failed` status, or the
94
+ * timeout elapses. Throws {@link SpekoApiError} on `failed` (with the
95
+ * server's `errorMessage`) or on timeout.
96
+ */
97
+ async pollDocumentReady(kbId, docId, opts = {}) {
98
+ const intervalMs = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
99
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
100
+ const deadline = Date.now() + timeoutMs;
101
+ while (Date.now() < deadline) {
102
+ const doc = await this.getDocument(kbId, docId);
103
+ if (doc.status === 'ready')
104
+ return doc;
105
+ if (doc.status === 'failed') {
106
+ throw new SpekoApiError(doc.errorMessage ?? 'Document ingest failed', 500, 'DOCUMENT_INGEST_FAILED');
107
+ }
108
+ await delay(intervalMs);
109
+ }
110
+ throw new SpekoApiError(`Document ingest timed out after ${timeoutMs}ms`, 408, 'DOCUMENT_INGEST_TIMEOUT');
111
+ }
112
+ }
113
+ function byteLengthOf(data) {
114
+ if (data instanceof Blob)
115
+ return data.size;
116
+ if (data instanceof Uint8Array)
117
+ return data.byteLength;
118
+ return data.byteLength;
119
+ }
120
+ function toBodyInit(data) {
121
+ if (data instanceof Blob)
122
+ return data;
123
+ if (data instanceof Uint8Array) {
124
+ return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
125
+ }
126
+ return data;
127
+ }
128
+ async function safeReadText(r) {
129
+ try {
130
+ return await r.text();
131
+ }
132
+ catch {
133
+ return '';
134
+ }
135
+ }
136
+ function delay(ms) {
137
+ return new Promise((resolve) => setTimeout(resolve, ms));
138
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"phone-numbers.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/phone-numbers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,oBAAoB,EACpB,uBAAuB,EACvB,cAAc,EACd,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAIjC;;;;;;;;;;OAUG;IACH,eAAe,CACb,MAAM,GAAE,uBAA4B,GACnC,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAWlC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAMxC,MAAM,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIhE,MAAM,CACJ,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,cAAc,CAAC;IAO1B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CAKnD"}
1
+ {"version":3,"file":"phone-numbers.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/phone-numbers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,oBAAoB,EACpB,uBAAuB,EACvB,cAAc,EACd,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,IAAI,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAIjC;;;;;;;;;;OAUG;IACH,eAAe,CAAC,MAAM,GAAE,uBAA4B,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAWtF,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAIxC,MAAM,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIhE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,cAAc,CAAC;IAI5E,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CAGnD"}
@@ -7,7 +7,7 @@ export declare class Realtime {
7
7
  * Open a speech-to-speech session. Posts `/v1/sessions` with `mode: 's2s'`
8
8
  * to mint a short-lived WebSocket token, then opens a direct WS to the
9
9
  * server's S2S proxy. The proxy bridges to the underlying provider
10
- * (OpenAI Realtime, Gemini Live, xAI Grok Voice) so the client sees a
10
+ * (OpenAI Realtime, Gemini Live, xAI Grok Voice, Inworld) so the client sees a
11
11
  * single transport regardless of which backend is in use.
12
12
  */
13
13
  connect(params: RealtimeConnectParams): Promise<RealtimeSessionHandle>;
@@ -1 +1 @@
1
- {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/realtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,qBAAqB,EAGrB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAU3B,qBAAa,QAAQ;IACP,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CA2B7E"}
1
+ {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/realtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,qBAAqB,EAGrB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAY3B,qBAAa,QAAQ;IACP,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CA0B7E"}
@@ -7,7 +7,7 @@ export class Realtime {
7
7
  * Open a speech-to-speech session. Posts `/v1/sessions` with `mode: 's2s'`
8
8
  * to mint a short-lived WebSocket token, then opens a direct WS to the
9
9
  * server's S2S proxy. The proxy bridges to the underlying provider
10
- * (OpenAI Realtime, Gemini Live, xAI Grok Voice) so the client sees a
10
+ * (OpenAI Realtime, Gemini Live, xAI Grok Voice, Inworld) so the client sees a
11
11
  * single transport regardless of which backend is in use.
12
12
  */
13
13
  async connect(params) {
@@ -26,18 +26,22 @@ export class Realtime {
26
26
  metadata: params.metadata,
27
27
  ttlSeconds: params.ttlSeconds,
28
28
  });
29
- return new BrowserRealtimeHandle(response.sessionId, response.wsUrl, response.wsToken, response.expiresAt);
29
+ return new BrowserRealtimeHandle(response.sessionId, response.wsUrl, response.wsToken, response.expiresAt, response.inputSampleRate, response.outputSampleRate);
30
30
  }
31
31
  }
32
32
  class BrowserRealtimeHandle {
33
33
  sessionId;
34
34
  expiresAt;
35
+ inputSampleRate;
36
+ outputSampleRate;
35
37
  ws;
36
38
  handlers = new Set();
37
39
  closed = false;
38
- constructor(sessionId, wsUrl, wsToken, expiresAt) {
40
+ constructor(sessionId, wsUrl, wsToken, expiresAt, inputSampleRate, outputSampleRate) {
39
41
  this.sessionId = sessionId;
40
42
  this.expiresAt = expiresAt;
43
+ this.inputSampleRate = inputSampleRate;
44
+ this.outputSampleRate = outputSampleRate;
41
45
  // Pass the token as the first subprotocol — browsers can't set headers
42
46
  // on `new WebSocket()`, so subprotocol is the only auth carrier that
43
47
  // doesn't leak through URL params.
@@ -134,6 +138,27 @@ class BrowserRealtimeHandle {
134
138
  final: Boolean(parsed['final']),
135
139
  });
136
140
  break;
141
+ case 'ready':
142
+ this.emit({
143
+ type: 'ready',
144
+ inputSampleRate: Number(parsed['inputSampleRate'] ?? this.inputSampleRate),
145
+ outputSampleRate: Number(parsed['outputSampleRate'] ?? this.outputSampleRate),
146
+ });
147
+ break;
148
+ case 'interruption':
149
+ this.emit({
150
+ type: 'interruption',
151
+ at: parsed['at'] === 'assistant' ? 'assistant' : 'user',
152
+ });
153
+ break;
154
+ case 'server_tool_call':
155
+ this.emit({
156
+ type: 'server_tool_call',
157
+ id: String(parsed['id'] ?? ''),
158
+ name: String(parsed['name'] ?? ''),
159
+ status: String(parsed['status'] ?? 'started'),
160
+ });
161
+ break;
137
162
  case 'tool_call':
138
163
  this.emit({
139
164
  type: 'tool_call',
@@ -1,11 +1,11 @@
1
1
  import type { HttpClient } from '../http.js';
2
- import type { SynthesizeOptions, SynthesizeResult } from '../types/index.js';
2
+ import type { SynthesizeOptions, SynthesizeResult, SynthesizeStreamResult } from '../types/index.js';
3
3
  export declare class Synthesize {
4
4
  private readonly http;
5
5
  constructor(http: HttpClient);
6
6
  /**
7
7
  * Synthesize text into audio. The Speko router picks the best TTS provider
8
- * for your `(language, optimizeFor)` and falls over automatically.
8
+ * for your `(language, region, optimizeFor)` and falls over automatically.
9
9
  *
10
10
  * The returned audio's format depends on the chosen provider — check the
11
11
  * `contentType` field on the result. ElevenLabs returns `audio/mpeg`,
@@ -20,5 +20,6 @@ export declare class Synthesize {
20
20
  * ```
21
21
  */
22
22
  call(text: string, options: SynthesizeOptions, abortSignal?: AbortSignal): Promise<SynthesizeResult>;
23
+ stream(text: string, options: SynthesizeOptions, abortSignal?: AbortSignal): Promise<SynthesizeStreamResult>;
23
24
  }
24
25
  //# sourceMappingURL=synthesize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/synthesize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;;;;;;;;;OAeG;IACG,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,EAC1B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,gBAAgB,CAAC;CA4B7B"}
1
+ {"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/synthesize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;;;;;;;;;OAeG;IACG,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,EAC1B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,gBAAgB,CAAC;IAiBtB,MAAM,CACV,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,EAC1B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,sBAAsB,CAAC;CAgCnC"}
@@ -5,7 +5,7 @@ export class Synthesize {
5
5
  }
6
6
  /**
7
7
  * Synthesize text into audio. The Speko router picks the best TTS provider
8
- * for your `(language, optimizeFor)` and falls over automatically.
8
+ * for your `(language, region, optimizeFor)` and falls over automatically.
9
9
  *
10
10
  * The returned audio's format depends on the chosen provider — check the
11
11
  * `contentType` field on the result. ElevenLabs returns `audio/mpeg`,
@@ -20,6 +20,21 @@ export class Synthesize {
20
20
  * ```
21
21
  */
22
22
  async call(text, options, abortSignal) {
23
+ const streamed = await this.stream(text, options, abortSignal);
24
+ const chunks = [];
25
+ for await (const chunk of streamed) {
26
+ chunks.push(chunk);
27
+ }
28
+ return {
29
+ audio: concatChunks(chunks),
30
+ contentType: streamed.contentType,
31
+ provider: streamed.provider,
32
+ model: streamed.model,
33
+ failoverCount: streamed.failoverCount,
34
+ scoresRunId: streamed.scoresRunId,
35
+ };
36
+ }
37
+ async stream(text, options, abortSignal) {
23
38
  const intent = {
24
39
  language: options.language,
25
40
  ...(options.region !== undefined && { region: options.region }),
@@ -32,14 +47,27 @@ export class Synthesize {
32
47
  body['speed'] = options.speed;
33
48
  if (options.constraints !== undefined)
34
49
  body['constraints'] = options.constraints;
35
- const { bytes, headers } = await this.http.requestBinary('POST', '/v1/synthesize', body, abortSignal);
36
- return {
37
- audio: bytes,
50
+ const { chunks, headers } = await this.http.requestBinaryStream('POST', '/v1/synthesize', body, abortSignal);
51
+ const result = {
38
52
  contentType: headers['content-type'] ?? 'application/octet-stream',
39
53
  provider: headers['x-speko-provider'] ?? 'unknown',
40
54
  model: headers['x-speko-model'] ?? 'unknown',
41
55
  failoverCount: parseInt(headers['x-speko-failover-count'] ?? '0', 10),
42
56
  scoresRunId: headers['x-speko-scores-run-id'] || null,
57
+ [Symbol.asyncIterator]() {
58
+ return chunks;
59
+ },
43
60
  };
61
+ return result;
62
+ }
63
+ }
64
+ function concatChunks(chunks) {
65
+ const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
66
+ const out = new Uint8Array(total);
67
+ let offset = 0;
68
+ for (const chunk of chunks) {
69
+ out.set(chunk, offset);
70
+ offset += chunk.byteLength;
44
71
  }
72
+ return out;
45
73
  }
@@ -1,11 +1,11 @@
1
1
  import type { HttpClient } from '../http.js';
2
- import type { TranscribeOptions, TranscribeResult } from '../types/index.js';
2
+ import type { TranscribeOptions, TranscribeResult, TranscribeStreamEvent } from '../types/index.js';
3
3
  export declare class Transcribe {
4
4
  private readonly http;
5
5
  constructor(http: HttpClient);
6
6
  /**
7
7
  * Transcribe audio. The Speko router picks the best STT provider for
8
- * your `(language, optimizeFor)` and falls over automatically.
8
+ * your `(language, region, optimizeFor)` and falls over automatically.
9
9
  *
10
10
  * @example
11
11
  * ```ts
@@ -16,5 +16,6 @@ export declare class Transcribe {
16
16
  * ```
17
17
  */
18
18
  call(audio: Uint8Array, options: TranscribeOptions, abortSignal?: AbortSignal): Promise<TranscribeResult>;
19
+ stream(audio: Uint8Array, options: TranscribeOptions, abortSignal?: AbortSignal): AsyncIterableIterator<TranscribeStreamEvent>;
19
20
  }
20
21
  //# sourceMappingURL=transcribe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transcribe.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/transcribe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;;;;;OAWG;IACG,IAAI,CACR,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,iBAAiB,EAC1B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,gBAAgB,CAAC;CAuB7B"}
1
+ {"version":3,"file":"transcribe.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/transcribe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAEpG,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;;;;;OAWG;IACG,IAAI,CACR,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,iBAAiB,EAC1B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,gBAAgB,CAAC;IAsBrB,MAAM,CACX,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,iBAAiB,EAC1B,WAAW,CAAC,EAAE,WAAW,GACxB,qBAAqB,CAAC,qBAAqB,CAAC;CAkChD"}
@@ -1,3 +1,4 @@
1
+ import { SpekoApiError } from '../errors.js';
1
2
  export class Transcribe {
2
3
  http;
3
4
  constructor(http) {
@@ -5,7 +6,7 @@ export class Transcribe {
5
6
  }
6
7
  /**
7
8
  * Transcribe audio. The Speko router picks the best STT provider for
8
- * your `(language, optimizeFor)` and falls over automatically.
9
+ * your `(language, region, optimizeFor)` and falls over automatically.
9
10
  *
10
11
  * @example
11
12
  * ```ts
@@ -16,6 +17,28 @@ export class Transcribe {
16
17
  * ```
17
18
  */
18
19
  async call(audio, options, abortSignal) {
20
+ let done;
21
+ for await (const event of this.stream(audio, options, abortSignal)) {
22
+ if (event.type === 'done') {
23
+ done = {
24
+ text: event.text,
25
+ provider: event.provider,
26
+ model: event.model,
27
+ confidence: event.confidence,
28
+ failoverCount: event.failoverCount,
29
+ scoresRunId: event.scoresRunId,
30
+ };
31
+ }
32
+ else if (event.type === 'error') {
33
+ throw new SpekoApiError(event.error, 200, event.code);
34
+ }
35
+ }
36
+ if (!done) {
37
+ throw new SpekoApiError('Transcribe stream ended without a done event', 200, 'STREAM_ENDED');
38
+ }
39
+ return done;
40
+ }
41
+ async *stream(audio, options, abortSignal) {
19
42
  const intent = {
20
43
  language: options.language,
21
44
  ...(options.region !== undefined && { region: options.region }),
@@ -28,6 +51,17 @@ export class Transcribe {
28
51
  if (options.constraints) {
29
52
  headers['X-Speko-Constraints'] = JSON.stringify(options.constraints);
30
53
  }
31
- return this.http.requestRaw('POST', '/v1/transcribe', audio, headers, abortSignal);
54
+ if (options.keywords && options.keywords.length > 0) {
55
+ headers['X-Speko-Stt-Options'] = JSON.stringify({
56
+ keywords: [...options.keywords],
57
+ });
58
+ }
59
+ const stream = await this.http.requestRawSse('POST', '/v1/transcribe', audio, headers, abortSignal);
60
+ for await (const event of stream) {
61
+ yield {
62
+ ...event.data,
63
+ type: event.event,
64
+ };
65
+ }
32
66
  }
33
67
  }
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../http.js';
2
- import type { UsageSummary, UsageQueryParams } from '../types/index.js';
2
+ import type { UsageQueryParams, UsageSummary } from '../types/index.js';
3
3
  export declare class Usage {
4
4
  private readonly http;
5
5
  constructor(http: HttpClient);
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAExE,qBAAa,KAAK;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;;OAQG;IACG,GAAG,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;CAU5D"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAExE,qBAAa,KAAK;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;;OAQG;IACG,GAAG,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;CAQ5D"}