diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index 383983316f..83cbed66df 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -44,6 +44,10 @@ assign_issues_by: - "api: parametermanager" to: - GoogleCloudPlatform/cloud-parameters-team +- labels: + - "api: modelarmor" + to: + - GoogleCloudPlatform/cloud-modelarmor-team assign_prs_by: - labels: @@ -77,3 +81,7 @@ assign_prs_by: - "api: parametermanager" to: - GoogleCloudPlatform/cloud-parameters-team +- labels: + - "api: modelarmor" + to: + - GoogleCloudPlatform/cloud-modelarmor-team diff --git a/CODEOWNERS b/CODEOWNERS index e2e4a75e55..bba35081d8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -44,6 +44,7 @@ document-warehouse @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPla ai-platform @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/text-embedding @GoogleCloudPlatform/cloud-samples-reviewers asset @GoogleCloudPlatform/cloud-asset-analysis-team @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers dlp @GoogleCloudPlatform/googleapis-dlp @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers +model-armor @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers @GoogleCloudPlatform/cloud-modelarmor-team security-center @GoogleCloudPlatform/gcp-security-command-center @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers retail @GoogleCloudPlatform/cloud-retail-team @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers media @GoogleCloudPlatform/cloud-media-team @GoogleCloudPlatform/nodejs-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers diff --git a/model-armor/package.json b/model-armor/package.json new file mode 100644 index 0000000000..40abac10c5 --- /dev/null +++ b/model-armor/package.json @@ -0,0 +1,26 @@ +{ + "name": "nodejs-model-armor-samples", + "private": true, + "license": "Apache-2.0", + "files": [ + "*.js" + ], + "author": "Google LLC", + "repository": "googleapis/nodejs-model-armor", + "engines": { + "node": ">=16.0.0" + }, + "scripts": { + "test": "c8 mocha -p -j 2 --recursive test/ --timeout=60000" + }, + "dependencies": { + "@google-cloud/modelarmor": "^0.1.0" + }, + "devDependencies": { + "c8": "^10.0.0", + "chai": "^4.5.0", + "mocha": "^10.0.0", + "uuid": "^10.0.0" + } +} + \ No newline at end of file diff --git a/model-armor/snippets/quickstart.js b/model-armor/snippets/quickstart.js new file mode 100644 index 0000000000..c5bf4fb3fe --- /dev/null +++ b/model-armor/snippets/quickstart.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'; + +/** + * Quickstart example for using Google Cloud Model Armor to + * create a template with RAI filters and sanitize content. + * + * @param {string} projectId - Google Cloud project ID. + * @param {string} locationId - Google Cloud location. + * @param {string} templateId - ID for the template to create. + */ +async function quickstart( + projectId = 'my-project', + locationId = 'us-central1', + templateId = 'my-template' +) { + // [START modelarmor_quickstart] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const locationId = 'us-central1'; + // const templateId = 'my-template'; + + // Imports the Model Armor library + const modelarmor = require('@google-cloud/modelarmor'); + const {ModelArmorClient} = modelarmor.v1; + const {protos} = modelarmor; + + const {RaiFilterType} = protos.google.cloud.modelarmor.v1; + const {DetectionConfidenceLevel} = protos.google.cloud.modelarmor.v1; + + // Instantiates a client + const client = new ModelArmorClient({ + apiEndpoint: `modelarmor.${locationId}.rep.googleapis.com`, + }); + + const parent = `projects/${projectId}/locations/${locationId}`; + + // Build the Model Armor template with preferred filters + // For more details on filters, refer to: + // https://cloud.google.com/security-command-center/docs/key-concepts-model-armor#ma-filters + const template = { + filterConfig: { + raiSettings: { + raiFilters: [ + { + filterType: RaiFilterType.DANGEROUS, + confidenceLevel: DetectionConfidenceLevel.HIGH, + }, + { + filterType: RaiFilterType.HARASSMENT, + confidenceLevel: DetectionConfidenceLevel.MEDIUM_AND_ABOVE, + }, + { + filterType: RaiFilterType.HATE_SPEECH, + confidenceLevel: DetectionConfidenceLevel.HIGH, + }, + { + filterType: RaiFilterType.SEXUALLY_EXPLICIT, + confidenceLevel: DetectionConfidenceLevel.HIGH, + }, + ], + }, + }, + }; + + const [createdTemplate] = await client.createTemplate({ + parent, + templateId, + template, + }); + + // Sanitize a user prompt using the created template + const userPrompt = 'Unsafe user prompt'; + + const [userPromptSanitizeResponse] = await client.sanitizeUserPrompt({ + name: `projects/${projectId}/locations/${locationId}/templates/${templateId}`, + userPromptData: { + text: userPrompt, + }, + }); + + // Sanitize a model response using the created template + const modelResponse = 'Unsanitized model output'; + + const [modelSanitizeResponse] = await client.sanitizeModelResponse({ + name: `projects/${projectId}/locations/${locationId}/templates/${templateId}`, + modelResponseData: { + text: modelResponse, + }, + }); + + return { + templateName: createdTemplate.name, + userPromptSanitizeResponse, + modelSanitizeResponse + }; + // [END modelarmor_quickstart] +} + +module.exports = quickstart; diff --git a/model-armor/test/.eslintrc.yml b/model-armor/test/.eslintrc.yml new file mode 100644 index 0000000000..9351c489b5 --- /dev/null +++ b/model-armor/test/.eslintrc.yml @@ -0,0 +1,17 @@ +# 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. + +--- +env: + mocha: true \ No newline at end of file diff --git a/model-armor/test/modelarmor.test.js b/model-armor/test/modelarmor.test.js new file mode 100644 index 0000000000..e31cebf730 --- /dev/null +++ b/model-armor/test/modelarmor.test.js @@ -0,0 +1,67 @@ +// Copyright 2023 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 cp = require('child_process'); +const {v4: uuidv4} = require('uuid'); +const {ModelArmorClient} = require('@google-cloud/modelarmor').v1; + +let projectId; +const locationId = process.env.GCLOUD_LOCATION || 'us-central1'; +const options = { + apiEndpoint: `modelarmor.${locationId}.rep.googleapis.com`, +}; + +const client = new ModelArmorClient(options); +const templateIdPrefix = `test-template-${uuidv4().substring(0, 8)}`; + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('Model Armor tests', () => { + const templatesToDelete = []; + + before(async () => { + projectId = await client.getProjectId(); + }); + + after(async () => { + // Clean up all templates + for (const templateName of templatesToDelete) { + try { + await client.deleteTemplate({name: templateName}); + console.log(`Cleaned up template: ${templateName}`); + } catch (error) { + console.error(`Failed to delete template ${templateName}:`, error); + } + } + }); + + // =================== Quickstart Tests =================== + + it('should create a template and sanitize content', async () => { + const quickstart = require('../snippets/quickstart'); + const testQuickstartTemplateId = `${templateIdPrefix}-quickstart`; + + await quickstart( + projectId, + locationId, + testQuickstartTemplateId + ); + + templatesToDelete.push( + `projects/${projectId}/locations/${locationId}/templates/${testQuickstartTemplateId}` + ); + }); +});