Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion deep-sea-stories/packages/backend/src/agent/gemini/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import { getInstructionsForStory } from '../../utils.js';
import type { AgentConfig } from '../api.js';
import type { VoiceAgentSession } from '../session.js';

const AGENT_EXPIRATION_MAX_SECONDS = 15;

export class GeminiSession implements VoiceAgentSession {
private onInterrupt: (() => void) | null = null;
private onAgentAudio: ((audio: Buffer) => void) | null = null;
private onTurnEnd: (() => void) | null = null;
private session: Session | null = null;
private transcriptionParts: string[] = [];
private genai: GoogleGenAI;
Expand Down Expand Up @@ -57,11 +60,20 @@ export class GeminiSession implements VoiceAgentSession {
this.session.sendClientContent({
turns: [
{
text: 'IMPORTANT: The game time has expired. You must now: 1) Tell the players that time is up, 2) Evaluate how close they were to solving the riddle, 3) IMMEDIATELY call the endGame function to close the game session. Do not wait for player response - call endGame right after your message.',
text: 'IMPORTANT: The game time has expired. You must now: 1) Tell the players that time is up, 2) Evaluate how close they were to solving the riddle, 3) Call the `endGame` function tool.',
},
],
turnComplete: true,
});

return new Promise<void>((resolve) => {
const timeout = setTimeout(resolve, AGENT_EXPIRATION_MAX_SECONDS * 1000);
this.onTurnEnd = () => {
this.onTurnEnd = null;
clearTimeout(timeout);
resolve();
};
});
}

async waitUntilDone() {
Expand All @@ -73,6 +85,7 @@ export class GeminiSession implements VoiceAgentSession {
async open() {
if (this.opening) return;
this.opening = true;
this.ending = false;

const params: LiveConnectParameters = {
model: GEMINI_MODEL,
Expand Down Expand Up @@ -199,6 +212,8 @@ export class GeminiSession implements VoiceAgentSession {
this.transcriptionParts = [];
}

if (turnFinished) this.onTurnEnd?.();

const base64 = message.data;
if (base64) {
this.handleAgentAudio(Buffer.from(base64, 'base64'));
Expand Down
25 changes: 13 additions & 12 deletions deep-sea-stories/packages/backend/src/game/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,39 +187,40 @@ export class GameRoom {
console.log(`⏰ Game time limit reached for room ${this.roomId}`);
try {
await this.gameSession?.announceTimeExpired();
await this.stopGame(true);
} catch (e) {
console.error('Error announcing time expired:', e);
}
}, GAME_TIME_LIMIT_SECONDS * 1000);
}

async stopGame(wait: boolean = false) {
if (!this.gameSession) return;

const gameSession = this.gameSession;
this.gameSession = null;

console.log('Stopping game room %s', this.roomId);
if (this.gameTimeoutId) {
clearTimeout(this.gameTimeoutId);
this.gameTimeoutId = null;
}

try {
if (this.gameSession) {
await this.gameSession.stopGame(wait);
await this.fishjamClient.deletePeer(
this.roomId,
this.gameSession.agentId,
);
}
} catch (e) {
if (!(e instanceof PeerNotFoundException)) throw e;
} finally {
this.gameSession = null;
this.story = undefined;
await gameSession.stopGame(wait);

this.notifierService.emitNotification(this.roomId, {
type: 'gameEnded' as const,
timestamp: Date.now(),
});

await this.fishjamClient.deletePeer(this.roomId, gameSession.agentId);
} catch (e) {
if (!(e instanceof PeerNotFoundException)) throw e;
} finally {
this.story = undefined;
this.gameStarted = false;

console.log(`Stopped game for room ${this.roomId}`);
}
}
Expand Down