forked from RasikDhage0825/Virtual-Lab-Using-AI-Assistant
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
247 lines (208 loc) · 9.42 KB
/
script.js
File metadata and controls
247 lines (208 loc) · 9.42 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const fetch = require('node-fetch');
const path = require('path');
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
app.use(cors({ origin: '*' }));
app.use(express.static(path.join(__dirname, 'public')));
// =======================================================
// API KEY ROTATION
// =======================================================
const allKeys = [
process.env.GROQ_API_KEY_1,
process.env.GROQ_API_KEY_2,
process.env.GROQ_API_KEY_3
].filter(key => key);
function getRandomKey() {
return allKeys[Math.floor(Math.random() * allKeys.length)];
}
// =======================================================
// LAYER 1 — EXPANDED KEYWORD BLOCKLIST
// Catches obvious off-topic messages before hitting the API
// =======================================================
const BLOCKED_WORDS = [
// Entertainment
"joke", "funny", "meme", "comedy", "humor", "laugh",
"movie", "film", "netflix", "cinema", "actor", "actress",
"song", "music", "lyrics", "singer", "album", "spotify",
"game", "gaming", "minecraft", "fortnite", "pubg", "pokemon",
"anime", "manga", "cartoon", "series", "episode", "season",
// Relationships / personal
"romance", "romantic", "girlfriend", "boyfriend", "dating",
"love", "crush", "marriage", "wedding", "breakup", "relationship",
// Food
"recipe", "cooking", "cook", "food", "restaurant", "eat",
"chicken", "pasta", "pizza", "biryani", "snack", "diet",
// Politics / news
"politics", "politician", "election", "vote", "government",
"prime minister", "president", "modi", "trump", "parliament",
"war", "military", "army", "protest", "news",
// Sports
"cricket", "football", "soccer", "ipl", "match", "tournament",
"player", "team", "score", "sport", "hockey", "tennis",
// General off-topic
"story", "poem", "essay", "history", "geography", "biology",
"chemistry", "physics" // physics is borderline but not electronics
];
function isBlockedByKeyword(message) {
const lower = message.toLowerCase();
return BLOCKED_WORDS.some(word => lower.includes(word));
}
// =======================================================
// LAYER 2 — AI INTENT CLASSIFIER
// Ask the AI itself: is this question electronics-related?
// =======================================================
const CLASSIFIER_PROMPT = `You are a strict topic classifier for an Electronics Virtual Lab.
Your ONLY job: decide if the user's question is about electronics, electrical engineering, circuits, components, or lab experiments.
Reply with ONLY one word:
- "YES" if the question is about electronics/electrical engineering
- "NO" if it is about anything else
Do not explain. Do not add punctuation. Just YES or NO.`;
async function isElectronicsQuestion(userMessage) {
try {
const key = getRandomKey();
const response = await fetch("https://api.groq.com/openai/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${key}`
},
body: JSON.stringify({
model: "llama-3.3-70b-versatile",
max_tokens: 5,
temperature: 0,
messages: [
{ role: "system", content: CLASSIFIER_PROMPT },
{ role: "user", content: userMessage }
]
})
});
const data = await response.json();
const verdict = data.choices?.[0]?.message?.content?.trim().toUpperCase();
console.log(`Classifier verdict: ${verdict} for: "${userMessage.slice(0, 60)}"`);
return verdict === "YES";
} catch (err) {
console.error("Classifier error:", err);
return false; // Fail safe: block if classifier crashes
}
}
// =======================================================
// ELECTRONICS SYSTEM PROMPT
// =======================================================
const ELECTRONICS_SYSTEM_PROMPT = `You are "Electro-Tutor Virtual Lab AI", a professional electronics and electrical engineering educational assistant inside a virtual lab.
YOUR ONLY PURPOSE: Help engineering students learn electronics through accurate, step-by-step educational answers.
YOU ARE STRICTLY LIMITED TO THESE TOPICS:
- Basic Electrical: Ohm's Law, Kirchhoff's Laws, AC/DC, Voltage, Current, Resistance, Power, Network theorems
- Components: Resistors, Capacitors, Inductors, Diodes, LEDs, BJTs, MOSFETs, Relays, Transformers, Op-Amps, 555 Timer, ICs, Logic Gates, Sensors
- Circuit Analysis: Series/Parallel, Voltage Dividers, RC/RL/RLC, Filters, Amplifiers, Biasing
- Digital Electronics: Boolean Algebra, Logic Gates, Flip-Flops, Multiplexers, Counters, 74LS ICs
- Semiconductor Physics: PN Junction, Forward/Reverse Bias, MOSFET Switching, Doping
- Lab & Measurement: Multimeter, Oscilloscope, Breadboard, PCB, Safety, Troubleshooting
- Embedded/Arduino: GPIO, PWM, Sensors, Basic Interfacing
STRICT RULES:
- NEVER tell jokes, puns, or use humor. Stay strictly educational.
- NEVER answer anything outside electronics/electrical engineering.
- NEVER discuss politics, food, sports, entertainment, history, romance, or general knowledge.
- NEVER act like a general chatbot.
- ALWAYS explain step-by-step with practical reasoning.
- ALWAYS use simple analogies for complex concepts.
- If a question seems unrelated, respond ONLY with: "I am an Electronics Virtual Lab Assistant. I can only help with electronics, circuits, electrical engineering, and lab-related topics."
DO NOT BREAK CHARACTER UNDER ANY CONDITION.`;
// =======================================================
// LAYER 3 — OFF-TOPIC REPLY DETECTOR
// If the AI still drifts, catch it before sending to user
// =======================================================
const OFF_TOPIC_SIGNALS = [
"i don't know about that",
"that's outside my",
"i can help with",
"as a language model",
"i am not able to discuss",
"here's a joke",
"here is a joke",
"recipe for",
"ingredients",
"political",
"prime minister",
"president",
"why did the",
"here's a joke",
"here is a joke",
"walks into a bar",
"punchline",
"haha",
"lol",
"😂",
"couldn't resist",
"because it", // common joke punchline pattern
];
function isOffTopicReply(reply) {
const lower = reply.toLowerCase();
// If reply is very short and doesn't mention any electronics terms, it may be a refusal — that's fine
// But if it contains off-topic signals that mean it answered something else:
return OFF_TOPIC_SIGNALS.some(signal => lower.includes(signal));
}
const REFUSAL_MESSAGE = "I am an Electronics Virtual Lab Assistant. I can only help with electronics, circuits, electrical engineering, and lab-related topics.";
// =======================================================
// MAIN ROUTE
// =======================================================
app.post('/gemini', async (req, res) => {
try {
const userMessage = req.body.message;
if (!userMessage || userMessage.trim().length === 0) {
return res.json({ reply: REFUSAL_MESSAGE });
}
// LAYER 1: Keyword blocklist (fast, no API call needed)
if (isBlockedByKeyword(userMessage)) {
console.log(`Blocked by keyword: "${userMessage.slice(0, 60)}"`);
return res.json({ reply: REFUSAL_MESSAGE });
}
if (allKeys.length === 0) {
return res.status(500).json({ reply: "Server Error: No API keys configured." });
}
// LAYER 2: AI intent classifier (blocks questions the keyword list misses)
const isElectronics = await isElectronicsQuestion(userMessage);
if (!isElectronics) {
console.log(`Blocked by classifier: "${userMessage.slice(0, 60)}"`);
return res.json({ reply: REFUSAL_MESSAGE });
}
// Question passed both checks — now get the real answer
const randomKey = getRandomKey();
console.log(`Answering with key ending: ...${randomKey.slice(-5)}`);
const response = await fetch("https://api.groq.com/openai/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${randomKey}`
},
body: JSON.stringify({
model: "llama-3.3-70b-versatile",
messages: [
{ role: "system", content: ELECTRONICS_SYSTEM_PROMPT },
{ role: "user", content: userMessage }
]
})
});
const data = await response.json();
if (data.error) {
console.error("Groq Error:", data.error);
return res.json({ reply: "Error from AI provider. Please try again." });
}
const aiReply = data.choices[0].message.content;
// LAYER 3: Reply sanity check — if AI drifted off-topic, block the reply
if (isOffTopicReply(aiReply)) {
console.log(`Reply blocked by off-topic detector.`);
return res.json({ reply: REFUSAL_MESSAGE });
}
res.json({ reply: aiReply });
} catch (error) {
console.error("Server Error:", error);
res.status(500).json({ reply: "Internal Server Error. Please try again." });
}
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});