completed demo. need to start making it production ready

This commit is contained in:
Om Raheja 2024-05-18 14:46:41 -04:00
parent eb6a3e04ab
commit e7b4eaac0b
3 changed files with 247 additions and 173 deletions

44
app.js
View File

@ -2,17 +2,61 @@ const express = require('express');
const multer = require('multer');
const axios = require('axios');
const fs = require('fs'); // Add this line to import the fs module
const mime = require('mime-types');
const app = express();
const port = 3000;
const upload = multer({ dest: 'uploads/' });
const GROQ_API_KEY = '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';
const model = 'llama3-8b-8192';
app.use(express.static("static"));
app.use(express.json());
// Middleware to serve static files with correct MIME type
app.get('/', (req, res) => {
res.sendFile(__dirname + '/static/index.html');
});
// Serve the script.js file with the correct MIME type
app.get('/script.js', (req, res) => {
const filePath = __dirname + '/static/script.js';
res.set('Content-Type', 'application/javascript');
res.sendFile(filePath);
});
app.post('/completions', async (req, res) => {
try {
const query = req.body.query;
if (!query) {
return res.status(400).json({ error: 'Prompt is required' });
}
const data = {
messages: [{ role: 'user', content: query }],
model,
};
const config = {
headers: {
Authorization: `Bearer ${GROQ_API_KEY}`,
'Content-Type': 'application/json',
},
};
const response = await axios.post('https://api.groq.com/openai/v1/chat/completions', data, config);
res.json(response.data);
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Error completing prompt' });
}
});
app.post('/upload', upload.single('image'), async (req, res) => {
try {

View File

@ -1,176 +1,19 @@
<!-- <!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Upload Image</title>
</head>
<body>
<input type="file" id="imageInput" accept="image/*" required />
<button onclick="uploadImage()">Upload Image</button> <br /><br />
<div id="editable-classes"></div>
<button id="add-class-btn">Add Class</button>
<div id="suggested-classes"></div>
</body>
</html> -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Image</title>
</head>
<body>
<input type="file" id="imageInput" accept="image/*" required=""> <button onclick="uploadImage()" id="upload-image-btn">Upload Image</button>
<br><br>
<div id="editable-classes"></div>
<button id="add-class-btn">Add Class</button>
<div id="suggested-classes"></div>
<br>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Fridge2Fit</title>
<meta
name="description"
content="Fridge2Fit is an AI-powered recipe generator that helps individuals with obesity take control of their diet. Simply upload a picture of your fridge contents, and our algorithm will suggest healthy recipes tailored to your ingredients. Our goal is to empower users to make informed food choices, reduce food waste, and promote a healthier lifestyle."
/>
<meta
name="keywords"
content="ai artificial intelligence fridge fit food fat obesity health healthy"
/>
<link href="/dist.css" rel="stylesheet" />
<script src="/load.js" defer></script>
</head>
<body
class="leading-normal tracking-normal text-indigo-400 m-6 bg-cover bg-fixed overflow-hidden"
style="background-image: url('header.png')"
>
<div class="h-full">
<!--Nav-->
<div class="w-full container mx-auto">
<div class="w-full flex items-center justify-between">
<a
class="flex items-center text-indigo-400 no-underline hover:no-underline font-bold text-2xl lg:text-4xl"
href="#"
>
Fridge<span
class="bg-clip-text text-transparent bg-gradient-to-r from-green-400 via-pink-500 to-purple-500"
>2Fit</span
>
</a>
<div class="flex w-1/2 justify-end content-center">
<a
class="inline-block text-blue-300 no-underline hover:text-pink-500 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4 transform hover:scale-125 duration-300 ease-in-out"
href="https://twitter.com/intent/tweet?url=#"
>
<svg
class="fill-current h-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
>
<path
d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"
></path>
</svg>
</a>
<a
class="inline-block text-blue-300 no-underline hover:text-pink-500 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4 transform hover:scale-125 duration-300 ease-in-out"
href="https://www.facebook.com/sharer/sharer.php?u=#"
>
<svg
class="fill-current h-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
>
<path
d="M19 6h5V0h-5c-3.86 0-7 3.14-7 7v3H8v6h4v16h6V16h5l1-6h-6V7c0-.542.458-1 1-1z"
></path>
</svg>
</a>
</div>
</div>
</div>
<!--Main-->
<div
class="container pt-24 md:pt-36 mx-auto flex flex-wrap flex-col md:flex-row items-center"
>
<!--Left Col-->
<div
class="flex flex-col w-full xl:w-2/5 justify-center lg:items-start overflow-y-hidden"
>
<h1
class="my-4 text-3xl md:text-5xl text-white opacity-75 font-bold leading-tight text-center md:text-left"
>
Main
<span
class="bg-clip-text text-transparent bg-gradient-to-r from-green-400 via-pink-500 to-purple-500"
>
Hero Message
</span>
to sell yourself!
</h1>
<p
class="leading-normal text-base md:text-2xl mb-8 text-center md:text-left"
>
An AI-powered recipe generator that helps individuals
with obesity take control of their diet.
</p>
<form
class="bg-gray-800 opacity-75 w-full shadow-lg rounded-lg px-8 pt-6 pb-8 mb-4 h-64"
>
<button
class="text-4xl w-full h-full bg-gradient-to-r from-purple-800 to-green-500 hover:from-pink-500 hover:to-green-500 text-white font-bold py-2 px-4 rounded-3xl focus:ring transform transition hover:scale-105 duration-300 ease-in-out flex items-center"
onclick="document.getElementById('imageInput').click();"
>
<input
type="file"
id="imageInput"
accept="image/*"
required
hidden
/>
<img
src="upload.svg"
class="h-1/2 pr-8 brightness-150"
/>
Upload your fridge
</button>
</form>
</div>
<!--Right Col-->
<div class="w-full xl:w-3/5 p-12 overflow-hidden">
<img
class="mx-auto w-full md:w-4/5 transform -rotate-6 transition hover:scale-105 duration-700 ease-in-out hover:rotate-6"
src="macbook.svg"
/>
</div>
<div class="mx-auto md:pt-16">
<p
class="text-blue-400 font-bold pb-8 lg:pb-6 text-center"
></p>
<div
class="flex w-full justify-center md:justify-start pb-24 lg:pb-0 fade-in"
>
<img
class="h-12 pr-12 transform hover:scale-125 duration-300 ease-in-out"
alt=""
/>
<img
class="h-12 transform hover:scale-125 duration-300 ease-in-out"
alt=""
/>
</div>
</div>
<!--Footer-->
<div
class="w-full pt-16 pb-6 text-sm text-center md:text-left fade-in"
>
<a
class="text-gray-500 no-underline hover:no-underline"
href="#"
>&copy; Fridge2Fit 2024</a
>
</div>
</div>
</div>
</body>
<button onclick="generateIngredients()">Generate Ingredients</button>
<script src="/script.js"></script>
</body>
</html>

187
static/script.js Normal file
View File

@ -0,0 +1,187 @@
const editableClassesDiv = document.getElementById("editable-classes");
const suggestedClassesDiv = document.getElementById("suggested-classes");
const addClassBtn = document.getElementById("add-class-btn");
const uploadImageBtn = document.getElementById("upload-image-btn");
const uploadImage = async () => {
const input = document.getElementById('imageInput');
const file = input.files[0];
const formData = new FormData();
formData.append('image', file);
try {
uploadImageBtn.disabled = true;
const response = await fetch('../upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error('Failed to upload image');
}
const jsonResponse = await response.json();
const classes = jsonResponse.predictions;
// Remove all underscores
classes.forEach(str => {
str.class = str.class.replace(/_/g, ' ');
});
// Create input fields for classes with confidence >= 0.5
classes.filter((cls) => cls.confidence >= 0.5).forEach((cls) => {
addInputField(cls.class);
});
// Create buttons for classes with confidence < 0.5
classes.filter((cls) => cls.confidence < 0.5).forEach((cls) => {
addSuggestion(cls.class);
});
addSuggestion("salt and pepper");
// Add event listener to "Add Class" button
addClassBtn.onclick = () => {
addInputField("");
};
} catch (exception) {
uploadImageBtn.disabled = false;
console.log(exception);
}
};
function addInputField(content) {
const inputField = document.createElement("input");
inputField.type = "text";
inputField.value = content;
const closeButton = document.createElement("button");
closeButton.style.border = "none";
closeButton.style.background = "transparent";
closeButton.style.cursor = "pointer";
closeButton.innerHTML = "x";
closeButton.addEventListener("click", () => {
editableClassesDiv.removeChild(inputField);
editableClassesDiv.removeChild(br);
editableClassesDiv.removeChild(closeButton);
});
editableClassesDiv.appendChild(inputField);
editableClassesDiv.appendChild(closeButton);
const br = document.createElement("br");
editableClassesDiv.appendChild(br);
}
function addSuggestion(content) {
const button = document.createElement("button");
button.textContent = content;
button.onclick = () => {
addInputField(content);
button.remove();
};
suggestedClassesDiv.appendChild(button);
}
async function createInference(query) {
try {
const response = await fetch("/completions", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
query
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
const content = data.choices[0].message.content;
return content;
} catch (error) {
return `<p>Error: ${error.message}</p>`;
}
}
async function generateIngredients() {
// Get all input elements inside the div
const inputs = editableClassesDiv.querySelectorAll("input");
// Initialize an empty string to hold the input values
let values = "";
// Check if there are any input elements
if (inputs.length === 0) {
return; // Exit the function silently if there are no input elements
}
// Loop through the input elements and add their values to the string
inputs.forEach((input) => {
if (values !== "") {
values += ", ";
}
values += input.value;
});
// Alert the input values
const output = await createInference("Make 4 recipes based on the following exclusive ingredients:" + values);
// Create a new div element to hold the output
const outputDiv = document.createElement("div");
outputDiv.innerHTML = format(output);
console.log(output);
// Add the output div to the page
document.body.appendChild(outputDiv);
}
function format(markdown) {
// Replace newlines with br tags
var formatted = markdown.replace(/\n/g, '<br>');
// Replace bold with b tags
const boldRegex = /\*\*(.+?)\*\*/g;
const boldMatches = formatted.match(boldRegex);
if (boldMatches) {
boldMatches.forEach(match => {
const boldText = match.replace(boldRegex, '<b>$1</b>');
formatted = formatted.replace(match, boldText);
});
}
// Replace italics with i tags
const italicRegex = /\*(.+?)\*/g;
const italicMatches = formatted.match(italicRegex);
if (italicMatches) {
italicMatches.forEach(match => {
const italicText = match.replace(italicRegex, '<i>$1</i>');
formatted = formatted.replace(match, italicText);
});
}
// Replace underline with u tags
const underlineRegex = /__(.+?)__/g;
const underlineMatches = formatted.match(underlineRegex);
if (underlineMatches) {
underlineMatches.forEach(match => {
const underlineText = match.replace(underlineRegex, '<u>$1</u>');
formatted = formatted.replace(match, underlineText);
});
}
return formatted;
}