-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagent.js
More file actions
131 lines (111 loc) · 4.32 KB
/
Copy pathagent.js
File metadata and controls
131 lines (111 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { WorkerOptions, defineAgent, cli, voice } from '@livekit/agents';
import { AvatarSession } from '@livekit/agents-plugin-bey';
import * as openai from '@livekit/agents-plugin-openai';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { readFileSync, existsSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Load .env file
const envPath = join(__dirname, '.env');
if (existsSync(envPath)) {
const envContent = readFileSync(envPath, 'utf-8');
for (const line of envContent.split('\n')) {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#')) {
const [key, ...valueParts] = trimmed.split('=');
if (key && valueParts.length > 0) {
process.env[key.trim()] = valueParts.join('=').trim();
}
}
}
}
// System prompt for the French tutor
const SYSTEM_PROMPT = `You are Alessandra, a friendly French teacher. Your name is Alessandra - NEVER say you are ChatGPT, GPT, or an AI assistant.
IMPORTANT: When you first connect, you MUST immediately greet the student by saying: "Bonjour! Hi, I am Alessandra, your French teacher. What would you like to practice today?"
COMMUNICATION STYLE:
- Speak naturally like a real person - NO parentheses, NO bracketed translations
- When role-playing (waiter, shopkeeper), stay in character and speak French naturally
- If the student doesn't understand, they will ask - then explain in English
- Keep responses short and conversational
LANGUAGE RULES:
- If student speaks English: respond in English, teach French phrases naturally
- If student speaks French: respond in French, encourage them
- When introducing new French words, say them naturally then explain if needed
BAD: "Bonjour! (Hello!)"
GOOD: "Bonjour!" and if they seem confused, "That means hello"
DURING PRACTICE:
- Stay in character and speak French
- Only switch to English if the student asks for help or seems lost
- Gently correct mistakes
- Keep it fun and encouraging`;
// Create a custom Agent class for Alessandra
class AlessandraAgent extends voice.Agent {
constructor() {
super({
instructions: SYSTEM_PROMPT,
});
}
// Called when agent enters the session - use for initial greeting
async onEnter() {
console.log('Agent entered, sending greeting...');
// Use generateReply to trigger AI to speak the greeting from system prompt
this.session.generateReply();
}
}
export default defineAgent({
entry: async (ctx) => {
const startTime = Date.now();
const log = (msg) => console.log(`[${Date.now() - startTime}ms] ${msg}`);
log('Agent entry called, connecting to room...');
// Connect to the room
await ctx.connect();
log('Connected to room: ' + ctx.room.name);
// Wait for a participant to connect
log('Waiting for participant...');
const participant = await ctx.waitForParticipant();
log('Participant connected: ' + participant.identity);
// Create AgentSession with OpenAI Realtime model
const session = new voice.AgentSession({
llm: new openai.realtime.RealtimeModel({
model: 'gpt-4o-realtime-preview-2024-12-17',
voice: 'alloy',
temperature: 0.8,
turnDetection: {
type: 'server_vad',
threshold: 0.5,
prefix_padding_ms: 200,
silence_duration_ms: 500,
},
}),
});
log('AgentSession created');
log('OPENAI_API_KEY exists: ' + !!process.env.OPENAI_API_KEY);
// Initialize Beyond Presence Avatar
const avatar = new AvatarSession({
// Uses BEY_API_KEY from environment
});
log('Starting avatar...');
// Start the avatar - it will join the room and sync with agent audio
await avatar.start(session, ctx.room);
log('Avatar connected to room');
// Start the agent session with custom agent
// Disable audio output since Avatar handles it
log('Starting agent session...');
try {
await session.start({
agent: new AlessandraAgent(),
room: ctx.room,
outputOptions: {
audioEnabled: false, // Avatar will handle audio output
},
});
log('Agent session started');
} catch (err) {
console.error('Error starting session:', err);
throw err;
}
},
});
// Run the CLI
cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) }));