@spekoai/sdk 0.2.1 → 0.4.0

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 +26 -0
  2. package/README.md +3 -0
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/lib/client.d.ts +12 -1
  6. package/dist/lib/client.d.ts.map +1 -1
  7. package/dist/lib/client.js +21 -0
  8. package/dist/lib/http.d.ts +15 -0
  9. package/dist/lib/http.d.ts.map +1 -1
  10. package/dist/lib/http.js +169 -0
  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 +2 -1
  15. package/dist/lib/resources/complete.d.ts.map +1 -1
  16. package/dist/lib/resources/complete.js +27 -1
  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 +49 -0
  24. package/dist/lib/resources/phone-numbers.d.ts.map +1 -0
  25. package/dist/lib/resources/phone-numbers.js +66 -0
  26. package/dist/lib/resources/synthesize.d.ts +2 -1
  27. package/dist/lib/resources/synthesize.d.ts.map +1 -1
  28. package/dist/lib/resources/synthesize.js +31 -3
  29. package/dist/lib/resources/transcribe.d.ts +2 -1
  30. package/dist/lib/resources/transcribe.d.ts.map +1 -1
  31. package/dist/lib/resources/transcribe.js +32 -1
  32. package/dist/lib/resources/voice.d.ts +29 -0
  33. package/dist/lib/resources/voice.d.ts.map +1 -0
  34. package/dist/lib/resources/voice.js +30 -0
  35. package/dist/lib/types/index.d.ts +411 -5
  36. package/dist/lib/types/index.d.ts.map +1 -1
  37. package/package.json +1 -1
@@ -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 Bun.file('faq.md').arrayBuffer(),
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":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,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;IASvE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAM5C,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,CACT,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,CAAC;IAMpC,cAAc,CACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,iCAAiC,GACxC,OAAO,CAAC,iCAAiC,CAAC;IAO7C,gBAAgB,CACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,CAAC;IAOpC,cAAc,CACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAMhC;;;;;;;;;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 Bun.file('faq.md').arrayBuffer(),
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
+ }
@@ -0,0 +1,49 @@
1
+ import type { HttpClient } from '../http.js';
2
+ import type { AvailablePhoneNumber, PhoneNumberCreateParams, PhoneNumberRow, PhoneNumberSearchParams, PhoneNumberUpdateParams } from '../types/index.js';
3
+ /**
4
+ * Phone numbers Speko has provisioned (or imported) on Telnyx for your
5
+ * organization. Each number can be used for outbound dialing and/or
6
+ * inbound, and carries an optional metadata template that's merged into
7
+ * the worker dispatch payload when the number is used.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * // List org's numbers
12
+ * const numbers = await speko.phoneNumbers.list();
13
+ *
14
+ * // Provision a new US local number
15
+ * const num = await speko.phoneNumbers.create({
16
+ * e164: '+12015551234',
17
+ * direction: 'both',
18
+ * label: 'Sales line',
19
+ * dispatchMetadataTemplate: {
20
+ * intent: { language: 'en', optimizeFor: 'latency' },
21
+ * systemPrompt: 'You are a helpful sales agent for Acme.',
22
+ * },
23
+ * });
24
+ * ```
25
+ */
26
+ export declare class PhoneNumbers {
27
+ private readonly http;
28
+ constructor(http: HttpClient);
29
+ list(): Promise<PhoneNumberRow[]>;
30
+ /**
31
+ * Search Telnyx's pool for orderable US numbers. Filter by area code
32
+ * and/or locality. Results include cost so you can preview "$1 upfront +
33
+ * $1/month" before committing to {@link create}.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const candidates = await speko.phoneNumbers.searchAvailable({ areaCode: '415' });
38
+ * console.log(candidates[0].friendlyName); // "+1 (415) 555-0123"
39
+ * ```
40
+ */
41
+ searchAvailable(params?: PhoneNumberSearchParams): Promise<AvailablePhoneNumber[]>;
42
+ get(id: string): Promise<PhoneNumberRow>;
43
+ create(params: PhoneNumberCreateParams): Promise<PhoneNumberRow>;
44
+ update(id: string, params: PhoneNumberUpdateParams): Promise<PhoneNumberRow>;
45
+ delete(id: string): Promise<{
46
+ released: boolean;
47
+ }>;
48
+ }
49
+ //# sourceMappingURL=phone-numbers.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Phone numbers Speko has provisioned (or imported) on Telnyx for your
3
+ * organization. Each number can be used for outbound dialing and/or
4
+ * inbound, and carries an optional metadata template that's merged into
5
+ * the worker dispatch payload when the number is used.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // List org's numbers
10
+ * const numbers = await speko.phoneNumbers.list();
11
+ *
12
+ * // Provision a new US local number
13
+ * const num = await speko.phoneNumbers.create({
14
+ * e164: '+12015551234',
15
+ * direction: 'both',
16
+ * label: 'Sales line',
17
+ * dispatchMetadataTemplate: {
18
+ * intent: { language: 'en', optimizeFor: 'latency' },
19
+ * systemPrompt: 'You are a helpful sales agent for Acme.',
20
+ * },
21
+ * });
22
+ * ```
23
+ */
24
+ export class PhoneNumbers {
25
+ http;
26
+ constructor(http) {
27
+ this.http = http;
28
+ }
29
+ list() {
30
+ return this.http.get('/v1/phone-numbers');
31
+ }
32
+ /**
33
+ * Search Telnyx's pool for orderable US numbers. Filter by area code
34
+ * and/or locality. Results include cost so you can preview "$1 upfront +
35
+ * $1/month" before committing to {@link create}.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const candidates = await speko.phoneNumbers.searchAvailable({ areaCode: '415' });
40
+ * console.log(candidates[0].friendlyName); // "+1 (415) 555-0123"
41
+ * ```
42
+ */
43
+ searchAvailable(params = {}) {
44
+ const query = new URLSearchParams();
45
+ if (params.areaCode)
46
+ query.set('areaCode', params.areaCode);
47
+ if (params.locality)
48
+ query.set('locality', params.locality);
49
+ if (params.limit !== undefined)
50
+ query.set('limit', String(params.limit));
51
+ const qs = query.toString();
52
+ return this.http.get(`/v1/phone-numbers/available${qs ? `?${qs}` : ''}`);
53
+ }
54
+ get(id) {
55
+ return this.http.get(`/v1/phone-numbers/${encodeURIComponent(id)}`);
56
+ }
57
+ create(params) {
58
+ return this.http.post('/v1/phone-numbers', params);
59
+ }
60
+ update(id, params) {
61
+ return this.http.patch(`/v1/phone-numbers/${encodeURIComponent(id)}`, params);
62
+ }
63
+ delete(id) {
64
+ return this.http.delete(`/v1/phone-numbers/${encodeURIComponent(id)}`);
65
+ }
66
+ }
@@ -1,5 +1,5 @@
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);
@@ -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"}
@@ -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,5 +1,5 @@
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);
@@ -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":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAG3B,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;CA+BhD"}
@@ -1,3 +1,4 @@
1
+ import { SpekoApiError } from '../errors.js';
1
2
  export class Transcribe {
2
3
  http;
3
4
  constructor(http) {
@@ -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,14 @@ 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 { ...event.data, type: event.event };
62
+ }
32
63
  }
