Skip to content
Merged

Test #21

Show file tree
Hide file tree
Changes from all 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
152 changes: 104 additions & 48 deletions routes/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@
router.use(express.urlencoded({ extended: true }));

router.use(
session({
store: new PgSession({
pool: pool1, // Connection pool
tableName: "session", // Use another table-name than the default "session" one
}),
secret: process.env.session_seceret_key, // Replace with your secret key
resave: false,
saveUninitialized: false,
cookie: {
maxAge: cookieExpireTime,
},
})

Check failure

Code scanning / CodeQL

Missing CSRF middleware High

This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
This cookie middleware is serving a
request handler
without CSRF protection.
);

router.use((req, res, next) => {
Expand Down Expand Up @@ -337,35 +337,34 @@
res.status(404).json({ message: "Chat history not found." });
}
});
async function getUserSettings(username) {
try {
const result = await pool1.query(
`SELECT ai_model, temperature
FROM user_settings
WHERE username = $1`,
[username]
);

router.post('/api/bot-chat', async (req, res) => {
const userMessage = req.body.message;
if (!userMessage) {
return res.status(400).json({ message: "Chat message is required." });
}


const temperature = Math.min(Math.max(parseFloat(req.body.temperature || 1.0), 0), 2);
let conversationHistory = [];
const chatId = req.body.chatId;

if (chatId) {
const fetchedHistory = await fetchChatHistoryById(chatId);
if (fetchedHistory && fetchedHistory.conversation_history) {
conversationHistory = fetchedHistory.conversation_history;
if (result.rows.length > 0) {
// User settings found
const settings = result.rows[0];
return {
modelName: settings.ai_model, // Assuming column name is ai_model
temperature: settings.temperature // Assuming column name is temperature
};
} else {
// No settings found for this user
return null; // Or you could return a default settings object instead of null
}
}

conversationHistory.push({ role: "user", parts: [{ text: userMessage }] });

let geminiApiKey;
geminiApiKey = process.env.GEMINI_API_KEY_maaz_waheed;

if (!geminiApiKey) {
return res.status(500).json({ error: "Gemini API key not configured." });
} catch (error) {
console.error("Error retrieving user settings:", error);
return null; // Or throw the error if you want to handle it differently up the call stack
}

const geminiApiUrl = `https://generativelanguage.googleapis.com/v1/models/gemini-2.0-flash:generateContent?key=${geminiApiKey}`;
}
async function gemini(geminiApiKey, models_name, conversationHistory, temperature) {
const geminiApiUrl = `https://generativelanguage.googleapis.com/v1/models/${models_name}:generateContent?key=${geminiApiKey}`;

try {
const geminiResponse = await fetch(geminiApiUrl, {
Expand All @@ -374,52 +373,109 @@
body: JSON.stringify({
contents: conversationHistory,
generationConfig: {
temperature: temperature // Add temperature to the request
temperature: temperature
}
})
});

if (!geminiResponse.ok) {
const errorData = await geminiResponse.json();
console.error("Gemini API error:", errorData);
return res.status(500).json({ message: "Gemini API request failed.", details: errorData });
// Improved error message to include details from Gemini API response
throw new Error(`Gemini API request failed with status ${geminiResponse.status}: ${errorData.error?.message || 'Unknown error'}`);
}

const geminiData = await geminiResponse.json();
const aiResponseText = geminiData.candidates?.[0]?.content?.parts?.[0]?.text;
return aiResponseText;
} catch (error) {
console.error("Error calling Gemini API:", error);
// Re-throw the error to be handled by the route handler
throw error;
}
}

router.post('/api/bot-chat', async (req, res) => {
const userMessage = req.body.message;
if (!userMessage) {
return res.status(400).json({ message: "Chat message is required." });
}
const username = req.session.user.username; // Assuming you have user session

const userSettings = await getUserSettings(username);
const temperature = Math.min(Math.max(parseFloat(userSettings.temperature|| 1.0), 0), 2);
let conversationHistory = [];
const chatId = req.body.chatId;

try { // Wrap the chat history fetching in try-catch
if (chatId) {
const fetchedHistory = await fetchChatHistoryById(chatId);
if (fetchedHistory && fetchedHistory.conversation_history) {
conversationHistory = fetchedHistory.conversation_history;
} else if (chatId && !fetchedHistory) {
// Handle case where chatId is provided but no history found (optional, depends on desired behavior)
return res.status(404).json({ message: "Chat history not found for the given chatId." });
}
}
} catch (dbError) { // Catch errors from fetchChatHistoryById (database errors)
console.error("Error fetching chat history:", dbError);
return res.status(500).json({ message: "Failed to fetch chat history.", error: dbError.message });
}


conversationHistory.push({ role: "user", parts: [{ text: userMessage }] });

let geminiApiKey;
geminiApiKey = process.env.GEMINI_API_KEY_maaz_waheed;

if (!geminiApiKey) {
return res.status(500).json({ error: "Gemini API key not configured." });
}
const models_name =userSettings.modelName; //"gemini-1.5-flash" "gemini-2.0-flash"
console.log(models_name);

let aiResponseText;
try {
aiResponseText = await gemini(geminiApiKey, models_name, conversationHistory, temperature);
} catch (geminiError) { // Catch errors from gemini function
return res.status(500).json({ message: "Error processing Gemini API.", error: geminiError.message });
}

if (aiResponseText) {
conversationHistory.push({ role: "model", parts: [{ text: aiResponseText }] });

if (chatId) {
await pool1.query(
`UPDATE Ai_history
SET conversation_history = $1,
created_at = CURRENT_TIMESTAMP,
temperature = $3
WHERE id = $2`,
[JSON.stringify(conversationHistory), chatId, temperature]
);
} else {
const insertResult = await pool1.query(
`INSERT INTO Ai_history (conversation_history, username, temperature)
VALUES ($1, $2, $3)
RETURNING id`,
[JSON.stringify(conversationHistory), req.session.user.username, temperature]
);
req.newChatId = insertResult.rows[0].id;
try { // Wrap database updates/inserts in try-catch
if (chatId) {
await pool1.query(
`UPDATE Ai_history
SET conversation_history = $1,
created_at = CURRENT_TIMESTAMP,
temperature = $3
WHERE id = $2`,
[JSON.stringify(conversationHistory), chatId, temperature]
);
} else {
const insertResult = await pool1.query(
`INSERT INTO Ai_history (conversation_history, username, temperature)
VALUES ($1, $2, $3)
RETURNING id`,
[JSON.stringify(conversationHistory), req.session.user.username, temperature]
);
req.newChatId = insertResult.rows[0].id;
}
} catch (dbError) { // Catch errors from database operations (pool1.query)
console.error("Error updating/inserting chat history:", dbError);
return res.status(500).json({ message: "Failed to save chat history.", error: dbError.message });
}


res.json({ aiResponse: aiResponseText, newChatId: req.newChatId });
} else {
res.status(500).json({ message: "Gemini API returned empty response." });
}

} catch (error) {
console.error("Error calling Gemini API:", error);
res.status(500).json({ message: "Error processing Gemini API.", error: error.message, details: error });
}

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
a database access
, but is not rate-limited.
This route handler performs
a database access
, but is not rate-limited.
});
);

router.post('/api/chat/clear-history/:chatId', async (req, res) => {
const chatId = req.params.chatId;
Expand Down
8 changes: 5 additions & 3 deletions views/mainPages/chatbot.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -1397,9 +1397,11 @@
<label for="model-select">AI Model</label>
<div class="settings-dropdown">
<select id="model-select">
<option value="default">Default Model</option>
<option value="advanced">Advanced Model</option>
<option value="expert">Expert Model</option>
<option value="gemini-2.0-flash">Gemini 2.0 Flash</option>
<option value="gemini-2.0-flash-lite">Gemini 2.0 Flash Lite</option>
<option value="gemini-1.5-flash">Gemini 1.5 Flash</option>
<option value="gemini-1.5-flash-8b">Gemini 1.5 Flash 8B</option>
<option value="gemini-1.5-pro">Gemini 1.5 Pro</option>
</select>
</div>
</div>
Expand Down
77 changes: 77 additions & 0 deletions views/staticPage/index.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,36 @@
color: var(--accent-color);
}

/* Model Selection Section */
.model-selection {
background-color: var(--header-bg);
padding: 2rem;
border-radius: var(--border-radius);
margin: 2rem 0;
border: 1px solid var(--border-color);
}

.model-table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}

.model-table th, .model-table td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid var(--border-color);
}

.model-table th {
background-color: rgba(3, 218, 198, 0.1);
color: var(--accent-color);
}

.model-table tr:hover {
background-color: rgba(255, 255, 255, 0.05);
}

/* Try Demo Section */
.try-demo {
padding: var(--section-padding);
Expand Down Expand Up @@ -456,6 +486,11 @@
footer .container {
flex-direction: column;
}

.model-table {
display: block;
overflow-x: auto;
}
}

@media (max-width: 480px) {
Expand Down Expand Up @@ -578,6 +613,48 @@
<li><strong>Developer Experimentation:</strong> A platform for developers to test and integrate various
AI models and chatbot functionalities.</li>
</ol>

<div class="model-selection">
<h3>Gemini Model Selection Guide</h3>
<p>Choose the right Gemini model for your needs based on these key features:</p>

<table class="model-table">
<thead>
<tr>
<th>Model</th>
<th>Type</th>
<th>Best For</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Gemini 2.0 Flash</strong></td>
<td>Text</td>
<td>Next generation features, speed, thinking, realtime responses</td>
</tr>
<tr>
<td><strong>Gemini 2.0 Flash-Lite</strong></td>
<td>Text</td>
<td>Cost efficiency and low latency requirements</td>
</tr>
<tr>
<td><strong>Gemini 1.5 Flash</strong></td>
<td>Text</td>
<td>Fast and versatile performance across a diverse variety of tasks</td>
</tr>
<tr>
<td><strong>Gemini 1.5 Flash-8B</strong></td>
<td>Text</td>
<td>High volume and lower intelligence tasks</td>
</tr>
<tr>
<td><strong>Gemini 1.5 Pro</strong></td>
<td>Text</td>
<td>Complex reasoning tasks requiring more intelligence</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>

Expand Down