You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Job interviews can be stressful, and practice is key to building confidence. While traditional mock interviews with friends or mentors are valuable, they aren't always available when you need them. In this tutorial, you'll learn how to build an AI-powered Interview Practice Tool that provides real-time feedback and helps improve interview skills.
19
+
Job interviews can be stressful, and practice is key to building confidence. While traditional mock interviews with friends or mentors are valuable, they are not always available when you need them. In this tutorial, you will learn how to build an AI-powered interview practice tool that provides real-time feedback to help improve interview skills.
20
20
21
21
By the end of this tutorial, you will have built a complete interview practice tool with the following core functionalities:
22
22
@@ -36,7 +36,7 @@ This tutorial demonstrates how to use multiple Cloudflare products and while man
36
36
37
37
## 1. Create a new Worker project
38
38
39
-
As the first step, create a Cloudflare Workers project using the Create Cloudflare CLI (C3) tool and the Hono framework.
39
+
Create a Cloudflare Workers project using the Create Cloudflare CLI (C3) tool and the Hono framework.
40
40
41
41
:::note
42
42
[Hono](https://hono.dev) is a lightweight web framework that helps build API endpoints and handle HTTP requests. This tutorial uses Hono to create and manage the application's routing and middleware components.
@@ -76,9 +76,9 @@ npx wrangler dev
76
76
When you run `wrangler dev`, the command starts a local development server and provides a `localhost` URL where you can preview your application.
77
77
You can now make changes to your code and see them reflected in real-time at the provided localhost address.
78
78
79
-
###Define TypeScript types for the interview system
79
+
##2. Define TypeScript types for the interview system
80
80
81
-
Now that the project is set up, let's create the TypeScript types that will form the foundation of the interview system. These types will help you maintain type safety and provide clear interfaces for the different components of your application.
81
+
Now that the project is set up, create the TypeScript types that will form the foundation of the interview system. These types will help you maintain type safety and provide clear interfaces for the different components of your application.
82
82
83
83
Create a new file `types.ts` that will contain essential types and enums for:
84
84
@@ -102,7 +102,7 @@ export interface ApiContext {
102
102
exporttypeHonoCtx=Context<ApiContext>;
103
103
104
104
// List of technical skills you can assess during mock interviews.
105
-
//Let's focus on popular web technologies and programming languages
105
+
//This application focuses on popular web technologies and programming languages
Next, set up custom error types to handle different kinds of errors that may occur in your application. This includes:
174
174
@@ -226,17 +226,17 @@ export class InterviewError extends Error {
226
226
}
227
227
```
228
228
229
-
## 2. Configure Authentication Middleware and User Route
229
+
## 4. Configure authentication middleware and user routes
230
230
231
-
First, you will implement a basic authentication system to track and identify users interacting with your AI interview practice tool. The system uses HTTP-only cookies to store usernames, allowing you to identify both the request sender and their corresponding Durable Object. This straightforward authentication approach requires users to provide a username, which is then stored securely in a cookie. This approach allows you to:
231
+
In this step, you will implement a basic authentication system to track and identify users interacting with your AI interview practice tool. The system uses HTTP-only cookies to store usernames, allowing you to identify both the request sender and their corresponding Durable Object. This straightforward authentication approach requires users to provide a username, which is then stored securely in a cookie. This approach allows you to:
232
232
233
233
- Identify users across requests
234
234
- Associate interview sessions with specific users
235
235
- Secure access to interview-related endpoints
236
236
237
237
### Create the Authentication Middleware
238
238
239
-
First, create a middleware function that will check for the presence of a valid authentication cookie. This middleware will be used to protect routes that require authentication.
239
+
Create a middleware function that will check for the presence of a valid authentication cookie. This middleware will be used to protect routes that require authentication.
240
240
241
241
Create a new middleware file `middleware/auth.ts`:
242
242
@@ -328,7 +328,7 @@ const app = new Hono<ApiContext>();
328
328
const api =newHono<ApiContext>();
329
329
330
330
// Set up global middleware that runs on every request
331
-
// - Logger gives us visibility into what's happening
331
+
// - Logger gives us visibility into what is happening
332
332
app.use("*", logger());
333
333
app.use("*", requireAuth);
334
334
@@ -337,7 +337,7 @@ app.use("*", requireAuth);
337
337
api.route("/auth", configureAuthRoutes());
338
338
339
339
// Mount all API routes under the version prefix (for example, /api/v1)
340
-
// This lets us make breaking changes in v2 without affecting v1 users
340
+
// This allows us to make breaking changes in v2 without affecting v1 users
341
341
app.route("/api/v1", api);
342
342
343
343
exportdefaultapp;
@@ -349,17 +349,17 @@ Now we have a basic authentication system that:
349
349
2. Securely stores the username in a cookie
350
350
3. Includes middleware to protect authenticated routes
351
351
352
-
## 3. Create a Durable Object to Manage Interview
352
+
## 5. Create a Durable Object to manage interviews
353
353
354
-
Now that you have your authentication system in place, create a Durable Object to manage interview sessions. Durable Objects are perfect for the interview practice tool because they provide:
354
+
Now that you have your authentication system in place, create a Durable Object to manage interview sessions. Durable Objects are perfect for this interview practice tool because they provide the following functionalities:
355
355
356
-
- Maintains state between connections, so users can reconnect without losing progress
357
-
-Comes with a powerful SQLite database to store all interview Q&A, feedback and metrics
358
-
- Enables smooth real-time interactions between the interviewer AI and candidate
359
-
- Handles multiple interview sessions efficiently without performance issues
360
-
- Creates a dedicated instance for each user, giving them their own isolated environment
356
+
- Maintains states between connections, so users can reconnect without losing progress.
357
+
-Provides a SQLite database to store all interview Q&A, feedback and metrics.
358
+
- Enables smooth real-time interactions between the interviewer AI and candidate.
359
+
- Handles multiple interview sessions efficiently without performance issues.
360
+
- Creates a dedicated instance for each user, giving them their own isolated environment.
361
361
362
-
First, let's configure the Durable Object in `wrangler.toml` file. Add the following configuration:
362
+
First, you will need to configure the Durable Object in `wrangler.toml` file. Add the following configuration:
363
363
364
364
```toml title="wrangler.toml"
365
365
[[durable_objects.bindings]]
@@ -383,7 +383,7 @@ export class Interview extends DurableObject<CloudflareBindings> {
// Initialize empty sessions map - we will add WebSocket connections as users join
387
387
this.sessions=newMap();
388
388
}
389
389
@@ -429,14 +429,14 @@ Since the Worker code is written in TypeScript, you should run the following com
429
429
npm run cf-typegen
430
430
```
431
431
432
-
### Set up SQLite Database Schema
432
+
### Set up SQLite database schema to store interview data
433
433
434
-
Let's use SQLite at the Durable Object level for data persistence. This gives each user their own isolated database instance. You'll need two main tables:
434
+
Now you will use SQLite at the Durable Object level for data persistence. This gives each user their own isolated database instance. You will need two main tables:
435
435
436
436
-`interviews`: Stores interview session data
437
437
-`messages`: Stores all messages exchanged during interviews
438
438
439
-
But before you create these tables, create a service class to handle your database operations. This encapsulates database logic and helps you:
439
+
Before you create these tables, create a service class to handle your database operations. This encapsulates database logic and helps you:
440
440
441
441
- Manage database schema changes
442
442
- Handle errors consistently
@@ -470,7 +470,7 @@ export class InterviewDatabaseService {
470
470
constructor(privatesql:SqlStorage) {}
471
471
472
472
/**
473
-
* Sets up the database schema by creating tables and indexes if they don't exist.
473
+
* Sets up the database schema by creating tables and indexes if they do not exist.
474
474
* This is called when initializing a new Durable Object instance to ensure
475
475
* we have the required database structure.
476
476
*
@@ -486,7 +486,7 @@ export class InterviewDatabaseService {
The `getInterviewDO` helper function uses the username from our authentication cookie to create a unique Durable Object ID. This ensures each user has their own isolated interview state.
925
-
:::
926
924
927
925
Update your main application file to include the routes and protect them with authentication middleware. Update `src/index.ts`:
##7. Set up WebSockets to handle real-time communication
975
973
976
-
Now that you have your basic interview management system in place, let's add real-time communication capabilities using WebSockets. You'll use Durable Objects to maintain WebSocket connections and handle real-time message processing.
974
+
With the basic interview management system in place, you will now implement Durable Objects to handle real-time message processing and maintain WebSocket connections.
977
975
978
-
First, update Interview Durable Object to handle WebSocket connections. Update`src/interview.ts`:
976
+
Update the `Interview` Durable Object to handle WebSocket connections by adding the following code to`src/interview.ts`:
@@ -1000,7 +998,7 @@ export class Interview extends DurableObject<CloudflareBindings> {
1000
998
returnthis.handleWebSocketUpgrade(request);
1001
999
}
1002
1000
1003
-
// If it's not a WebSocket request, we don't handle it
1001
+
// If it is not a WebSocket request, we don't handle it
1004
1002
returnnewResponse("Not found", { status: 404 });
1005
1003
}
1006
1004
@@ -1084,7 +1082,7 @@ The WebSocket system provides real-time communication features for interview pra
1084
1082
- To keep connections stable, it automatically responds to ping messages with pongs, preventing timeouts
1085
1083
- Candidates and interviewers receive instant updates as the interview progresses, creating a natural conversational flow
1086
1084
1087
-
## 4. Add audio processing capabilities with Workers AI
1085
+
## 8. Add audio processing capabilities with Workers AI
1088
1086
1089
1087
Now that WebSocket connection set up, the next step is to add speech-to-text capabilities using Workers AI. Let's use Cloudflare's Whisper model to transcribe audio in real-time during the interview.
1090
1088
@@ -1097,17 +1095,17 @@ The audio processing pipeline will work like this:
1097
1095
5. We immediately send the transcription back to the client
1098
1096
6. The client receives a notification that the AI interviewer is generating a response
1099
1097
1100
-
### Create Audio Processing Pipeline
1098
+
### Create audio processing pipeline
1101
1099
1102
-
Now let's update Interview Durable Object to handle binary audio data:
1100
+
In this step you will update the Interview Durable Object to handle the following:
1103
1101
1104
-
1.Detects binary audio data sent through WebSocket
1105
-
2.Creates a unique message ID for tracking the processing status
1106
-
3.Notifies clients that audio processing has begun
1107
-
4.Includes error handling for failed audio processing
1108
-
5.Broadcasts status updates to all connected clients
1102
+
1.Detect binary audio data sent through WebSocket
1103
+
2.Create a unique message ID for tracking the processing status
1104
+
3.Notify clients that audio processing has begun
1105
+
4.Include error handling for failed audio processing
1106
+
5.Broadcast status updates to all connected clients
1109
1107
1110
-
First, let's update Interview Durable Object to handle binary WebSocket messages. Add the following methods to your `src/interview.ts` file:
1108
+
First, update Interview Durable Object to handle binary WebSocket messages. Add the following methods to your `src/interview.ts` file:
1111
1109
1112
1110
```typescript title="src/interview.ts"
1113
1111
// ... previous code ...
@@ -1189,23 +1187,23 @@ Your `handleBinaryAudio` method currently logs when it receives audio data. Next
1189
1187
1190
1188
### Configure speech-to-text
1191
1189
1192
-
Now that audio processing pipeline is set up, let's integrate Workers AI's Whisper model for speech-to-text transcription.
1190
+
Now that audio processing pipeline is set up, you will now integrate Workers AI's Whisper model for speech-to-text transcription.
1193
1191
1194
-
First, configure the Worker AI binding in your `wrangler.toml` file by adding:
1192
+
Configure the Worker AI binding in your `wrangler.toml` file by adding:
1195
1193
1196
1194
```toml
1197
1195
# ... previous configuration ...
1198
1196
[ai]
1199
1197
binding = "AI"
1200
1198
```
1201
1199
1202
-
Next, you need to generate TypeScript types for our AI binding. Run the following command:
1200
+
Next, generate TypeScript types for our AI binding. Run the following command:
1203
1201
1204
1202
```sh
1205
1203
npm run cf-typegen
1206
1204
```
1207
1205
1208
-
Now, let's create a new service class for AI operations. Create a new file called `services/AIService.ts`:
1206
+
You will need a new service class for AI operations. Create a new file called `services/AIService.ts`:
Now, let's update Interview Durable Object to use this new AI service. Update the handleBinaryAudio method in `src/interview.ts`:
1236
+
You will need to update the `Interview` Durable Object to use this new AI service. To do this, update the handleBinaryAudio method in `src/interview.ts`:
The Whisper model `@cf/openai/whisper-tiny-en` is optimized for English speech recognition. If you need support for other languages, you can use different Whisper model variants available through Workers AI.
1291
1289
:::
1292
1290
1293
-
Now when users speak during the interview, their audio will be automatically transcribed and stored as messages in the interview session. The transcribed text will be immediately available to both the user and the AI interviewer for generating appropriate responses.
1291
+
When users speak during the interview, their audio will be automatically transcribed and stored as messages in the interview session. The transcribed text will be immediately available to both the user and the AI interviewer for generating appropriate responses.
1294
1292
1295
-
## 5. Integrate AI response generation
1293
+
## 9. Integrate AI response generation
1296
1294
1297
1295
Now that you have audio transcription working, let's implement AI interviewer response generation using Workers AI's LLM capabilities. You'll create an interview system that:
1298
1296
@@ -1301,9 +1299,9 @@ Now that you have audio transcription working, let's implement AI interviewer re
1301
1299
- Gives constructive feedback
1302
1300
- Stays in character as a professional interviewer
1303
1301
1304
-
### 5.1. Set up Workers AI LLM integration
1302
+
### Set up Workers AI LLM integration
1305
1303
1306
-
First, let's enhance`AIService` class to handle LLM interactions. You will need to add methods for:
1304
+
First, update the`AIService` class to handle LLM interactions. You will need to add methods for:
The @cf/meta/llama-2-7b-chat-int8 model is optimized for chat-like interactions and provides good performance while maintaining reasonable resource usage.
1355
1353
:::
1356
1354
1357
-
### 5.2. Create conversation prompt
1355
+
### Create the conversation prompt
1358
1356
1359
1357
Prompt engineering is crucial for getting high-quality responses from the LLM. Next, you will create a system prompt that:
Finally, let's integrate the LLM response generation into the interview flow. Update the `handleBinaryAudio` method in the `src/interview.ts` Durable Object to:
1379
+
Finally, integrate the LLM response generation into the interview flow. Update the `handleBinaryAudio` method in the `src/interview.ts` Durable Object to:
0 commit comments