@pyai/sdk 0.2.3 → 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.
- package/README.md +65 -19
- package/dist/index.d.ts +292 -60
- package/dist/index.js +189 -81
- package/package.json +18 -3
- package/src/index.ts +447 -121
package/README.md
CHANGED
|
@@ -8,10 +8,10 @@ dependencies; runs in the browser and Node 18+.
|
|
|
8
8
|
## PyAI products
|
|
9
9
|
|
|
10
10
|
- **[Hear](https://pyai.com/models/hear)**, Lightning-fast, telephony-native **speech-to-text**. Whisper-compatible transcription tuned for real phone-call audio, with live streaming partials so your app reacts mid-sentence, plus async batch transcription for big archives. `POST /v1/audio/transcriptions`
|
|
11
|
-
- **[Speak](https://pyai.com/models/speak)**, Ultra-realistic **text-to-speech** that starts speaking in tens of milliseconds. Stream lifelike, expressive voices, choose from
|
|
11
|
+
- **[Speak](https://pyai.com/models/speak)**, Ultra-realistic **text-to-speech** that starts speaking in tens of milliseconds. Stream lifelike, expressive voices, choose from 144 stock voices, or clone any voice instantly, for free. `POST /v1/audio/speech`
|
|
12
12
|
- **[Omni](https://pyai.com/models/omni)** _(flagship)_, One **API for a complete, end-to-end voice AI agent**. A single WebSocket where your agent listens, thinks, and speaks, grounded in your knowledge bases and tools, with human-like turn-taking and instant barge-in, no STT, LLM, or TTS to stitch together yourself. `wss://api.pyai.com/v1/omni`
|
|
13
13
|
- **[Trace](https://pyai.com/models/trace)** _(flagship)_, The **compliance API that keeps your AI agents safe**. Trace automatically checks every call for HIPAA, TCPA, and PII risks (plus your own brand-voice rules), flags the exact rule broken, redacts sensitive data, and seals each call with a tamper-evident audit trail, so a risky conversation never slips through. `GET /v1/trace/interactions`
|
|
14
|
-
- **
|
|
14
|
+
- **Cue**, reserved turn-detection and grounding fields on Hear streaming. Grounding is not active on the serving route yet.
|
|
15
15
|
- **[AMD](https://pyai.com/models/amd)**, **Answering-machine detection** that tells your dialer who or what answered, human, voicemail, IVR, iPhone/Google screening, dead number, fax, in a fraction of Twilio's dead-air dwell, with the reason. A one-line-TwiML Twilio Media Streams drop-in; billed per answered call (first 5,000/month free). `wss://api.pyai.com/v1/amd/stream`
|
|
16
16
|
- **[Telephony](https://pyai.com/models/telephony)**, Instant **managed phone numbers** for your voice agents. Provision a US number and route live calls straight into an Omni agent, no carrier contracts, no telephony glue. `POST /v1/telephony/numbers`
|
|
17
17
|
|
|
@@ -93,6 +93,16 @@ omni.sendDtmf("5");
|
|
|
93
93
|
omni.close();
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
+
Live `0x02` transcript bodies are plain UTF-8 caller-text deltas, not JSON.
|
|
97
|
+
`onTranscript` receives the normalized
|
|
98
|
+
`{ event:"transcript", role:"user", text, final:false, mode:"delta" }` shape.
|
|
99
|
+
Coalesce successive deltas for the current caller turn. Bounded direct JSON
|
|
100
|
+
bodies remain accepted for older bridges.
|
|
101
|
+
|
|
102
|
+
`rate` configures caller input. A `rate: 16000` session still receives 24 kHz
|
|
103
|
+
agent audio; `rate: 8000` receives 8 kHz. Read `hello.audio_out` before playback.
|
|
104
|
+
Omni has no commit frame—keep streaming silence during caller pauses.
|
|
105
|
+
|
|
96
106
|
From the browser, mint an ephemeral token server-side with
|
|
97
107
|
`pyai.omni.createSession({ allowedOrigins })` and pass it as `token` so the page
|
|
98
108
|
never holds a secret key:
|
|
@@ -101,34 +111,70 @@ never holds a secret key:
|
|
|
101
111
|
const omni = pyai.omni.connect({ token: session.token, configure: { voice_id, persona } });
|
|
102
112
|
```
|
|
103
113
|
|
|
104
|
-
> Omni
|
|
105
|
-
|
|
106
|
-
>
|
|
107
|
-
> `pyai.realtimeSubprotocol()` (or `pyai.connectRealtime()`)
|
|
108
|
-
>
|
|
109
|
-
>
|
|
114
|
+
> Omni connects only to `wss://api.pyai.com/v1/omni` and is **zero-state**, no
|
|
115
|
+
> agent to create. `sessionLabel` is an optional opaque tag (never required).
|
|
116
|
+
> Need the raw socket? Use `pyai.realtimeURL({ sessionLabel })` with
|
|
117
|
+
> `pyai.realtimeSubprotocol()` (or `pyai.connectRealtime()`).
|
|
118
|
+
> The raw URL helper accepts canonical `format`, `rate`, and `api_key` query
|
|
119
|
+
> parameters; retired connect aliases, model selectors, and token query names
|
|
120
|
+
> throw instead of being translated.
|
|
110
121
|
|
|
111
|
-
## Streaming speech-to-text (Hear
|
|
122
|
+
## Streaming speech-to-text (Hear)
|
|
112
123
|
|
|
113
124
|
`transcriptions.stream()` hides the WebSocket frame protocol behind callbacks.
|
|
114
|
-
It opens `wss://api.pyai.com/v1/audio/transcriptions/stream` (key carried as the
|
|
125
|
+
It opens `wss://api.pyai.com/v1/audio/transcriptions/stream?protocol=pyai-hear-v1` (key carried as the
|
|
115
126
|
WS subprotocol, so it works in the browser), routes the wire frames to
|
|
116
|
-
`onPartial`/`onFinal`/`onError`, and gives you `sendAudio`,
|
|
117
|
-
`close()`:
|
|
127
|
+
`onConfigAck`/`onPartial`/`onFinal`/`onError`, and gives you `sendAudio`,
|
|
128
|
+
`configureEndpointing()`, `commit()`, and `close()`:
|
|
118
129
|
|
|
119
130
|
```ts
|
|
120
131
|
const hear = pyai.audio.transcriptions.stream({
|
|
121
132
|
sampleRate: 16000,
|
|
133
|
+
endpointingMs: 800, // minimum trailing pause; may wait up to max(800, 1500) ms
|
|
134
|
+
vocabulary: ["Nguyen", "SKU-99"],
|
|
135
|
+
onConfigAck: (ack) => {
|
|
136
|
+
if (ack.warnings.length) throw new Error(JSON.stringify(ack.warnings));
|
|
137
|
+
},
|
|
122
138
|
onPartial: (f) => console.log("…", f.text),
|
|
123
|
-
onFinal: (f) => console.log("✓", f.text,
|
|
139
|
+
onFinal: (f) => console.log("✓", f.text, f.endpoint_reason),
|
|
124
140
|
onError: (e) => console.error(e),
|
|
125
141
|
});
|
|
126
142
|
|
|
127
|
-
micChunks.on("data", (pcm16) => hear.sendAudio(pcm16)); //
|
|
128
|
-
|
|
143
|
+
micChunks.on("data", (pcm16) => hear.sendAudio(pcm16)); // keep sending silence through pauses
|
|
144
|
+
hear.configureEndpointing(950); // update without reconnecting
|
|
145
|
+
vad.on("end", () => hear.commit()); // optional forced final
|
|
129
146
|
// hear.close() also flushes a final for any buffered audio
|
|
130
147
|
```
|
|
131
148
|
|
|
149
|
+
Streaming uses up to five sanitized vocabulary terms. To store organization
|
|
150
|
+
suggestions, use a key with `hear:configure` and set explicit activation
|
|
151
|
+
profiles first:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
await pyai.hear.vocabulary.set({
|
|
155
|
+
terms: ["Nguyen", "SKU-99"],
|
|
156
|
+
enabledFor: ["batch", "hear_stream"],
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Request-level terms come first. Stored suggestions fill remaining slots up to
|
|
161
|
+
five. The effective list is fixed when a stream opens or a batch job is created.
|
|
162
|
+
Organization Hear terms are not used by Omni and are not populated
|
|
163
|
+
automatically from CRM or dialer data. A managed Agent can opt in with its own
|
|
164
|
+
list:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
const agent = await pyai.agents.create({
|
|
168
|
+
name: "Front desk",
|
|
169
|
+
vocabulary: ["Nguyen", "Acme Dental", "SKU-99"],
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await pyai.agents.update(agent.agent_id, { vocabulary: [] });
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The Agent list is sanitized to at most five effective terms and fixed when a
|
|
176
|
+
new session starts. An empty list turns the feature off.
|
|
177
|
+
|
|
132
178
|
Frame `type`s, WS close codes, and error `code`s are exported as named
|
|
133
179
|
constants so you never hardcode a magic string:
|
|
134
180
|
|
|
@@ -163,8 +209,8 @@ const ulaw = await pyai.audio.speech({
|
|
|
163
209
|
|
|
164
210
|
| `response_format` | sample rates (Hz) | Content-Type |
|
|
165
211
|
|---|---|---|
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
212
|
+
| `wav` (default) | 8000 / 16000 / 24000 / 48000 | `audio/wav` |
|
|
213
|
+
| `mp3` | 8000 / 16000 / 24000 / 48000 | `audio/mpeg` |
|
|
168
214
|
| `opus` | 8000 / 16000 / 24000 / 48000 | `audio/ogg` |
|
|
169
215
|
| `aac` | 8000 / 16000 / 24000 / 48000 | `audio/aac` |
|
|
170
216
|
| `flac` | 8000 / 16000 / 24000 / 48000 | `audio/flac` |
|
|
@@ -175,7 +221,7 @@ const ulaw = await pyai.audio.speech({
|
|
|
175
221
|
`sample_rate` is optional, omit it for the engine's native 24 kHz (`g711_*` is
|
|
176
222
|
always 8 kHz). The set is typed (`SpeechFormat`) and exported as `SPEECH_FORMATS`
|
|
177
223
|
/ `SPEECH_SAMPLE_RATES` for dropdowns and validation. Any other value is a
|
|
178
|
-
`400 unsupported_format`; omit `response_format` for the default `
|
|
224
|
+
`400 unsupported_format`; omit `response_format` for the default `wav`.
|
|
179
225
|
|
|
180
226
|
> See [`examples/speak-telephony-formats`](../../examples/speak-telephony-formats)
|
|
181
227
|
> for the full before/after: ~120 lines of resampler + μ-law replaced by one
|
|
@@ -292,7 +338,7 @@ Speak→Hear round-trip, and prints remediation hints for any failure:
|
|
|
292
338
|
```bash
|
|
293
339
|
export PYAI_API_KEY=pyai_test_...
|
|
294
340
|
npx pyai doctor
|
|
295
|
-
# PASS key (/v1/me), env=test; 3 scope(s): hear:transcribe,
|
|
341
|
+
# PASS key (/v1/me), env=test; 3 scope(s): hear:transcribe, speak:synthesize, hear:stream
|
|
296
342
|
# PASS models.list, 12 models
|
|
297
343
|
# PASS voices.list, 38 voices
|
|
298
344
|
# PASS speak→hear round-trip, synth 45210 bytes → "the quick brown fox…"
|