33
64
  }
@@ -0,0 +1,29 @@
1
+ import type { HttpClient } from '../http.js';
2
+ import type { VoiceDialParams, VoiceDialResult } from '../types/index.js';
3
+ /**
4
+ * Outbound phone calls via Speko's Telnyx-backed gateway.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const { sessionId, status } = await speko.voice.dial({
9
+ * to: '+12015551234',
10
+ * intent: { language: 'en', optimizeFor: 'latency' },
11
+ * systemPrompt: 'You are a helpful Speko assistant. Greet the caller in English.',
12
+ * });
13
+ * console.log('dialing:', sessionId, status);
14
+ * ```
15
+ */
16
+ export declare class Voice {
17
+ private readonly http;
18
+ constructor(http: HttpClient);
19
+ /**
20
+ * Place an outbound call. The destination's phone rings; once they pick
21
+ * up, audio bridges to a Speko worker running the configured pipeline
22
+ * (STT→LLM→TTS) on a LiveKit room.
23
+ *
24
+ * Lifecycle is observable via Telnyx webhooks (which Speko handles
25
+ * server-side) and reflected in the `voice_session` row's `status`.
26
+ */
27
+ dial(params: VoiceDialParams): Promise<VoiceDialResult>;
28
+ }
29
+ //# sourceMappingURL=voice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.d.ts","sourceRoot":"","sources":["../../../src/lib/resources/voice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE1E;;;;;;;;;;;;GAYG;AACH,qBAAa,KAAK;IACJ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C;;;;;;;OAOG;IACG,IAAI,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;CAG9D"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Outbound phone calls via Speko's Telnyx-backed gateway.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * const { sessionId, status } = await speko.voice.dial({
7
+ * to: '+12015551234',
8
+ * intent: { language: 'en', optimizeFor: 'latency' },
9
+ * systemPrompt: 'You are a helpful Speko assistant. Greet the caller in English.',
10
+ * });
11
+ * console.log('dialing:', sessionId, status);
12
+ * ```
13
+ */
14
+ export class Voice {
15
+ http;
16
+ constructor(http) {
17
+ this.http = http;
18
+ }
19
+ /**
20
+ * Place an outbound call. The destination's phone rings; once they pick
21
+ * up, audio bridges to a Speko worker running the configured pipeline
22
+ * (STT→LLM→TTS) on a LiveKit room.
23
+ *
24
+ * Lifecycle is observable via Telnyx webhooks (which Speko handles
25
+ * server-side) and reflected in the `voice_session` row's `status`.
26
+ */
27
+ async dial(params) {
28
+ return this.http.post('/v1/sessions/phone', params);
29
+ }
30
+ }