diff --git a/Demos/PaliGemma2-on-Web/README.md b/Demos/PaliGemma2-on-Web/README.md new file mode 100644 index 0000000..40019f2 --- /dev/null +++ b/Demos/PaliGemma2-on-Web/README.md @@ -0,0 +1,41 @@ +### Developed by [Nitin Tiwari](https://linkedin.com/in/tiwari-nitin). + +# Inference PaliGemma 2 on the browser with ONNX & Transformers.js +This project is an implementation of inferencing the paligemma2-3b-mix-224 model on the browser using its converted ONNX weights and Hugging Face Transformers.js. + +## PaliGemma 2 to ONNX Conversion: +![Logo](assets/paligemma2-onnx-pipeline.png) + + +## Steps to run: + +1. Clone the repository on your local machine. +2. Navigate to `gemma-cookbook/Demos/PaliGemma2-on-Web` directory. +3. Run `npm install` to install the Node.js packages. +4. Run `node server.js` to start the server. +5. Open `localhost:3000` on your web browser and start inferencing with PaliGemma 2. + +> [!NOTE] +> For the first time, it will take around 10-15 minutes to load the model weights. + +## Results: +![Logo](assets/paligemma2-onnx-output.gif) + + +## Resources & References + +1. [Google DeepMind PaliGemma 2](https://developers.googleblog.com/en/introducing-paligemma-2-mix/) +2. Colab Notebooks: + + + + + + + + + +
Convert and quantize PaliGemma 2 to ONNXOpen In Colab
Inference PaliGemma 2 with Transformers.jsOpen In Colab
+ +3. [**Medium Blog**](https://medium.com/@tiwarinitin1999/inference-paligemma-2-with-transformers-js-5545986ac14a) for step-by-step implementation. +4. [ONNX Community](https://huggingface.co/onnx-community) diff --git a/Demos/PaliGemma2-on-Web/assets/paligemma2-onnx-output.gif b/Demos/PaliGemma2-on-Web/assets/paligemma2-onnx-output.gif new file mode 100644 index 0000000..086ac7f Binary files /dev/null and b/Demos/PaliGemma2-on-Web/assets/paligemma2-onnx-output.gif differ diff --git a/Demos/PaliGemma2-on-Web/assets/paligemma2-onnx-pipeline.png b/Demos/PaliGemma2-on-Web/assets/paligemma2-onnx-pipeline.png new file mode 100644 index 0000000..d06ebe3 Binary files /dev/null and b/Demos/PaliGemma2-on-Web/assets/paligemma2-onnx-pipeline.png differ diff --git a/Demos/PaliGemma2-on-Web/package.json b/Demos/PaliGemma2-on-Web/package.json new file mode 100644 index 0000000..d413577 --- /dev/null +++ b/Demos/PaliGemma2-on-Web/package.json @@ -0,0 +1,28 @@ +{ + "name": "paligemma2-onnx-transformers.js", + "version": "1.0.0", + "main": "server.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "keywords": [], + "author": "Nitin Tiwari", + "license": "MIT", + "description": "Inference PaliGemma 2 on the browser using ONNX weights, and Transformers.js.", + "repository": { + "type": "git", + "url": "git+https://github.com/NSTiwari/PaliGemma2-ONNX-Transformers.js.git" + }, + "bugs": { + "url": "https://github.com/NSTiwari/PaliGemma2-ONNX-Transformers.js/issues" + }, + "homepage": "https://github.com/NSTiwari/PaliGemma2-ONNX-Transformers.js#readme", + "dependencies": { + "@huggingface/transformers": "^3.3.3", + "canvas": "^3.1.0", + "express": "^4.21.2", + "server.js": "^1.0.0" + } +} diff --git a/Demos/PaliGemma2-on-Web/public/index.html b/Demos/PaliGemma2-on-Web/public/index.html new file mode 100644 index 0000000..bc5fa73 --- /dev/null +++ b/Demos/PaliGemma2-on-Web/public/index.html @@ -0,0 +1,41 @@ + + + + + + + Inference PaliGemma 2 with 🤗 Transformers.js + + + + +
+
+

Inference PaliGemma 2 with 🤗 Transformers.js

+
+ +
+
+ + +
+
+ +
+
+ + +
+ + +
+ +
+ +
+
+ + + + + diff --git a/Demos/PaliGemma2-on-Web/public/script.js b/Demos/PaliGemma2-on-Web/public/script.js new file mode 100644 index 0000000..fc60e43 --- /dev/null +++ b/Demos/PaliGemma2-on-Web/public/script.js @@ -0,0 +1,186 @@ +document.addEventListener('DOMContentLoaded', () => { + const imageUpload = document.getElementById('imageUpload'); + const processButton = document.getElementById('processButton'); + const originalImage = document.getElementById('originalImage'); + const processedCanvas = document.getElementById('processedCanvas'); + const promptInput = document.getElementById('promptInput'); + const responseTextDiv = document.getElementById('responseText'); + const ctx = processedCanvas.getContext('2d'); + + let imageBase64 = ''; + let originalImageURL = ''; + let originalImageObj; + + // Initially hide the original image + originalImage.style.display = 'none'; + + // Handle image upload + imageUpload.addEventListener('change', (event) => { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + imageBase64 = e.target.result.split(',')[1]; + originalImageURL = e.target.result; + originalImage.src = originalImageURL; + + originalImageObj = new Image(); + originalImageObj.onload = () => { + // Keep aspect ratio while scaling + const originalWidth = originalImageObj.width; + const originalHeight = originalImageObj.height; + + // Maximum dimensions for the display area + const maxWidth = 600; + const maxHeight = 400; + + // Calculate aspect ratio + const aspectRatio = originalWidth / originalHeight; + + let displayWidth = maxWidth; + let displayHeight = maxWidth / aspectRatio; + + if (displayHeight > maxHeight) { + displayHeight = maxHeight; + displayWidth = maxHeight * aspectRatio; + } + + processedCanvas.width = displayWidth; + processedCanvas.height = displayHeight; + ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height); + ctx.drawImage(originalImageObj, 0, 0, displayWidth, displayHeight); // Draw the original image on the canvas + + // Show the original image after it's loaded for preview + originalImage.style.display = 'block'; + }; + originalImageObj.src = originalImageURL; + + // Enable the process button + processButton.disabled = false; + responseTextDiv.style.display = 'none'; + responseTextDiv.innerHTML = ''; + promptInput.value = ''; + }; + reader.readAsDataURL(file); + } + }); + + // Handle process button click + processButton.addEventListener('click', async () => { + if (!imageBase64) { + alert("Please upload an image first."); + return; + } + + // Clear off previous results. + ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height); + responseTextDiv.innerHTML = 'Analyzing...'; + responseTextDiv.style.display = 'block'; + + + let prompt = promptInput.value || ""; + if (prompt.toLowerCase().includes("detect")) { + const labelMatch = prompt.match(/detect\s+(.*)/i); + const label = labelMatch ? labelMatch[1] : 'Unknown'; + prompt = `detect ${label}`; + } else { + prompt = `${prompt}`; + } + + try { + const response = await fetch('/process-image', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ image: imageBase64, prompt: prompt, targetWidth: processedCanvas.width, targetHeight: processedCanvas.height, originalWidth: originalImageObj.width, originalHeight: originalImageObj.height }), + }); + + if (response.ok) { + const data = await response.json(); + + if (data.success) { + if (prompt.includes("detect")) { + const { boundingBox } = data; + drawBoundingBox(boundingBox, ctx) + + responseTextDiv.style.display = 'block'; + responseTextDiv.innerHTML = "Response: " + escapeHtml(data.message); + } + else { + processedCanvas.width = 0; + processedCanvas.height = 0; + ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height); + responseTextDiv.style.display = 'block'; + responseTextDiv.innerHTML = data.message; + } + } + else { + processedCanvas.width = 0; + processedCanvas.height = 0; + ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height); + responseTextDiv.style.display = 'block'; + responseTextDiv.innerHTML = "Response: " + data.message; + } + } + else { + alert('Error processing image.'); + } + + } catch (error) { + console.error('Error:', error); + alert('Error processing image.'); + } finally { + + } + }); + + + // Function to draw the bounding box on canvas + function drawBoundingBox(boundingBox, ctx) { + + const { x1, y1, x2, y2, label } = boundingBox; + + // Generate random color for the bounding box and label background + const randomColor = getRandomColor(); + + // Set styles for the bounding box (random color stroke) + ctx.strokeStyle = randomColor; + ctx.lineWidth = 5; + ctx.strokeRect(x1, y1, x2 - x1, y2 - y1); + + // Adjust label background height to fit the text properly + const labelPadding = 10; + const textWidth = ctx.measureText(label.charAt(0).toUpperCase() + label.slice(1)).width; + const labelWidth = textWidth * 3; + const labelHeight = 30; + const labelY = y1 - labelHeight; + + // Draw background for the label (same random color as bounding box) + ctx.fillStyle = randomColor; + ctx.fillRect(x1, labelY, labelWidth, labelHeight); + + // Set the text color to white + ctx.fillStyle = "white"; + ctx.font = "bold 20px Arial"; + ctx.fillText(label.charAt(0).toUpperCase() + label.slice(1), x1 + labelPadding, labelY + labelHeight - labelPadding); + } + + // Function to generate a random RGB color + function getRandomColor() { + const r = Math.floor(Math.random() * 256); + const g = Math.floor(Math.random() * 256); + const b = Math.floor(Math.random() * 256); + return `rgb(${r},${g},${b})`; + } + + function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + +}); diff --git a/Demos/PaliGemma2-on-Web/public/style.css b/Demos/PaliGemma2-on-Web/public/style.css new file mode 100644 index 0000000..bf916ff --- /dev/null +++ b/Demos/PaliGemma2-on-Web/public/style.css @@ -0,0 +1,165 @@ +/* General Reset and Body Styling */ +body, +h1, +h2, +h3, +p, +ul, +li, +button, +input { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background-color: #f7f7f7; + color: #333; + line-height: 1.6; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + padding: 20px; +} + +/* Container Styling */ +.container { + width: 100%; + max-width: 800px; + background-color: #fff; + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + overflow: hidden; +} + +/* Header Styling */ +header { + background-color: #1A237E; /* Updated to the new background color */ + color: #fff; + text-align: center; + font-size: 24px; + padding: 15px; + border-bottom: 2px solid #120C55; /* Slightly darker border for contrast */ +} + +/* Image Section Styling */ +.image-section { + padding: 20px; + display: flex; + justify-content: center; + align-items: center; + min-height: 300px; + position: relative; /* Ensure it can hold both the image and canvas */ +} + +.image-container { + width: 100%; + max-width: 600px; + position: relative; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; +} + +.image-container img { + max-width: 100%; + max-height: 400px; + object-fit: contain; + display: block; + margin: 0 auto; +} + +#processedCanvas { + position: absolute; + top: 0; + left: 0; + pointer-events: none; /* Prevent the canvas from interfering with image interactions */ + width: 100%; /* Ensure canvas scales with the image */ + height: 100%; /* Match the image size */ + object-fit: contain; +} + +/* Input Section Styling */ +.input-section { + padding: 20px; + text-align: center; + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: -20px; +} + +.file-upload { + display: inline-block; + border: 1px solid #ccc; + background-color: #f2f2f2; + padding: 10px 15px; + border-radius: 6px; + cursor: pointer; + position: relative; + width: 100%; + margin-right: 10px; + box-sizing: border-box; +} + +.file-upload input[type=file] { + position: absolute; + top: 0; + left: 0; + width: 100%; /* Take up the full width */ + height: 100%; /* Take up the full height */ + opacity: 0; /* Make it invisible */ + cursor: pointer; +} + +/* Text Input Styling */ +.input-section input[type="text"] { + padding: 10px; + border: 1px solid #ddd; + border-radius: 6px; + font-size: 19px; + width: 100%; + box-sizing: border-box; +} + +/* Detect Button Styling */ +.input-section button { + background-color: #2979FF; /* Default button color */ + border: none; + color: white; + font-size: 20px; + padding: 10px 15px; + text-align: center; + text-decoration: none; + display: inline-block; + cursor: pointer; + border-radius: 6px; + transition: background-color 0.3s ease; +} + +/* Hover effect for the buttons */ +.input-section button:hover { + background-color: #2196F3; /* Darker blue on hover */ +} + +/* Status Section Styling */ +.status-section { + text-align: center; + padding: 12px; +} + + +#responseText { + font-size: 18px; + color: #263238; + font-weight: bold; + margin-top: 5px; +} diff --git a/Demos/PaliGemma2-on-Web/server.js b/Demos/PaliGemma2-on-Web/server.js new file mode 100644 index 0000000..ef60b9a --- /dev/null +++ b/Demos/PaliGemma2-on-Web/server.js @@ -0,0 +1,144 @@ +import express from 'express'; +import { AutoProcessor, PaliGemmaForConditionalGeneration, load_image } from "@huggingface/transformers"; +import { createCanvas, loadImage } from "canvas"; // Import canvas for drawing +import fs from "fs"; +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); +const path = require('path'); + +import { Buffer } from 'node:buffer'; +global.Buffer = Buffer; + +// Function to generate a random RGB color +function getRandomColor() { + const r = Math.floor(Math.random() * 256); + const g = Math.floor(Math.random() * 256); + const b = Math.floor(Math.random() * 256); + return `rgb(${r},${g},${b})`; +} + +// Load processor and model +const model_id = "NSTiwari/paligemma2-3b-mix-224-onnx"; // Change this to use a different PaliGemma model +const processor = await AutoProcessor.from_pretrained(model_id); +const model = await PaliGemmaForConditionalGeneration.from_pretrained( + model_id, + { + dtype: { + embed_tokens: "fp16", // or 'fp16' + vision_encoder: "fp16", // or 'q4', 'fp16' + decoder_model_merged: "q4", // or 'q4f16' + }, + } +); +console.log("Model and processor loaded successfully."); + +const app = express(); +const port = 3000; + +// Serve static files from the 'public' directory +app.use(express.static('public')); +app.use(express.json({ limit: '10mb' })); // Increase limit if needed for large images + +// API endpoint for image processing +app.post('/process-image', async (req, res) => { + try { + const base64Image = req.body.image; + const prompt = req.body.prompt || "caption en"; // Default prompt if not sent + const targetWidth = req.body.targetWidth; //Added targetWidth from body to get the targetWidth + const targetHeight = req.body.targetHeight; //Added targetHeight from body to get the targetHeight + const originalWidth = req.body.originalWidth; //Add the originalWidth + const originalHeight = req.body.originalHeight; //Add the originalHeight + + // Create a buffer from the base64 image data + const buffer = Buffer.from(base64Image, 'base64'); + + // Save the buffer to disk as a JPEG file + const tempImagePath = path.join(process.cwd(), 'temp_image.jpg'); // Use a unique filename for each image + fs.writeFileSync(tempImagePath, buffer); + + // Load the image + const raw_image = await loadImage(tempImagePath); + + // Resize the image using canvas + const canvas = createCanvas(targetWidth, targetHeight); + const ctx = canvas.getContext('2d'); + ctx.drawImage(raw_image, 0, 0, targetWidth, targetHeight); + + // Convert canvas to a data URL + const resizedImageDataURL = canvas.toDataURL('image/jpeg'); + + // Extract base64 from resized image + const resizedBase64Image = resizedImageDataURL.split(',')[1]; + const resizedBuffer = Buffer.from(resizedBase64Image, 'base64'); + const resizedTempImagePath = path.join(process.cwd(), 'resized_temp_image.jpg'); + fs.writeFileSync(resizedTempImagePath, resizedBuffer); + + // Load the resized image with huggingface + const resizedImage = await load_image(resizedTempImagePath); + + const inputs = await processor(resizedImage, prompt); + + // Generate a response from the model + const response = await model.generate({ + ...inputs, + max_new_tokens: 100, + }); + + // Extract the generated IDs from the response + const generatedIds = response.slice(null, [inputs.input_ids.dims[1], null]); + + // Decode the generated IDs to get the answer + const decodedAnswer = processor.batch_decode(generatedIds, { + skip_special_tokens: true, + }); + + console.log("Prompt: ", prompt); + console.log("Response: ", decodedAnswer[0]); + + if (prompt.includes("detect")) { + // Parse the response to extract bounding box coordinates + const boundingBoxes = decodedAnswer[0].match(//g); + + if (boundingBoxes && boundingBoxes.length === 4) { + // Extract numbers from tags and cast to integers + const coordinates = boundingBoxes.map(tag => parseInt(tag.replace("", ""))); + const [y1, x1, y2, x2] = coordinates.map(coord => Math.floor(coord)); + + // Normalize the bounding box coordinates to the RESIZED image dimensions + const normX1 = Math.round((x1 / 1024) * targetWidth); + const normY1 = Math.round((y1 / 1024) * targetHeight); + const normX2 = Math.round((x2 / 1024) * targetWidth); + const normY2 = Math.round((y2 / 1024) * targetHeight); + + // Extract the label from the prompt + const labelMatch = prompt.match(/detect (.*)/); + const label = labelMatch ? labelMatch[1] : "Unknown"; + + // Remove the temp image file + fs.unlinkSync(tempImagePath); + fs.unlinkSync(resizedTempImagePath); + res.json({ success: true, boundingBox: { x1: normX1, y1: normY1, x2: normX2, y2: normY2, label: label}, message: decodedAnswer[0] }); + } + else { + // Remove the temp image file if the bounding box coordinates were not found + fs.unlinkSync(tempImagePath); + fs.unlinkSync(resizedTempImagePath); + res.json({ success: false, message: decodedAnswer[0] }); + } + } + else { + // Remove the temp image file if the bounding box coordinates were not found + fs.unlinkSync(tempImagePath); + fs.unlinkSync(resizedTempImagePath); + res.json({ success: false, message: decodedAnswer[0] }); + } + + } catch (error) { + console.error("Error processing image:", error); + res.status(500).json({ success: false, message: 'Error processing image' }); + } +}); + +app.listen(port, () => { + console.log(`Server listening at http://localhost:${port}`); +}); diff --git a/Demos/README.md b/Demos/README.md index e876216..e168b3c 100644 --- a/Demos/README.md +++ b/Demos/README.md @@ -14,3 +14,8 @@ This folder houses the companion notebooks for the "Build with AI" video series, | ----------------------------------------------------------- | ----------- | | [Gemma on Android](Gemma-on-Android/) | Android app to deploy fine-tuned Gemma-2B-it model using MediaPipe LLM Inference API. | | [PaliGemma on Android](PaliGemma-on-Android/) | Inference PaliGemma on Android using Hugging Face and Gradio Client API for tasks like zero-shot object detection, image captioning, and visual question-answering. | + +## Web App Demo +| Folder | Description | +| ----------------------------------------------------------- | ----------- | +| [PaliGemma 2 on Web](PaliGemma2-on-Web/) | Inference PaliGemma 2 on the web using its ONNX weights and Transformers.js for tasks like object detection, image captioning, OCR, and visual Q&A. | diff --git a/Demos/personal-code-assistant/pipet-code-agent-2/package-lock.json b/Demos/personal-code-assistant/pipet-code-agent-2/package-lock.json index 0b213cc..9fb30fb 100644 --- a/Demos/personal-code-assistant/pipet-code-agent-2/package-lock.json +++ b/Demos/personal-code-assistant/pipet-code-agent-2/package-lock.json @@ -21,7 +21,7 @@ "@vscode/test-electron": "^2.3.2", "eslint": "^8.41.0", "glob": "^8.1.0", - "mocha": "^10.2.0", + "mocha": "^10.8.2", "typescript": "^5.1.3" }, "engines": { @@ -468,10 +468,11 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -703,12 +704,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -738,10 +740,11 @@ "dev": true }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -1578,32 +1581,32 @@ } }, "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", @@ -1611,49 +1614,24 @@ }, "engines": { "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "balanced-match": "^1.0.0" } }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1661,21 +1639,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -1692,22 +1655,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } + "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -1901,6 +1853,7 @@ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } @@ -2040,10 +1993,11 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } @@ -2259,10 +2213,11 @@ } }, "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", @@ -2321,10 +2276,11 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } diff --git a/Demos/personal-code-assistant/pipet-code-agent-2/package.json b/Demos/personal-code-assistant/pipet-code-agent-2/package.json index 53e5dc2..c98b9eb 100644 --- a/Demos/personal-code-assistant/pipet-code-agent-2/package.json +++ b/Demos/personal-code-assistant/pipet-code-agent-2/package.json @@ -76,7 +76,7 @@ "@vscode/test-electron": "^2.3.2", "eslint": "^8.41.0", "glob": "^8.1.0", - "mocha": "^10.2.0", + "mocha": "^10.8.2", "typescript": "^5.1.3" }, "dependencies": {