feat(voice): on-demand PodMan voice test + Hermes notify + TTS tuning
Snapshot of in-progress voice work, committed to unblock concurrent edits: speakInRoom + POST voice-test endpoint, Test PodMan voice button (PodView/api), Hermes notify action + scripts/hermes-notify.mjs, agent exits on LiveKit disconnect for auto-restart, TTS playback tuning (subscriber-ready delay, preroll/tail silence, fallback line, microphone source), verify script updates. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import type { Room } from '@livekit/rtc-node';
|
||||
import { Room as LiveKitRoom } from '@livekit/rtc-node';
|
||||
import { AccessToken } from 'livekit-server-sdk';
|
||||
import type { Collision, DataMessage, HermesMessage, Intervention } from '@podman/shared';
|
||||
import { DATA_TOPIC } from '@podman/shared';
|
||||
import { env } from '../env.js';
|
||||
import { speak } from '../voice/live.js';
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
@@ -37,3 +41,52 @@ export async function publishHermesMessage(
|
||||
topic: DATA_TOPIC,
|
||||
});
|
||||
}
|
||||
|
||||
export async function publishHermesIntervention(
|
||||
room: Room,
|
||||
collision: Collision,
|
||||
intervention: Intervention,
|
||||
voiceLine?: string,
|
||||
): Promise<void> {
|
||||
const data: DataMessage = { type: 'COLLISION', collision, intervention };
|
||||
await room.localParticipant?.publishData(encoder.encode(JSON.stringify(data)), {
|
||||
reliable: true,
|
||||
topic: DATA_TOPIC,
|
||||
});
|
||||
await publishHermesMessage(room, collision, intervention);
|
||||
if (voiceLine) await speak(room, voiceLine);
|
||||
}
|
||||
|
||||
async function hermesToken(roomName: string): Promise<string> {
|
||||
const at = new AccessToken(env.LIVEKIT_API_KEY, env.LIVEKIT_API_SECRET, {
|
||||
identity: `podman-hermes-${Date.now()}`,
|
||||
name: 'PodMan Hermes',
|
||||
ttl: '10m',
|
||||
});
|
||||
at.addGrant({
|
||||
roomJoin: true,
|
||||
room: roomName,
|
||||
canPublish: true,
|
||||
canSubscribe: true,
|
||||
canPublishData: true,
|
||||
});
|
||||
return at.toJwt();
|
||||
}
|
||||
|
||||
export async function notifyHermesInterventionInRoom(
|
||||
roomName: string,
|
||||
collision: Collision,
|
||||
intervention: Intervention,
|
||||
voiceLine?: string,
|
||||
): Promise<void> {
|
||||
const room = new LiveKitRoom();
|
||||
try {
|
||||
await room.connect(env.LIVEKIT_URL, await hermesToken(roomName), {
|
||||
autoSubscribe: false,
|
||||
dynacast: false,
|
||||
});
|
||||
await publishHermesIntervention(room, collision, intervention, voiceLine);
|
||||
} finally {
|
||||
await room.disconnect().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user