diff --git a/.d.ts b/.d.ts new file mode 100644 index 00000000..48c877c6 --- /dev/null +++ b/.d.ts @@ -0,0 +1 @@ +declare module 'washyourmouthoutwithsoap'; diff --git a/package-lock.json b/package-lock.json index 7d0a0313..f44c091b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@types/mustache": "^4.2.5", "async-retry": "^1.3.3", "hono": "^3.12.0", + "washyourmouthoutwithsoap": "^1.0.2", "zod": "^3.22.4" }, "bin": { @@ -6666,6 +6667,11 @@ "makeerror": "1.0.12" } }, + "node_modules/washyourmouthoutwithsoap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/washyourmouthoutwithsoap/-/washyourmouthoutwithsoap-1.0.2.tgz", + "integrity": "sha512-N008S6j3VoPgjUwvW1Q2roTYg9Xuy1LfR/R4BdjddhzM8YBNgIR4FpSQ9zVmNkvpi/fS9cfAQLSjWKXE3wQ1zA==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 485ec436..2b541fb9 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@types/mustache": "^4.2.5", "async-retry": "^1.3.3", "hono": "^3.12.0", + "washyourmouthoutwithsoap": "^1.0.2", "zod": "^3.22.4" }, "devDependencies": { diff --git a/plugins/.d.ts b/plugins/.d.ts new file mode 100644 index 00000000..f20d18af --- /dev/null +++ b/plugins/.d.ts @@ -0,0 +1 @@ +declare module 'washyourmouthoutwithsoap'; \ No newline at end of file diff --git a/plugins/default/manifest.json b/plugins/default/manifest.json index 3f62d45d..6f4aa315 100644 --- a/plugins/default/manifest.json +++ b/plugins/default/manifest.json @@ -464,6 +464,20 @@ } }, { + "name": "NSFW Checker", + "id": "nsfwChecker", + "type": "guardrail", + "supportedHooks": ["afterRequestHook"], + "description": [ + { + "type": "subHeading", + "text": "Checks if the content contains NSFW words." + } + ], + "parameters": { + "type": "object", + "properties": {} + }, "name": "Lowercase check", "id": "alllowercase", "type": "guardrail", diff --git a/plugins/index.ts b/plugins/index.ts index 1137e777..0196562f 100644 --- a/plugins/index.ts +++ b/plugins/index.ts @@ -16,21 +16,7 @@ import { handler as portkeymoderateContent } from './portkey/moderateContent'; import { handler as portkeylanguage } from './portkey/language'; import { handler as portkeypii } from './portkey/pii'; import { handler as portkeygibberish } from './portkey/gibberish'; -import { handler as aporiavalidateProject } from './aporia/validateProject'; -import { handler as sydelabssydeguard } from './sydelabs/sydeguard'; -import { handler as pillarscanPrompt } from './pillar/scanPrompt'; -import { handler as pillarscanResponse } from './pillar/scanResponse'; -import { handler as patronusphi } from './patronus/phi'; -import { handler as patronuspii } from './patronus/pii'; -import { handler as patronusisConcise } from './patronus/isConcise'; -import { handler as patronusisHelpful } from './patronus/isHelpful'; -import { handler as patronusisPolite } from './patronus/isPolite'; -import { handler as patronusnoApologies } from './patronus/noApologies'; -import { handler as patronusnoGenderBias } from './patronus/noGenderBias'; -import { handler as patronusnoRacialBias } from './patronus/noRacialBias'; -import { handler as patronusretrievalAnswerRelevance } from './patronus/retrievalAnswerRelevance'; -import { handler as patronustoxicity } from './patronus/toxicity'; -import { handler as patronuscustom } from './patronus/custom'; +import { handler as nsfwCheckernsfwChecker } from './nsfw/nsfwChecker'; export const plugins = { default: { @@ -55,27 +41,7 @@ export const plugins = { pii: portkeypii, gibberish: portkeygibberish, }, - aporia: { - validateProject: aporiavalidateProject, - }, - sydelabs: { - sydeguard: sydelabssydeguard, - }, - pillar: { - scanPrompt: pillarscanPrompt, - scanResponse: pillarscanResponse, - }, - patronus: { - phi: patronusphi, - pii: patronuspii, - isConcise: patronusisConcise, - isHelpful: patronusisHelpful, - isPolite: patronusisPolite, - noApologies: patronusnoApologies, - noGenderBias: patronusnoGenderBias, - noRacialBias: patronusnoRacialBias, - retrievalAnswerRelevance: patronusretrievalAnswerRelevance, - toxicity: patronustoxicity, - custom: patronuscustom, + nsfw: { + nsfwChecker: nsfwCheckernsfwChecker, }, }; diff --git a/plugins/nsfw/manifest.json b/plugins/nsfw/manifest.json new file mode 100644 index 00000000..1a5e81ac --- /dev/null +++ b/plugins/nsfw/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "nsfw", + "id": "nsfwChecker", + "functions": [ + { + "name": "nsfw", + "id": "nsfwChecker", + "supportedHooks": ["afterRequestHook"], + "type": "guardrail", + "description": [ + { + "type": "subHeading", + "text": "Checks the response for NSFW content." + } + ] + } + ] +} diff --git a/plugins/nsfw/nsfw.tests.ts b/plugins/nsfw/nsfw.tests.ts new file mode 100644 index 00000000..6ced177e --- /dev/null +++ b/plugins/nsfw/nsfw.tests.ts @@ -0,0 +1,67 @@ +// plugins/nsfw/nsfw.tests.ts +import { handler as nsfwHandler } from './nsfwChecker'; +import { HookEventType, PluginContext, PluginParameters } from '../types'; + +describe('NSFW Handler', () => { + it('should return true for NSFW content', async () => { + const context: PluginContext = { + response: { + text: 'They are such a boob at this, they suck ass!', + }, + }; + const eventType: HookEventType = 'afterRequestHook'; + const parameters: PluginParameters = {}; + + const result = await nsfwHandler(context, parameters, eventType); + expect(result).toBeDefined(); + expect(result.verdict).toBe(true); + expect(result.error).toBeNull(); + expect(result.data).toBeDefined(); + }); + + it('should return false for non-NSFW content', async () => { + const context: PluginContext = { + response: { + text: 'This is a test string with no NSFW content', + }, + }; + const eventType: HookEventType = 'afterRequestHook'; + const parameters: PluginParameters = {}; + + const result = await nsfwHandler(context, parameters, eventType); + expect(result).toBeDefined(); + expect(result.verdict).toBe(false); + expect(result.error).toBeNull(); + expect(result.data).toBeDefined(); + }); + + it('should return true for non-NSFW content', async () => { + const context: PluginContext = { + response: { + text: 'They are such a boob at this, they suck ass!', + }, + }; + const eventType: HookEventType = 'afterRequestHook'; + const parameters: PluginParameters = {}; + + const result = await nsfwHandler(context, parameters, eventType); + expect(result).toBeDefined(); + expect(result.verdict).toBe(true); + expect(result.error).toBeNull(); + expect(result.data).toBeDefined(); + }); + + it('should return an error for missing response text', async () => { + const context: PluginContext = { + response: {}, + }; + const eventType: HookEventType = 'afterRequestHook'; + const parameters: PluginParameters = {}; + + const result = await nsfwHandler(context, parameters, eventType); + expect(result).toBeDefined(); + expect(result.error).toBeDefined(); + expect(result.verdict).toBeUndefined(); + expect(result.data).toBeNull(); + }); +}); diff --git a/plugins/nsfw/nsfwChecker.ts b/plugins/nsfw/nsfwChecker.ts new file mode 100644 index 00000000..582dedef --- /dev/null +++ b/plugins/nsfw/nsfwChecker.ts @@ -0,0 +1,24 @@ +import { + HookEventType, + PluginContext, + PluginHandler, + PluginParameters, +} from '../types'; +import { getText } from '../utils'; +import * as wash from 'washyourmouthoutwithsoap'; + +export const handler: PluginHandler = async ( + context: PluginContext, + parameters: PluginParameters, + eventType: HookEventType, + options: any +) => { + const responseText = getText(context.response, eventType); + const containsNsfw = wash.check('en', responseText); + + return { + verdict: containsNsfw, + data: null, + error: null, + }; +};