@pyai/sdk 0.4.0 → 0.6.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/src/index.ts CHANGED
@@ -165,8 +165,15 @@ function assertActiveSpeechParams(params: SpeechParams): void {
165
165
  }
166
166
  }
167
167
 
168
- export interface CreateJobParams {
169
- audio_url: string;
168
+ export type CreateJobParams = CreateJobOptions & (
169
+ | { audio_url: string; gpt_live?: never }
170
+ | { gpt_live: { session_id: string }; audio_url?: never }
171
+ );
172
+
173
+ export interface CreateJobOptions {
174
+ /** Requires Trace entitlement; incompatible with channel/diarize. */
175
+ trace?: boolean;
176
+ rule_pack?: Record<string, unknown>;
170
177
  model?: string;
171
178
  diarize?: boolean;
172
179
  channel?: boolean;
@@ -731,6 +738,8 @@ export const OmniEvent = {
731
738
  Hello: "hello",
732
739
  /** Ack for your `configure` frame (echoes the resolved `voice_id`). */
733
740
  Configured: "configured",
741
+ /** Served voice capabilities changed, for example after synthesis fallback. */
742
+ VoiceCapabilities: "voice_capabilities",
734
743
  /** Session is live; includes the resolved agent + audio caps. */
735
744
  SessionStarted: "session_started",
736
745
  /** Turn boundary (user/assistant speaking). */
@@ -1066,6 +1075,17 @@ export class OmniConnection {
1066
1075
  try {
1067
1076
  const parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(1))) as OmniServerFrame;
1068
1077
  if (parsed?.event === OmniEvent.Transcript) {
1078
+ // The deployed engine also sends this bounded synthesis-input
1079
+ // advisory. It is not caller ASR or proof that speech was played.
1080
+ const keys = Object.keys(parsed);
1081
+ const text = omniTranscriptText(parsed.text);
1082
+ if (parsed.role === "assistant" && parsed.final === true && text?.trim()
1083
+ && keys.length === 4
1084
+ && keys.every((key) => ["event", "role", "text", "final"].includes(key))) {
1085
+ this.opts.onEvent?.(parsed);
1086
+ this.opts.onTranscript?.({ event: "transcript", role: "assistant", text, final: true, mode: "replace" });
1087
+ return;
1088
+ }
1069
1089
  this.opts.onError?.(new Error("Omni transcript events must use a binary 0x02 frame"));
1070
1090
  return;
1071
1091
  }
@@ -1593,16 +1613,21 @@ export interface AmdCall extends AmdCallSummary {
1593
1613
 
1594
1614
  /**
1595
1615
  * A mid-call AMD decision event pushed on the stream (and to the per-call
1596
- * TwiML `webhook`). Carries the coarse routing class; the machine subtype
1597
- * (`voicemail`/`ivr`/`screening`/`music`) is on the stored call record
1598
- * (`AmdCall`) and the `amd.call.completed` webhook instead.
1616
+ * TwiML `webhook`). Carries the routing class and an optional machine subtype.
1617
+ * Stored `AmdCall` records fold the subtype into `answered_by`.
1599
1618
  */
1600
1619
  export interface AmdDecisionEvent {
1601
1620
  event?: "amd";
1602
1621
  call_id?: string;
1603
1622
  answered_by?: AmdWireAnsweredBy;
1604
1623
  answered_by_twilio?: string | null;
1624
+ subtype?: string;
1625
+ /** A human or automated answering party was identified. */
1626
+ party_detected?: boolean;
1627
+ /** Classification events return false; detection does not establish recording readiness. */
1628
+ voicemail_ready?: boolean;
1605
1629
  confidence?: number | null;
1630
+ /** Processed inbound audio through the decision, not time since carrier answer. */
1606
1631
  decision_ms?: number | null;
1607
1632
  reason?: string | null;
1608
1633
  [k: string]: unknown;