diff --git a/genai/bounding-box/boundingbox-with-txt-img.js b/genai/bounding-box/boundingbox-with-txt-img.js new file mode 100644 index 0000000000..2eb6852c5d --- /dev/null +++ b/genai/bounding-box/boundingbox-with-txt-img.js @@ -0,0 +1,159 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START googlegenaisdk_boundingbox_with_txt_img] +const {GoogleGenAI} = require('@google/genai'); + +const {createCanvas, loadImage} = require('canvas'); +const fetch = require('node-fetch'); +const fs = require('fs'); + +const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT; +const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global'; + +async function fetchImageAsBase64(uri) { + const response = await fetch(uri); + const buffer = await response.buffer(); + return buffer.toString('base64'); +} + +async function plotBoundingBoxes(imageUri, boundingBoxes) { + console.log('Creating bounding boxes'); + const image = await loadImage(imageUri); + const canvas = createCanvas(image.width, image.height); + const ctx = canvas.getContext('2d'); + + ctx.drawImage(image, 0, 0); + + const colors = ['red', 'blue', 'green', 'orange']; + + boundingBoxes.forEach((bbox, i) => { + const [yMin, xMin, yMax, xMax] = bbox.box_2d; + + const absYMin = Math.floor((yMin / 1000) * image.height); + const absXMin = Math.floor((xMin / 1000) * image.width); + const absYMax = Math.floor((yMax / 1000) * image.height); + const absXMax = Math.floor((xMax / 1000) * image.width); + + ctx.strokeStyle = colors[i % colors.length]; + ctx.lineWidth = 4; + ctx.strokeRect(absXMin, absYMin, absXMax - absXMin, absYMax - absYMin); + + ctx.fillStyle = colors[i % colors.length]; + ctx.font = '20px Arial'; + ctx.fillText(bbox.label, absXMin + 8, absYMin + 20); + }); + + fs.writeFileSync('output.png', canvas.toBuffer('image/png')); + console.log('Saved output to file: output.png'); +} + +async function createBoundingBox( + projectId = GOOGLE_CLOUD_PROJECT, + location = GOOGLE_CLOUD_LOCATION +) { + const client = new GoogleGenAI({ + vertexai: true, + project: projectId, + location: location, + }); + + const systemInstruction = ` + Return bounding boxes as an array with labels. + Never return masks. Limit to 25 objects. + If an object is present multiple times, give each object a unique label + according to its distinct characteristics (colors, size, position, etc). + `; + + const safetySettings = [ + { + category: 'HARM_CATEGORY_DANGEROUS_CONTENT', + threshold: 'BLOCK_ONLY_HIGH', + }, + ]; + + const imageUri = + 'https://storage.googleapis.com/generativeai-downloads/images/socks.jpg'; + const base64Image = await fetchImageAsBase64(imageUri); + + const boundingBoxSchema = { + type: 'ARRAY', + description: 'List of bounding boxes for detected objects', + items: { + type: 'OBJECT', + title: 'BoundingBox', + description: 'Represents a bounding box with coordinates and label', + properties: { + box_2d: { + type: 'ARRAY', + description: + 'Bounding box coordinates in format [y_min, x_min, y_max, x_max]', + items: { + type: 'INTEGER', + format: 'int32', + }, + minItems: '4', + maxItems: '4', + }, + label: { + type: 'STRING', + description: 'Label describing the object within the bounding box', + }, + }, + required: ['box_2d', 'label'], + }, + }; + + const response = await client.models.generateContent({ + model: 'gemini-2.5-flash', + contents: [ + { + role: 'user', + parts: [ + { + text: 'Output the positions of the socks with a face. Label according to position in the image.', + }, + { + inlineData: { + data: base64Image, + mimeType: 'image/jpeg', + }, + }, + ], + }, + ], + config: { + systemInstruction: systemInstruction, + safetySettings: safetySettings, + responseMimeType: 'application/json', + temperature: 0.5, + responseSchema: boundingBoxSchema, + }, + }); + + const candidate = response.candidates[0].content.parts[0].text; + const boundingBoxes = JSON.parse(candidate); + + console.log('Bounding boxes:', boundingBoxes); + + await plotBoundingBoxes(imageUri, boundingBoxes); + return boundingBoxes; +} +// [END googlegenaisdk_boundingbox_with_txt_img] + +module.exports = { + createBoundingBox, +}; diff --git a/genai/embeddings/embeddings-docretrieval-with-txt.js b/genai/embeddings/embeddings-docretrieval-with-txt.js new file mode 100644 index 0000000000..c7b59d0385 --- /dev/null +++ b/genai/embeddings/embeddings-docretrieval-with-txt.js @@ -0,0 +1,56 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START googlegenaisdk_embeddings_docretrieval_with_txt] +const {GoogleGenAI} = require('@google/genai'); + +const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT; + +async function generateEmbeddingsForRetrieval( + projectId = GOOGLE_CLOUD_PROJECT +) { + const client = new GoogleGenAI(projectId); + + const prompt = [ + "How do I get a driver's license/learner's permit?", + "How long is my driver's license valid for?", + "Driver's knowledge test study guide", + ]; + + const response = await client.models.embedContent({ + model: 'gemini-embedding-001', + contents: prompt, + config: { + taskType: 'RETRIEVAL_DOCUMENT', // Optional + outputDimensionality: 3072, // Optional + title: "Driver's License", // Optional + }, + }); + + console.log(response); + + // Example response: + // embeddings=[ContentEmbedding(values=[-0.06302902102470398, 0.00928034819662571, 0.014716853387653828, -0.028747491538524628, ... ], + // statistics=ContentEmbeddingStatistics(truncated=False, token_count=13.0))] + // metadata=EmbedContentMetadata(billable_character_count=112) + + return response; +} +// [END googlegenaisdk_embeddings_docretrieval_with_txt] + +module.exports = { + generateEmbeddingsForRetrieval, +}; diff --git a/genai/express-mode/api-key-example.js b/genai/express-mode/api-key-example.js new file mode 100644 index 0000000000..4cfc91e2f9 --- /dev/null +++ b/genai/express-mode/api-key-example.js @@ -0,0 +1,43 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START googlegenaisdk_vertexai_express_mode] +const {GoogleGenAI} = require('@google/genai'); +const API_KEY = 'PUT HERE YOUR API KEY'; + +async function generateWithApiKey(apiKey = API_KEY) { + const client = new GoogleGenAI({ + vertexai: true, + apiKey: apiKey, + }); + + const response = await client.models.generateContentStream({ + model: 'gemini-2.5-flash', + contents: 'Explain bubble sort to me.', + }); + + console.log(response.text); + + // Example response: + // Bubble Sort is a simple sorting algorithm that repeatedly steps through the list + + return response; +} +// [END googlegenaisdk_vertexai_express_mode] + +module.exports = { + generateWithApiKey, +}; diff --git a/genai/package.json b/genai/package.json index 2c370bd447..bb6a8c465d 100644 --- a/genai/package.json +++ b/genai/package.json @@ -13,22 +13,18 @@ "test": "c8 mocha -p -j 2 --timeout 2400000 test/*.test.js test/**/*.test.js" }, "dependencies": { - "@google/genai": "1.20.0", + "@google/genai": "^0.13.0", "axios": "^1.6.2", - "google-auth-library": "^10.3.0", - "luxon": "^3.7.1", - "proxyquire": "^2.1.3", - "node-fetch": "^3.3.2", - "openai": "^5.19.1", - "supertest": "^7.0.0" + "supertest": "^7.0.0", + "canvas": "^3.1.0", + "node-fetch": "^2.7.0" }, "devDependencies": { "c8": "^10.0.0", "chai": "^4.5.0", "mocha": "^10.0.0", - "node-fetch": "^3.3.2", - "proxyquire": "^2.1.3", "sinon": "^18.0.0", - "uuid": "^10.0.0" + "uuid": "^10.0.0", + "proxyquire": "^2.1.3" } } diff --git a/genai/provisioned-throughput/provisionedthroughput-with-txt.js b/genai/provisioned-throughput/provisionedthroughput-with-txt.js new file mode 100644 index 0000000000..6463284272 --- /dev/null +++ b/genai/provisioned-throughput/provisionedthroughput-with-txt.js @@ -0,0 +1,62 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START googlegenaisdk_provisionedthroughput_with_txt] +const {GoogleGenAI} = require('@google/genai'); + +const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT; +const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global'; + +async function generateWithProvisionedThroughput( + projectId = GOOGLE_CLOUD_PROJECT, + location = GOOGLE_CLOUD_LOCATION +) { + const client = new GoogleGenAI({ + vertexai: true, + project: projectId, + location: location, + httpOptions: { + apiVersion: 'v1', + headers: { + // Options: + // - "dedicated": Use Provisioned Throughput + // - "shared": Use pay-as-you-go + // https://cloud.google.com/vertex-ai/generative-ai/docs/use-provisioned-throughput + 'X-Vertex-AI-LLM-Request-Type': 'shared', + }, + }, + }); + + const response = await client.models.generateContent({ + model: 'gemini-2.5-flash', + contents: 'How does AI work?', + }); + + console.log(response.text); + + // Example response: + // Okay, let's break down how AI works. It's a broad field, so I'll focus on the ... + // Here's a simplified overview: + // ... + + return response.text; +} + +// [END googlegenaisdk_provisionedthroughput_with_txt] + +module.exports = { + generateWithProvisionedThroughput, +}; diff --git a/genai/safety/safety-with-txt.js b/genai/safety/safety-with-txt.js new file mode 100644 index 0000000000..986f9b6781 --- /dev/null +++ b/genai/safety/safety-with-txt.js @@ -0,0 +1,115 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START googlegenaisdk_safety_with_txt] +const {GoogleGenAI} = require('@google/genai'); + +const GOOGLE_CLOUD_PROJECT = process.env.GOOGLE_CLOUD_PROJECT; +const GOOGLE_CLOUD_LOCATION = process.env.GOOGLE_CLOUD_LOCATION || 'global'; + +async function generateWithSafetySettings( + projectId = GOOGLE_CLOUD_PROJECT, + location = GOOGLE_CLOUD_LOCATION +) { + const client = new GoogleGenAI({ + vertexai: true, + project: projectId, + location: location, + }); + + const systemInstruction = 'Be as mean as possible.'; + + const prompt = + 'Write a list of 5 disrespectful things that I might say to the universe after stubbing my toe in the dark.'; + + const safetySettings = [ + { + category: 'HARM_CATEGORY_DANGEROUS_CONTENT', + threshold: 'BLOCK_LOW_AND_ABOVE', + }, + { + category: 'HARM_CATEGORY_HARASSMENT', + threshold: 'BLOCK_LOW_AND_ABOVE', + }, + { + category: 'HARM_CATEGORY_HATE_SPEECH', + threshold: 'BLOCK_LOW_AND_ABOVE', + }, + { + category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', + threshold: 'BLOCK_LOW_AND_ABOVE', + }, + ]; + + const response = await client.models.generateContent({ + model: 'gemini-2.5-flash', + contents: prompt, + config: { + systemInstruction: systemInstruction, + safetySettings: safetySettings, + }, + }); + + console.log(response.text); + console.log(response.candidates[0].finishMessage); + + for (const each of response.candidates[0].safetyRatings) { + console.log('\nCategory:', String(each.category)); + console.log('Is Blocked:', each.blocked); + console.log('Probability:', each.probability); + console.log('Probability Score:', each.probabilityScore); + console.log('Severity:', each.severity); + console.log('Severity Score:', each.severityScore); + } + + // Example response: + // + // Category: HarmCategory.HARM_CATEGORY_HATE_SPEECH + // Is Blocked: False + // Probability: HarmProbability.NEGLIGIBLE + // Probability Score: 2.547714e-05 + // Severity: HarmSeverity.HARM_SEVERITY_NEGLIGIBLE + // Severity Score: None + // + // Category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT + // Is Blocked: False + // Probability: HarmProbability.NEGLIGIBLE + // Probability Score: 3.6103818e-06 + // Severity: HarmSeverity.HARM_SEVERITY_NEGLIGIBLE + // Severity Score: None + // + // Category: HarmCategory.HARM_CATEGORY_HARASSMENT + // Is Blocked: True + // Probability: HarmProbability.MEDIUM + // Probability Score: 0.71599233 + // Severity: HarmSeverity.HARM_SEVERITY_MEDIUM + // Severity Score: 0.30782545 + // + // Category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT + // Is Blocked: False + // Probability: HarmProbability.NEGLIGIBLE + // Probability Score: 1.5624657e-05 + // Severity: HarmSeverity.HARM_SEVERITY_NEGLIGIBLE + // Severity Score: None + + return response; +} + +// [END googlegenaisdk_safety_with_txt] + +module.exports = { + generateWithSafetySettings, +}; diff --git a/genai/test/api-key-example.test.js b/genai/test/api-key-example.test.js new file mode 100644 index 0000000000..021886fe57 --- /dev/null +++ b/genai/test/api-key-example.test.js @@ -0,0 +1,49 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const proxyquire = require('proxyquire').noCallThru(); + +describe('vertexai-express-mode', () => { + it('should call generateContentStream and return the mocked response', async function () { + this.timeout(10000); + + const mockGenerateContentStreamResult = { + text: 'Bubble sort works by repeatedly swapping adjacent elements until sorted.', + }; + + class MockModels { + async generateContentStream() { + return mockGenerateContentStreamResult; + } + } + + class MockGoogleGenAI { + constructor() { + this.models = new MockModels(); + } + } + + const sample = proxyquire('../express-mode/api-key-example.js', { + '@google/genai': {GoogleGenAI: MockGoogleGenAI}, + }); + + const response = await sample.generateWithApiKey('FAKE_API_KEY'); + + assert.strictEqual(response.text, mockGenerateContentStreamResult.text); + }); +}); diff --git a/genai/test/boundingbox-with-txt-img.test.js b/genai/test/boundingbox-with-txt-img.test.js new file mode 100644 index 0000000000..fb2950e150 --- /dev/null +++ b/genai/test/boundingbox-with-txt-img.test.js @@ -0,0 +1,29 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); + +const projectId = process.env.CAIP_PROJECT_ID; +const sample = require('../bounding-box/boundingbox-with-txt-img'); + +describe('boundingbox-with-txt-img', async () => { + it('should return the bounding box', async function () { + this.timeout(100000); + const output = await sample.createBoundingBox(projectId); + assert(output.length > 0); + }); +}); diff --git a/genai/test/embeddings-docretrieval-with-txt.test.js b/genai/test/embeddings-docretrieval-with-txt.test.js new file mode 100644 index 0000000000..08d2edbc7f --- /dev/null +++ b/genai/test/embeddings-docretrieval-with-txt.test.js @@ -0,0 +1,29 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); + +const projectId = process.env.CAIP_PROJECT_ID; +const sample = require('../embeddings/embeddings-docretrieval-with-txt.js'); + +describe('embeddings-docretrieval-with-txt', async () => { + it('should return an object containing embeddings and metadata', async () => { + const generatedFileNames = + await sample.generateEmbeddingsForRetrieval(projectId); + assert.containsAllKeys(generatedFileNames, ['embeddings', 'metadata']); + }); +}); diff --git a/genai/test/provisionedthroughput-with-txt.test.js b/genai/test/provisionedthroughput-with-txt.test.js new file mode 100644 index 0000000000..f036a890d5 --- /dev/null +++ b/genai/test/provisionedthroughput-with-txt.test.js @@ -0,0 +1,29 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); + +const projectId = process.env.CAIP_PROJECT_ID; +const sample = require('../provisioned-throughput/provisionedthroughput-with-txt.js'); + +describe('provisionedthroughput-with-txt', () => { + it('should return provisioned throughput result', async function () { + this.timeout(50000); + const output = await sample.generateWithProvisionedThroughput(projectId); + assert(output.length > 0); + }); +}); diff --git a/genai/test/safety-with-txt.test.js b/genai/test/safety-with-txt.test.js new file mode 100644 index 0000000000..40fdbd2faf --- /dev/null +++ b/genai/test/safety-with-txt.test.js @@ -0,0 +1,29 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); + +const projectId = process.env.CAIP_PROJECT_ID; +const sample = require('../safety/safety-with-txt.js'); + +describe('safety-with-txt', () => { + it('should call generateContentStream with safety instructions', async function () { + this.timeout(50000); + const output = await sample.generateWithSafetySettings(projectId); + assert(output.text.length > 0); + }); +});