@ai-ecoverse/pi-audio 1.20.0 → 1.21.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 +3 -1
- package/dist/captions.js +16 -8
- package/dist/captions.js.map +2 -2
- package/dist/durable/index.js +3 -1
- package/dist/durable/index.js.map +2 -2
- package/dist/fish-agents/events.js +94 -0
- package/dist/fish-agents/events.js.map +7 -0
- package/dist/fish-agents/index.js +57 -0
- package/dist/fish-agents/index.js.map +7 -0
- package/dist/fish-agents/provision.js +178 -0
- package/dist/fish-agents/provision.js.map +7 -0
- package/dist/fish-agents/room.js +80 -0
- package/dist/fish-agents/room.js.map +7 -0
- package/dist/fish-agents/session.js +304 -0
- package/dist/fish-agents/session.js.map +7 -0
- package/dist/realtime.js.map +2 -2
- package/dist/soundscape.js +14 -2
- package/dist/soundscape.js.map +2 -2
- package/dist/tokens.js +11 -1
- package/dist/tokens.js.map +2 -2
- package/dist/turns.js +78 -30
- package/dist/turns.js.map +2 -2
- package/dist/types/fish-agents/events.d.ts +21 -0
- package/dist/types/fish-agents/index.d.ts +11 -0
- package/dist/types/fish-agents/provision.d.ts +11 -0
- package/dist/types/fish-agents/room.d.ts +46 -0
- package/dist/types/fish-agents/session.d.ts +19 -0
- package/dist/types/realtime.d.ts +1 -0
- package/dist/vad-listen.js +43 -12
- package/dist/vad-listen.js.map +2 -2
- package/dist/vad.js +15 -7
- package/dist/vad.js.map +2 -2
- package/package.json +5 -1
package/dist/vad-listen.js
CHANGED
|
@@ -71,6 +71,7 @@ function closeTurn(session, at) {
|
|
|
71
71
|
session.pendingEnd = void 0;
|
|
72
72
|
session.heldEnd = void 0;
|
|
73
73
|
session.providerTurn = void 0;
|
|
74
|
+
session.sawFinal = false;
|
|
74
75
|
session.stream.push({ type: "turn_end", text, at });
|
|
75
76
|
}
|
|
76
77
|
function openTurn(session, at) {
|
|
@@ -79,6 +80,7 @@ function openTurn(session, at) {
|
|
|
79
80
|
session.started = true;
|
|
80
81
|
session.turnOpen = true;
|
|
81
82
|
session.pendingStart = void 0;
|
|
83
|
+
session.sawFinal = false;
|
|
82
84
|
}
|
|
83
85
|
function applyEvent(session, event) {
|
|
84
86
|
if (event.type === "speech_start") {
|
|
@@ -95,6 +97,7 @@ function applyEvent(session, event) {
|
|
|
95
97
|
}
|
|
96
98
|
if (event.type === "partial" || event.type === "final") {
|
|
97
99
|
if (!session.started) openTurn(session, session.pendingStart ?? session.lastAt);
|
|
100
|
+
if (event.type === "final") session.sawFinal = true;
|
|
98
101
|
session.segments.set(event.segmentId, event.text);
|
|
99
102
|
session.stream.push(event);
|
|
100
103
|
return;
|
|
@@ -106,8 +109,21 @@ function applyEvent(session, event) {
|
|
|
106
109
|
}
|
|
107
110
|
if (event.type === "done") session.pendingDone = event;
|
|
108
111
|
}
|
|
112
|
+
function turnReady(session) {
|
|
113
|
+
if (session.sawFinal) return true;
|
|
114
|
+
if (session.providerTurn !== void 0) return true;
|
|
115
|
+
if (session.pendingDone) return true;
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
function holdForNextSpeech(session, events, update) {
|
|
119
|
+
if (session.heldEnd === void 0 || !session.turnOpen) return;
|
|
120
|
+
const started = update?.speechStart !== void 0 || events.some((event) => event.type === "speech_start");
|
|
121
|
+
if (!started) return;
|
|
122
|
+
closeTurn(session, session.heldEnd);
|
|
123
|
+
}
|
|
109
124
|
function absorb(session, events, update) {
|
|
110
125
|
if (session.stopped) return;
|
|
126
|
+
holdForNextSpeech(session, events, update);
|
|
111
127
|
if (update?.speechStart !== void 0) session.pendingStart = update.speechStart;
|
|
112
128
|
if (update?.turnEnd !== void 0) session.pendingEnd = update.turnEnd;
|
|
113
129
|
const textual = events.some(
|
|
@@ -124,7 +140,21 @@ function absorb(session, events, update) {
|
|
|
124
140
|
session.heldEnd = session.pendingEnd;
|
|
125
141
|
session.pendingEnd = void 0;
|
|
126
142
|
}
|
|
127
|
-
if (session.heldEnd !== void 0) closeTurn(session, session.heldEnd);
|
|
143
|
+
if (session.heldEnd !== void 0 && turnReady(session)) closeTurn(session, session.heldEnd);
|
|
144
|
+
}
|
|
145
|
+
async function finishWhenClosed(session, options, stream, closed, take) {
|
|
146
|
+
while (!closed() && !session.stopped) {
|
|
147
|
+
if (options?.signal?.aborted) {
|
|
148
|
+
stream.push({ type: "error", reason: "aborted", error: "aborted" });
|
|
149
|
+
session.stopped = true;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
await tick();
|
|
153
|
+
take();
|
|
154
|
+
}
|
|
155
|
+
if (session.stopped) return;
|
|
156
|
+
take();
|
|
157
|
+
finish(session);
|
|
128
158
|
}
|
|
129
159
|
function finish(session) {
|
|
130
160
|
if (session.stopped) return;
|
|
@@ -161,8 +191,14 @@ async function pump(listenModel, model, audio, options, stream, vad) {
|
|
|
161
191
|
const inbox = [];
|
|
162
192
|
let readerClosed = false;
|
|
163
193
|
const reading = (async () => {
|
|
164
|
-
|
|
165
|
-
|
|
194
|
+
try {
|
|
195
|
+
for await (const event of inner) inbox.push(event);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
198
|
+
inbox.push({ type: "error", reason: "error", error: message });
|
|
199
|
+
} finally {
|
|
200
|
+
readerClosed = true;
|
|
201
|
+
}
|
|
166
202
|
})();
|
|
167
203
|
const session = {
|
|
168
204
|
stream,
|
|
@@ -170,6 +206,7 @@ async function pump(listenModel, model, audio, options, stream, vad) {
|
|
|
170
206
|
closed: "",
|
|
171
207
|
turnOpen: false,
|
|
172
208
|
started: false,
|
|
209
|
+
sawFinal: false,
|
|
173
210
|
stopped: false,
|
|
174
211
|
lastAt: 0
|
|
175
212
|
};
|
|
@@ -199,15 +236,9 @@ async function pump(listenModel, model, audio, options, stream, vad) {
|
|
|
199
236
|
take(update);
|
|
200
237
|
}
|
|
201
238
|
gate.push(null);
|
|
202
|
-
if (!session.stopped)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
await tick();
|
|
206
|
-
take();
|
|
207
|
-
attempt += 1;
|
|
208
|
-
} while (!readerClosed && attempt < 50);
|
|
209
|
-
finish(session);
|
|
210
|
-
} else await tick();
|
|
239
|
+
if (!session.stopped)
|
|
240
|
+
await finishWhenClosed(session, options, stream, () => readerClosed, take);
|
|
241
|
+
else await tick();
|
|
211
242
|
} catch (error) {
|
|
212
243
|
gate.push(null);
|
|
213
244
|
if (!session.stopped) {
|
package/dist/vad-listen.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/vad-listen.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AudioChunk, AudioSource } from './audio.ts';\nimport type { TranscriptionModel } from './models.ts';\nimport {\n type ListenOptions,\n type TranscriptionEvent,\n TranscriptionEventStream,\n} from './transcription.ts';\nimport { energyVad, type VadHook, type VadUpdate } from './vad.ts';\n\ntype ListenModel = (\n model: TranscriptionModel<string>,\n audio: AudioSource,\n options?: ListenOptions\n) => TranscriptionEventStream;\n\ninterface Gate {\n source: AudioSource;\n push(chunk: AudioChunk | null): void;\n}\n\nfunction tick(): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n}\n\nfunction audioGate(): Gate {\n const queued: { chunk: AudioChunk | null }[] = [];\n let resolveWait: ((chunk: AudioChunk | null) => void) | undefined;\n const source: AudioSource = {\n async *[Symbol.asyncIterator]() {\n while (true) {\n const ready = queued.shift();\n if (ready !== undefined) {\n if (ready.chunk === null) return;\n yield ready.chunk;\n continue;\n }\n const chunk = await new Promise<AudioChunk | null>((resolve) => {\n resolveWait = resolve;\n });\n if (chunk === null) return;\n yield chunk;\n }\n },\n };\n return {\n source,\n push(chunk) {\n if (resolveWait) {\n const resolve = resolveWait;\n resolveWait = undefined;\n resolve(chunk);\n return;\n }\n queued.push({ chunk });\n },\n };\n}\n\nfunction joinSegments(parts: string[]): string {\n let text = '';\n for (const part of parts) {\n if (text.length === 0 || /\\s$/.test(text) || /^\\s/.test(part)) text += part;\n else text += ` ${part}`;\n }\n return text;\n}\n\ninterface Session {\n stream: TranscriptionEventStream;\n segments: Map<string, string>;\n closed: string;\n turnOpen: boolean;\n started: boolean;\n pendingStart?: number;\n pendingEnd?: number;\n heldEnd?: number;\n providerTurn?: string;\n pendingDone?: Extract<TranscriptionEvent, { type: 'done' }>;\n stopped: boolean;\n lastAt: number;\n}\n\nfunction liveText(session: Session): string {\n const parts = [...session.segments.values()];\n if (parts.length > 0) return joinSegments(parts);\n return session.pendingDone?.transcript.text ?? session.providerTurn ?? '';\n}\n\nfunction turnText(session: Session): string {\n const live = liveText(session);\n if (session.closed.length > 0 && live.startsWith(session.closed)) {\n return live.slice(session.closed.length).trim();\n }\n if (session.providerTurn && !live.startsWith(session.providerTurn)) return session.providerTurn;\n return live.trim();\n}\n\nfunction closeTurn(session: Session, at: number) {\n if (!session.turnOpen || session.stopped) return;\n const text = turnText(session);\n if (text.length === 0) return;\n session.closed = liveText(session);\n session.turnOpen = false;\n session.started = false;\n session.pendingEnd = undefined;\n session.heldEnd = undefined;\n session.providerTurn = undefined;\n session.stream.push({ type: 'turn_end', text, at });\n}\n\nfunction openTurn(session: Session, at: number) {\n if (session.started || session.stopped) return;\n session.stream.push({ type: 'speech_start', at });\n session.started = true;\n session.turnOpen = true;\n session.pendingStart = undefined;\n}\n\nfunction applyEvent(session: Session, event: TranscriptionEvent) {\n if (event.type === 'speech_start') {\n if (!session.started) session.stream.push(event);\n session.started = true;\n session.turnOpen = true;\n session.pendingStart = undefined;\n return;\n }\n if (event.type === 'turn_end') {\n session.providerTurn = event.text;\n session.pendingEnd = event.at;\n return;\n }\n if (event.type === 'partial' || event.type === 'final') {\n if (!session.started) openTurn(session, session.pendingStart ?? session.lastAt);\n session.segments.set(event.segmentId, event.text);\n session.stream.push(event);\n return;\n }\n if (event.type === 'error') {\n session.stream.push(event);\n session.stopped = true;\n return;\n }\n if (event.type === 'done') session.pendingDone = event;\n}\n\nfunction absorb(session: Session, events: TranscriptionEvent[], update: VadUpdate | undefined) {\n if (session.stopped) return;\n if (update?.speechStart !== undefined) session.pendingStart = update.speechStart;\n if (update?.turnEnd !== undefined) session.pendingEnd = update.turnEnd;\n const textual = events.some(\n (event) => event.type === 'partial' || event.type === 'final' || event.type === 'done'\n );\n if (session.pendingStart !== undefined && (textual || events.length === 0)) {\n openTurn(session, session.pendingStart);\n }\n for (const event of events) {\n applyEvent(session, event);\n if (session.stopped) return;\n }\n if (session.pendingEnd !== undefined) {\n session.heldEnd = session.pendingEnd;\n session.pendingEnd = undefined;\n }\n if (session.heldEnd !== undefined) closeTurn(session, session.heldEnd);\n}\n\nfunction finish(session: Session) {\n if (session.stopped) return;\n if (session.pendingStart !== undefined) openTurn(session, session.pendingStart);\n if (session.turnOpen) closeTurn(session, session.heldEnd ?? session.pendingEnd ?? session.lastAt);\n if (session.pendingDone) session.stream.push(session.pendingDone);\n else {\n session.stream.push({\n type: 'error',\n reason: 'error',\n error: 'Transcription ended without a transcript',\n });\n }\n}\n\nexport function listenWithVad(\n listenModel: ListenModel,\n model: TranscriptionModel<string>,\n audio: AudioSource,\n options?: ListenOptions,\n vad: VadHook = options?.vad ?? energyVad()\n): TranscriptionEventStream {\n const stream = new TranscriptionEventStream();\n void pump(listenModel, model, audio, options, stream, vad);\n return stream;\n}\n\nasync function pump(\n listenModel: ListenModel,\n model: TranscriptionModel<string>,\n audio: AudioSource,\n options: ListenOptions | undefined,\n stream: TranscriptionEventStream,\n vad: VadHook\n) {\n if (options?.signal?.aborted) {\n stream.push({ type: 'error', reason: 'aborted', error: 'aborted' });\n return;\n }\n const gate = audioGate();\n let inner: TranscriptionEventStream;\n try {\n inner = listenModel(model, gate.source, options);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n stream.push({ type: 'error', reason: 'error', error: message });\n return;\n }\n const inbox: TranscriptionEvent[] = [];\n let readerClosed = false;\n const reading = (async () => {\n for await (const event of inner) inbox.push(event);\n readerClosed = true;\n })();\n const session: Session = {\n stream,\n segments: new Map(),\n closed: '',\n turnOpen: false,\n started: false,\n stopped: false,\n lastAt: 0,\n };\n const take = (update?: VadUpdate) => {\n const events = inbox.splice(0, inbox.length);\n absorb(session, events, update);\n };\n try {\n await tick();\n take();\n for await (const chunk of audio) {\n if (session.stopped) break;\n if (options?.signal?.aborted) {\n gate.push(null);\n await tick();\n take();\n if (!session.stopped) {\n stream.push({ type: 'error', reason: 'aborted', error: 'aborted' });\n session.stopped = true;\n }\n break;\n }\n session.lastAt = chunk.timestamp;\n const update = vad.push(chunk);\n gate.push(chunk);\n await tick();\n take(update);\n }\n gate.push(null);\n if (!session.stopped)
|
|
5
|
-
"mappings": "AAEA;AAAA,EAGE;AAAA,OACK;AACP,SAAS,iBAA+C;AAaxD,SAAS,OAAsB;AAC7B,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,eAAW,SAAS,CAAC;AAAA,EACvB,CAAC;AACH;AAEA,SAAS,YAAkB;AACzB,QAAM,SAAyC,CAAC;AAChD,MAAI;AACJ,QAAM,SAAsB;AAAA,IAC1B,QAAQ,OAAO,aAAa,IAAI;AAC9B,aAAO,MAAM;AACX,cAAM,QAAQ,OAAO,MAAM;AAC3B,YAAI,UAAU,QAAW;AACvB,cAAI,MAAM,UAAU,KAAM;AAC1B,gBAAM,MAAM;AACZ;AAAA,QACF;AACA,cAAM,QAAQ,MAAM,IAAI,QAA2B,CAAC,YAAY;AAC9D,wBAAc;AAAA,QAChB,CAAC;AACD,YAAI,UAAU,KAAM;AACpB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA,KAAK,OAAO;AACV,UAAI,aAAa;AACf,cAAM,UAAU;AAChB,sBAAc;AACd,gBAAQ,KAAK;AACb;AAAA,MACF;AACA,aAAO,KAAK,EAAE,MAAM,CAAC;AAAA,IACvB;AAAA,EACF;AACF;AAEA,SAAS,aAAa,OAAyB;AAC7C,MAAI,OAAO;AACX,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,WAAW,KAAK,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,EAAG,SAAQ;AAAA,QAClE,SAAQ,IAAI,IAAI;AAAA,EACvB;AACA,SAAO;AACT;
|
|
4
|
+
"sourcesContent": ["import type { AudioChunk, AudioSource } from './audio.ts';\nimport type { TranscriptionModel } from './models.ts';\nimport {\n type ListenOptions,\n type TranscriptionEvent,\n TranscriptionEventStream,\n} from './transcription.ts';\nimport { energyVad, type VadHook, type VadUpdate } from './vad.ts';\n\ntype ListenModel = (\n model: TranscriptionModel<string>,\n audio: AudioSource,\n options?: ListenOptions\n) => TranscriptionEventStream;\n\ninterface Gate {\n source: AudioSource;\n push(chunk: AudioChunk | null): void;\n}\n\nfunction tick(): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, 0);\n });\n}\n\nfunction audioGate(): Gate {\n const queued: { chunk: AudioChunk | null }[] = [];\n let resolveWait: ((chunk: AudioChunk | null) => void) | undefined;\n const source: AudioSource = {\n async *[Symbol.asyncIterator]() {\n while (true) {\n const ready = queued.shift();\n if (ready !== undefined) {\n if (ready.chunk === null) return;\n yield ready.chunk;\n continue;\n }\n const chunk = await new Promise<AudioChunk | null>((resolve) => {\n resolveWait = resolve;\n });\n if (chunk === null) return;\n yield chunk;\n }\n },\n };\n return {\n source,\n push(chunk) {\n if (resolveWait) {\n const resolve = resolveWait;\n resolveWait = undefined;\n resolve(chunk);\n return;\n }\n queued.push({ chunk });\n },\n };\n}\n\nfunction joinSegments(parts: string[]): string {\n let text = '';\n for (const part of parts) {\n if (text.length === 0 || /\\s$/.test(text) || /^\\s/.test(part)) text += part;\n else text += ` ${part}`;\n }\n return text;\n}\n\ninterface Session {\n stream: TranscriptionEventStream;\n segments: Map<string, string>;\n closed: string;\n turnOpen: boolean;\n started: boolean;\n pendingStart?: number;\n pendingEnd?: number;\n heldEnd?: number;\n providerTurn?: string;\n pendingDone?: Extract<TranscriptionEvent, { type: 'done' }>;\n sawFinal: boolean;\n stopped: boolean;\n lastAt: number;\n}\n\nfunction liveText(session: Session): string {\n const parts = [...session.segments.values()];\n if (parts.length > 0) return joinSegments(parts);\n return session.pendingDone?.transcript.text ?? session.providerTurn ?? '';\n}\n\nfunction turnText(session: Session): string {\n const live = liveText(session);\n if (session.closed.length > 0 && live.startsWith(session.closed)) {\n return live.slice(session.closed.length).trim();\n }\n if (session.providerTurn && !live.startsWith(session.providerTurn)) return session.providerTurn;\n return live.trim();\n}\n\nfunction closeTurn(session: Session, at: number) {\n if (!session.turnOpen || session.stopped) return;\n const text = turnText(session);\n if (text.length === 0) return;\n session.closed = liveText(session);\n session.turnOpen = false;\n session.started = false;\n session.pendingEnd = undefined;\n session.heldEnd = undefined;\n session.providerTurn = undefined;\n session.sawFinal = false;\n session.stream.push({ type: 'turn_end', text, at });\n}\n\nfunction openTurn(session: Session, at: number) {\n if (session.started || session.stopped) return;\n session.stream.push({ type: 'speech_start', at });\n session.started = true;\n session.turnOpen = true;\n session.pendingStart = undefined;\n session.sawFinal = false;\n}\n\nfunction applyEvent(session: Session, event: TranscriptionEvent) {\n if (event.type === 'speech_start') {\n if (!session.started) session.stream.push(event);\n session.started = true;\n session.turnOpen = true;\n session.pendingStart = undefined;\n return;\n }\n if (event.type === 'turn_end') {\n session.providerTurn = event.text;\n session.pendingEnd = event.at;\n return;\n }\n if (event.type === 'partial' || event.type === 'final') {\n if (!session.started) openTurn(session, session.pendingStart ?? session.lastAt);\n if (event.type === 'final') session.sawFinal = true;\n session.segments.set(event.segmentId, event.text);\n session.stream.push(event);\n return;\n }\n if (event.type === 'error') {\n session.stream.push(event);\n session.stopped = true;\n return;\n }\n if (event.type === 'done') session.pendingDone = event;\n}\n\nfunction turnReady(session: Session): boolean {\n if (session.sawFinal) return true;\n if (session.providerTurn !== undefined) return true;\n if (session.pendingDone) return true;\n return false;\n}\n\nfunction holdForNextSpeech(\n session: Session,\n events: TranscriptionEvent[],\n update: VadUpdate | undefined\n) {\n if (session.heldEnd === undefined || !session.turnOpen) return;\n const started =\n update?.speechStart !== undefined || events.some((event) => event.type === 'speech_start');\n if (!started) return;\n closeTurn(session, session.heldEnd);\n}\n\nfunction absorb(session: Session, events: TranscriptionEvent[], update: VadUpdate | undefined) {\n if (session.stopped) return;\n holdForNextSpeech(session, events, update);\n if (update?.speechStart !== undefined) session.pendingStart = update.speechStart;\n if (update?.turnEnd !== undefined) session.pendingEnd = update.turnEnd;\n const textual = events.some(\n (event) => event.type === 'partial' || event.type === 'final' || event.type === 'done'\n );\n if (session.pendingStart !== undefined && (textual || events.length === 0)) {\n openTurn(session, session.pendingStart);\n }\n for (const event of events) {\n applyEvent(session, event);\n if (session.stopped) return;\n }\n if (session.pendingEnd !== undefined) {\n session.heldEnd = session.pendingEnd;\n session.pendingEnd = undefined;\n }\n if (session.heldEnd !== undefined && turnReady(session)) closeTurn(session, session.heldEnd);\n}\n\nasync function finishWhenClosed(\n session: Session,\n options: ListenOptions | undefined,\n stream: TranscriptionEventStream,\n closed: () => boolean,\n take: () => void\n) {\n while (!closed() && !session.stopped) {\n if (options?.signal?.aborted) {\n stream.push({ type: 'error', reason: 'aborted', error: 'aborted' });\n session.stopped = true;\n return;\n }\n await tick();\n take();\n }\n if (session.stopped) return;\n take();\n finish(session);\n}\n\nfunction finish(session: Session) {\n if (session.stopped) return;\n if (session.pendingStart !== undefined) openTurn(session, session.pendingStart);\n if (session.turnOpen) closeTurn(session, session.heldEnd ?? session.pendingEnd ?? session.lastAt);\n if (session.pendingDone) session.stream.push(session.pendingDone);\n else {\n session.stream.push({\n type: 'error',\n reason: 'error',\n error: 'Transcription ended without a transcript',\n });\n }\n}\n\nexport function listenWithVad(\n listenModel: ListenModel,\n model: TranscriptionModel<string>,\n audio: AudioSource,\n options?: ListenOptions,\n vad: VadHook = options?.vad ?? energyVad()\n): TranscriptionEventStream {\n const stream = new TranscriptionEventStream();\n void pump(listenModel, model, audio, options, stream, vad);\n return stream;\n}\n\nasync function pump(\n listenModel: ListenModel,\n model: TranscriptionModel<string>,\n audio: AudioSource,\n options: ListenOptions | undefined,\n stream: TranscriptionEventStream,\n vad: VadHook\n) {\n if (options?.signal?.aborted) {\n stream.push({ type: 'error', reason: 'aborted', error: 'aborted' });\n return;\n }\n const gate = audioGate();\n let inner: TranscriptionEventStream;\n try {\n inner = listenModel(model, gate.source, options);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n stream.push({ type: 'error', reason: 'error', error: message });\n return;\n }\n const inbox: TranscriptionEvent[] = [];\n let readerClosed = false;\n const reading = (async () => {\n try {\n for await (const event of inner) inbox.push(event);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n inbox.push({ type: 'error', reason: 'error', error: message });\n } finally {\n readerClosed = true;\n }\n })();\n const session: Session = {\n stream,\n segments: new Map(),\n closed: '',\n turnOpen: false,\n started: false,\n sawFinal: false,\n stopped: false,\n lastAt: 0,\n };\n const take = (update?: VadUpdate) => {\n const events = inbox.splice(0, inbox.length);\n absorb(session, events, update);\n };\n try {\n await tick();\n take();\n for await (const chunk of audio) {\n if (session.stopped) break;\n if (options?.signal?.aborted) {\n gate.push(null);\n await tick();\n take();\n if (!session.stopped) {\n stream.push({ type: 'error', reason: 'aborted', error: 'aborted' });\n session.stopped = true;\n }\n break;\n }\n session.lastAt = chunk.timestamp;\n const update = vad.push(chunk);\n gate.push(chunk);\n await tick();\n take(update);\n }\n gate.push(null);\n if (!session.stopped)\n await finishWhenClosed(session, options, stream, () => readerClosed, take);\n else await tick();\n } catch (error) {\n gate.push(null);\n if (!session.stopped) {\n const message = error instanceof Error ? error.message : String(error);\n stream.push({ type: 'error', reason: 'error', error: message });\n }\n }\n await reading;\n}\n"],
|
|
5
|
+
"mappings": "AAEA;AAAA,EAGE;AAAA,OACK;AACP,SAAS,iBAA+C;AAaxD,SAAS,OAAsB;AAC7B,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,eAAW,SAAS,CAAC;AAAA,EACvB,CAAC;AACH;AAEA,SAAS,YAAkB;AACzB,QAAM,SAAyC,CAAC;AAChD,MAAI;AACJ,QAAM,SAAsB;AAAA,IAC1B,QAAQ,OAAO,aAAa,IAAI;AAC9B,aAAO,MAAM;AACX,cAAM,QAAQ,OAAO,MAAM;AAC3B,YAAI,UAAU,QAAW;AACvB,cAAI,MAAM,UAAU,KAAM;AAC1B,gBAAM,MAAM;AACZ;AAAA,QACF;AACA,cAAM,QAAQ,MAAM,IAAI,QAA2B,CAAC,YAAY;AAC9D,wBAAc;AAAA,QAChB,CAAC;AACD,YAAI,UAAU,KAAM;AACpB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA,KAAK,OAAO;AACV,UAAI,aAAa;AACf,cAAM,UAAU;AAChB,sBAAc;AACd,gBAAQ,KAAK;AACb;AAAA,MACF;AACA,aAAO,KAAK,EAAE,MAAM,CAAC;AAAA,IACvB;AAAA,EACF;AACF;AAEA,SAAS,aAAa,OAAyB;AAC7C,MAAI,OAAO;AACX,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,WAAW,KAAK,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,EAAG,SAAQ;AAAA,QAClE,SAAQ,IAAI,IAAI;AAAA,EACvB;AACA,SAAO;AACT;AAkBA,SAAS,SAAS,SAA0B;AAC1C,QAAM,QAAQ,CAAC,GAAG,QAAQ,SAAS,OAAO,CAAC;AAC3C,MAAI,MAAM,SAAS,EAAG,QAAO,aAAa,KAAK;AAC/C,SAAO,QAAQ,aAAa,WAAW,QAAQ,QAAQ,gBAAgB;AACzE;AAEA,SAAS,SAAS,SAA0B;AAC1C,QAAM,OAAO,SAAS,OAAO;AAC7B,MAAI,QAAQ,OAAO,SAAS,KAAK,KAAK,WAAW,QAAQ,MAAM,GAAG;AAChE,WAAO,KAAK,MAAM,QAAQ,OAAO,MAAM,EAAE,KAAK;AAAA,EAChD;AACA,MAAI,QAAQ,gBAAgB,CAAC,KAAK,WAAW,QAAQ,YAAY,EAAG,QAAO,QAAQ;AACnF,SAAO,KAAK,KAAK;AACnB;AAEA,SAAS,UAAU,SAAkB,IAAY;AAC/C,MAAI,CAAC,QAAQ,YAAY,QAAQ,QAAS;AAC1C,QAAM,OAAO,SAAS,OAAO;AAC7B,MAAI,KAAK,WAAW,EAAG;AACvB,UAAQ,SAAS,SAAS,OAAO;AACjC,UAAQ,WAAW;AACnB,UAAQ,UAAU;AAClB,UAAQ,aAAa;AACrB,UAAQ,UAAU;AAClB,UAAQ,eAAe;AACvB,UAAQ,WAAW;AACnB,UAAQ,OAAO,KAAK,EAAE,MAAM,YAAY,MAAM,GAAG,CAAC;AACpD;AAEA,SAAS,SAAS,SAAkB,IAAY;AAC9C,MAAI,QAAQ,WAAW,QAAQ,QAAS;AACxC,UAAQ,OAAO,KAAK,EAAE,MAAM,gBAAgB,GAAG,CAAC;AAChD,UAAQ,UAAU;AAClB,UAAQ,WAAW;AACnB,UAAQ,eAAe;AACvB,UAAQ,WAAW;AACrB;AAEA,SAAS,WAAW,SAAkB,OAA2B;AAC/D,MAAI,MAAM,SAAS,gBAAgB;AACjC,QAAI,CAAC,QAAQ,QAAS,SAAQ,OAAO,KAAK,KAAK;AAC/C,YAAQ,UAAU;AAClB,YAAQ,WAAW;AACnB,YAAQ,eAAe;AACvB;AAAA,EACF;AACA,MAAI,MAAM,SAAS,YAAY;AAC7B,YAAQ,eAAe,MAAM;AAC7B,YAAQ,aAAa,MAAM;AAC3B;AAAA,EACF;AACA,MAAI,MAAM,SAAS,aAAa,MAAM,SAAS,SAAS;AACtD,QAAI,CAAC,QAAQ,QAAS,UAAS,SAAS,QAAQ,gBAAgB,QAAQ,MAAM;AAC9E,QAAI,MAAM,SAAS,QAAS,SAAQ,WAAW;AAC/C,YAAQ,SAAS,IAAI,MAAM,WAAW,MAAM,IAAI;AAChD,YAAQ,OAAO,KAAK,KAAK;AACzB;AAAA,EACF;AACA,MAAI,MAAM,SAAS,SAAS;AAC1B,YAAQ,OAAO,KAAK,KAAK;AACzB,YAAQ,UAAU;AAClB;AAAA,EACF;AACA,MAAI,MAAM,SAAS,OAAQ,SAAQ,cAAc;AACnD;AAEA,SAAS,UAAU,SAA2B;AAC5C,MAAI,QAAQ,SAAU,QAAO;AAC7B,MAAI,QAAQ,iBAAiB,OAAW,QAAO;AAC/C,MAAI,QAAQ,YAAa,QAAO;AAChC,SAAO;AACT;AAEA,SAAS,kBACP,SACA,QACA,QACA;AACA,MAAI,QAAQ,YAAY,UAAa,CAAC,QAAQ,SAAU;AACxD,QAAM,UACJ,QAAQ,gBAAgB,UAAa,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,cAAc;AAC3F,MAAI,CAAC,QAAS;AACd,YAAU,SAAS,QAAQ,OAAO;AACpC;AAEA,SAAS,OAAO,SAAkB,QAA8B,QAA+B;AAC7F,MAAI,QAAQ,QAAS;AACrB,oBAAkB,SAAS,QAAQ,MAAM;AACzC,MAAI,QAAQ,gBAAgB,OAAW,SAAQ,eAAe,OAAO;AACrE,MAAI,QAAQ,YAAY,OAAW,SAAQ,aAAa,OAAO;AAC/D,QAAM,UAAU,OAAO;AAAA,IACrB,CAAC,UAAU,MAAM,SAAS,aAAa,MAAM,SAAS,WAAW,MAAM,SAAS;AAAA,EAClF;AACA,MAAI,QAAQ,iBAAiB,WAAc,WAAW,OAAO,WAAW,IAAI;AAC1E,aAAS,SAAS,QAAQ,YAAY;AAAA,EACxC;AACA,aAAW,SAAS,QAAQ;AAC1B,eAAW,SAAS,KAAK;AACzB,QAAI,QAAQ,QAAS;AAAA,EACvB;AACA,MAAI,QAAQ,eAAe,QAAW;AACpC,YAAQ,UAAU,QAAQ;AAC1B,YAAQ,aAAa;AAAA,EACvB;AACA,MAAI,QAAQ,YAAY,UAAa,UAAU,OAAO,EAAG,WAAU,SAAS,QAAQ,OAAO;AAC7F;AAEA,eAAe,iBACb,SACA,SACA,QACA,QACA,MACA;AACA,SAAO,CAAC,OAAO,KAAK,CAAC,QAAQ,SAAS;AACpC,QAAI,SAAS,QAAQ,SAAS;AAC5B,aAAO,KAAK,EAAE,MAAM,SAAS,QAAQ,WAAW,OAAO,UAAU,CAAC;AAClE,cAAQ,UAAU;AAClB;AAAA,IACF;AACA,UAAM,KAAK;AACX,SAAK;AAAA,EACP;AACA,MAAI,QAAQ,QAAS;AACrB,OAAK;AACL,SAAO,OAAO;AAChB;AAEA,SAAS,OAAO,SAAkB;AAChC,MAAI,QAAQ,QAAS;AACrB,MAAI,QAAQ,iBAAiB,OAAW,UAAS,SAAS,QAAQ,YAAY;AAC9E,MAAI,QAAQ,SAAU,WAAU,SAAS,QAAQ,WAAW,QAAQ,cAAc,QAAQ,MAAM;AAChG,MAAI,QAAQ,YAAa,SAAQ,OAAO,KAAK,QAAQ,WAAW;AAAA,OAC3D;AACH,YAAQ,OAAO,KAAK;AAAA,MAClB,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACF;AAEO,SAAS,cACd,aACA,OACA,OACA,SACA,MAAe,SAAS,OAAO,UAAU,GACf;AAC1B,QAAM,SAAS,IAAI,yBAAyB;AAC5C,OAAK,KAAK,aAAa,OAAO,OAAO,SAAS,QAAQ,GAAG;AACzD,SAAO;AACT;AAEA,eAAe,KACb,aACA,OACA,OACA,SACA,QACA,KACA;AACA,MAAI,SAAS,QAAQ,SAAS;AAC5B,WAAO,KAAK,EAAE,MAAM,SAAS,QAAQ,WAAW,OAAO,UAAU,CAAC;AAClE;AAAA,EACF;AACA,QAAM,OAAO,UAAU;AACvB,MAAI;AACJ,MAAI;AACF,YAAQ,YAAY,OAAO,KAAK,QAAQ,OAAO;AAAA,EACjD,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,WAAO,KAAK,EAAE,MAAM,SAAS,QAAQ,SAAS,OAAO,QAAQ,CAAC;AAC9D;AAAA,EACF;AACA,QAAM,QAA8B,CAAC;AACrC,MAAI,eAAe;AACnB,QAAM,WAAW,YAAY;AAC3B,QAAI;AACF,uBAAiB,SAAS,MAAO,OAAM,KAAK,KAAK;AAAA,IACnD,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAM,KAAK,EAAE,MAAM,SAAS,QAAQ,SAAS,OAAO,QAAQ,CAAC;AAAA,IAC/D,UAAE;AACA,qBAAe;AAAA,IACjB;AAAA,EACF,GAAG;AACH,QAAM,UAAmB;AAAA,IACvB;AAAA,IACA,UAAU,oBAAI,IAAI;AAAA,IAClB,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,UAAU;AAAA,IACV,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACA,QAAM,OAAO,CAAC,WAAuB;AACnC,UAAM,SAAS,MAAM,OAAO,GAAG,MAAM,MAAM;AAC3C,WAAO,SAAS,QAAQ,MAAM;AAAA,EAChC;AACA,MAAI;AACF,UAAM,KAAK;AACX,SAAK;AACL,qBAAiB,SAAS,OAAO;AAC/B,UAAI,QAAQ,QAAS;AACrB,UAAI,SAAS,QAAQ,SAAS;AAC5B,aAAK,KAAK,IAAI;AACd,cAAM,KAAK;AACX,aAAK;AACL,YAAI,CAAC,QAAQ,SAAS;AACpB,iBAAO,KAAK,EAAE,MAAM,SAAS,QAAQ,WAAW,OAAO,UAAU,CAAC;AAClE,kBAAQ,UAAU;AAAA,QACpB;AACA;AAAA,MACF;AACA,cAAQ,SAAS,MAAM;AACvB,YAAM,SAAS,IAAI,KAAK,KAAK;AAC7B,WAAK,KAAK,KAAK;AACf,YAAM,KAAK;AACX,WAAK,MAAM;AAAA,IACb;AACA,SAAK,KAAK,IAAI;AACd,QAAI,CAAC,QAAQ;AACX,YAAM,iBAAiB,SAAS,SAAS,QAAQ,MAAM,cAAc,IAAI;AAAA,QACtE,OAAM,KAAK;AAAA,EAClB,SAAS,OAAO;AACd,SAAK,KAAK,IAAI;AACd,QAAI,CAAC,QAAQ,SAAS;AACpB,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,aAAO,KAAK,EAAE,MAAM,SAAS,QAAQ,SAAS,OAAO,QAAQ,CAAC;AAAA,IAChE;AAAA,EACF;AACA,QAAM;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/vad.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { convertAudio } from "./convert.js";
|
|
1
2
|
function width(encoding) {
|
|
2
3
|
if (encoding === "pcm16") return 2;
|
|
3
4
|
if (encoding === "pcmf32") return 4;
|
|
@@ -22,6 +23,15 @@ function rms(chunk) {
|
|
|
22
23
|
}
|
|
23
24
|
return Math.sqrt(sum / samples);
|
|
24
25
|
}
|
|
26
|
+
const compressed = /* @__PURE__ */ new Set(["opus", "mp3", "flac"]);
|
|
27
|
+
function pcmEnergy(chunk) {
|
|
28
|
+
const encoding = chunk.format.encoding;
|
|
29
|
+
if (encoding === "pcm16" || encoding === "pcmf32") return chunk;
|
|
30
|
+
if (compressed.has(encoding)) {
|
|
31
|
+
throw new Error(`energyVad requires PCM audio, received ${encoding}`);
|
|
32
|
+
}
|
|
33
|
+
return convertAudio(chunk, { ...chunk.format, encoding: "pcm16" });
|
|
34
|
+
}
|
|
25
35
|
function energyVad(options = {}) {
|
|
26
36
|
const threshold = options.threshold ?? 0.02;
|
|
27
37
|
const silenceMs = options.silenceMs ?? 500;
|
|
@@ -29,13 +39,11 @@ function energyVad(options = {}) {
|
|
|
29
39
|
let silence = 0;
|
|
30
40
|
return {
|
|
31
41
|
push(chunk) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
const duration = durationMs(chunk);
|
|
42
|
+
const sample = pcmEnergy(chunk);
|
|
43
|
+
const measured = rms(sample);
|
|
44
|
+
let level = 0;
|
|
45
|
+
if (measured !== void 0) level = measured;
|
|
46
|
+
const duration = durationMs(sample);
|
|
39
47
|
if (level >= threshold) {
|
|
40
48
|
silence = 0;
|
|
41
49
|
if (speaking) return void 0;
|
package/dist/vad.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/vad.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AudioChunk } from './audio.ts';\n\nexport interface VadUpdate {\n speechStart?: number;\n turnEnd?: number;\n}\n\nexport interface VadHook {\n push(chunk: AudioChunk): VadUpdate | undefined;\n}\n\nexport interface EnergyVadOptions {\n threshold?: number;\n silenceMs?: number;\n}\n\nfunction width(encoding: AudioChunk['format']['encoding']): number | undefined {\n if (encoding === 'pcm16') return 2;\n if (encoding === 'pcmf32') return 4;\n return undefined;\n}\n\nfunction durationMs(chunk: AudioChunk): number {\n const bytes = width(chunk.format.encoding);\n if (bytes === undefined || chunk.format.sampleRate <= 0 || chunk.format.channels <= 0) return 0;\n const samples = chunk.data.byteLength / bytes / chunk.format.channels;\n return (samples / chunk.format.sampleRate) * 1000;\n}\n\nfunction rms(chunk: AudioChunk): number | undefined {\n const bytes = width(chunk.format.encoding);\n if (bytes === undefined) return undefined;\n const samples = Math.floor(chunk.data.byteLength / bytes);\n if (samples === 0) return 0;\n const view = new DataView(chunk.data.buffer, chunk.data.byteOffset, chunk.data.byteLength);\n let sum = 0;\n for (let index = 0; index < samples; index += 1) {\n const sample =\n bytes === 2 ? view.getInt16(index * 2, true) / 32768 : view.getFloat32(index * 4, true);\n sum += sample * sample;\n }\n return Math.sqrt(sum / samples);\n}\n\nexport function energyVad(options: EnergyVadOptions = {}): VadHook {\n const threshold = options.threshold ?? 0.02;\n const silenceMs = options.silenceMs ?? 500;\n let speaking = false;\n let silence = 0;\n return {\n push(chunk) {\n const
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { AudioChunk, AudioEncoding } from './audio.ts';\nimport { convertAudio } from './convert.ts';\n\nexport interface VadUpdate {\n speechStart?: number;\n turnEnd?: number;\n}\n\nexport interface VadHook {\n push(chunk: AudioChunk): VadUpdate | undefined;\n}\n\nexport interface EnergyVadOptions {\n threshold?: number;\n silenceMs?: number;\n}\n\nfunction width(encoding: AudioChunk['format']['encoding']): number | undefined {\n if (encoding === 'pcm16') return 2;\n if (encoding === 'pcmf32') return 4;\n return undefined;\n}\n\nfunction durationMs(chunk: AudioChunk): number {\n const bytes = width(chunk.format.encoding);\n if (bytes === undefined || chunk.format.sampleRate <= 0 || chunk.format.channels <= 0) return 0;\n const samples = chunk.data.byteLength / bytes / chunk.format.channels;\n return (samples / chunk.format.sampleRate) * 1000;\n}\n\nfunction rms(chunk: AudioChunk): number | undefined {\n const bytes = width(chunk.format.encoding);\n if (bytes === undefined) return undefined;\n const samples = Math.floor(chunk.data.byteLength / bytes);\n if (samples === 0) return 0;\n const view = new DataView(chunk.data.buffer, chunk.data.byteOffset, chunk.data.byteLength);\n let sum = 0;\n for (let index = 0; index < samples; index += 1) {\n const sample =\n bytes === 2 ? view.getInt16(index * 2, true) / 32768 : view.getFloat32(index * 4, true);\n sum += sample * sample;\n }\n return Math.sqrt(sum / samples);\n}\n\nconst compressed = new Set<AudioEncoding>(['opus', 'mp3', 'flac']);\n\nfunction pcmEnergy(chunk: AudioChunk): AudioChunk {\n const encoding = chunk.format.encoding;\n if (encoding === 'pcm16' || encoding === 'pcmf32') return chunk;\n if (compressed.has(encoding)) {\n throw new Error(`energyVad requires PCM audio, received ${encoding}`);\n }\n return convertAudio(chunk, { ...chunk.format, encoding: 'pcm16' });\n}\n\nexport function energyVad(options: EnergyVadOptions = {}): VadHook {\n const threshold = options.threshold ?? 0.02;\n const silenceMs = options.silenceMs ?? 500;\n let speaking = false;\n let silence = 0;\n return {\n push(chunk) {\n const sample = pcmEnergy(chunk);\n const measured = rms(sample);\n let level = 0;\n if (measured !== undefined) level = measured;\n const duration = durationMs(sample);\n if (level >= threshold) {\n silence = 0;\n if (speaking) return undefined;\n speaking = true;\n return { speechStart: chunk.timestamp };\n }\n if (!speaking) return undefined;\n silence += duration;\n if (silence < silenceMs) return undefined;\n speaking = false;\n silence = 0;\n return { turnEnd: chunk.timestamp + duration };\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,oBAAoB;AAgB7B,SAAS,MAAM,UAAgE;AAC7E,MAAI,aAAa,QAAS,QAAO;AACjC,MAAI,aAAa,SAAU,QAAO;AAClC,SAAO;AACT;AAEA,SAAS,WAAW,OAA2B;AAC7C,QAAM,QAAQ,MAAM,MAAM,OAAO,QAAQ;AACzC,MAAI,UAAU,UAAa,MAAM,OAAO,cAAc,KAAK,MAAM,OAAO,YAAY,EAAG,QAAO;AAC9F,QAAM,UAAU,MAAM,KAAK,aAAa,QAAQ,MAAM,OAAO;AAC7D,SAAQ,UAAU,MAAM,OAAO,aAAc;AAC/C;AAEA,SAAS,IAAI,OAAuC;AAClD,QAAM,QAAQ,MAAM,MAAM,OAAO,QAAQ;AACzC,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,UAAU,KAAK,MAAM,MAAM,KAAK,aAAa,KAAK;AACxD,MAAI,YAAY,EAAG,QAAO;AAC1B,QAAM,OAAO,IAAI,SAAS,MAAM,KAAK,QAAQ,MAAM,KAAK,YAAY,MAAM,KAAK,UAAU;AACzF,MAAI,MAAM;AACV,WAAS,QAAQ,GAAG,QAAQ,SAAS,SAAS,GAAG;AAC/C,UAAM,SACJ,UAAU,IAAI,KAAK,SAAS,QAAQ,GAAG,IAAI,IAAI,QAAQ,KAAK,WAAW,QAAQ,GAAG,IAAI;AACxF,WAAO,SAAS;AAAA,EAClB;AACA,SAAO,KAAK,KAAK,MAAM,OAAO;AAChC;AAEA,MAAM,aAAa,oBAAI,IAAmB,CAAC,QAAQ,OAAO,MAAM,CAAC;AAEjE,SAAS,UAAU,OAA+B;AAChD,QAAM,WAAW,MAAM,OAAO;AAC9B,MAAI,aAAa,WAAW,aAAa,SAAU,QAAO;AAC1D,MAAI,WAAW,IAAI,QAAQ,GAAG;AAC5B,UAAM,IAAI,MAAM,0CAA0C,QAAQ,EAAE;AAAA,EACtE;AACA,SAAO,aAAa,OAAO,EAAE,GAAG,MAAM,QAAQ,UAAU,QAAQ,CAAC;AACnE;AAEO,SAAS,UAAU,UAA4B,CAAC,GAAY;AACjE,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,YAAY,QAAQ,aAAa;AACvC,MAAI,WAAW;AACf,MAAI,UAAU;AACd,SAAO;AAAA,IACL,KAAK,OAAO;AACV,YAAM,SAAS,UAAU,KAAK;AAC9B,YAAM,WAAW,IAAI,MAAM;AAC3B,UAAI,QAAQ;AACZ,UAAI,aAAa,OAAW,SAAQ;AACpC,YAAM,WAAW,WAAW,MAAM;AAClC,UAAI,SAAS,WAAW;AACtB,kBAAU;AACV,YAAI,SAAU,QAAO;AACrB,mBAAW;AACX,eAAO,EAAE,aAAa,MAAM,UAAU;AAAA,MACxC;AACA,UAAI,CAAC,SAAU,QAAO;AACtB,iBAAW;AACX,UAAI,UAAU,UAAW,QAAO;AAChC,iBAAW;AACX,gBAAU;AACV,aAAO,EAAE,SAAS,MAAM,YAAY,SAAS;AAAA,IAC/C;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/pi-audio",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "Voice bindings for pi: speech-to-text, text-to-speech and realtime speech for pi-coding-agent plugins and slicc-agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -73,6 +73,10 @@
|
|
|
73
73
|
"./elevenlabs-agents": {
|
|
74
74
|
"types": "./dist/types/elevenlabs-agents/index.d.ts",
|
|
75
75
|
"default": "./dist/elevenlabs-agents/index.js"
|
|
76
|
+
},
|
|
77
|
+
"./fish-agents": {
|
|
78
|
+
"types": "./dist/types/fish-agents/index.d.ts",
|
|
79
|
+
"default": "./dist/fish-agents/index.js"
|
|
76
80
|
}
|
|
77
81
|
},
|
|
78
82
|
"files": [
|