UNPKG

modsure

Version:

Check user content against your site rules using Jev or Laya.

243 lines (179 loc) • 10.6 kB
# modsure Moderate posts, comments, and other user-written text against your site's rules using Jev or Laya. **Someone clicks Post → modsure checks your rules → your site publishes the post or explains what needs to change.** Zero runtime or development dependencies. Native `fetch`, ESM JavaScript, TypeScript declarations, Node.js 22+. Runs on your server with your own TypeSafe API key or a loaded Laya client. ## 1. Install Install modsure in your application: ```sh npm install modsure ``` These examples use ES modules. Set `"type": "module"` in your application's `package.json`, or use `.mjs` file extensions and update the imports accordingly. ## 2. Add your API key Get your own TypeSafe API key from [console.typesafe.ai](https://console.typesafe.ai). Create a `.env` file in your application's root: ```dotenv TYPESAFE_API_KEY=your_api_key ``` Add `.env` to `.gitignore`. On your hosting platform, set `TYPESAFE_API_KEY` in its environment settings. Keep the key and moderation code on your server, not in browser code. ## 3. Write your rules in one config file Create `modsure.config.js` in your application's root. This file keeps the API-key environment variable, settings, and rules together: ```js export default { apiKey: process.env.TYPESAFE_API_KEY, threshold: 0.7, rules: [ { id: 'no-spam', description: 'No unsolicited advertising or promotional links.', message: 'Please remove advertising or promotional links before posting.', }, { id: 'be-respectful', description: 'No personal attacks, harassment, or threats against other people.', message: 'Please remove personal attacks, harassment, or threats.', }, { id: 'respect-privacy', description: 'Do not share another person’s private contact details without permission.', message: 'Please remove other people’s private information.', }, ], }; ``` - `id`: a unique name for the rule, returned when it is flagged. - `description`: the rule the model checks, written in plain language. - `message`: what your site can show the author if the rule is flagged. Defaults to `description`. - `threshold`: the violation probability that triggers rejection. Here, `0.7` means 70% or higher. A rule can set its own `threshold` to override this value. The library's default threshold is `0.5` if you omit it. Try your rules against representative posts and adjust the threshold for your community. ## 4. Check text before publishing Create `moderation.js` next to your config: ```js import { createModerator } from 'modsure'; import config from './modsure.config.js'; // Create once and reuse for incoming posts. export const moderator = createModerator(config); ``` In your server's post handler: ```js import { ModerationError } from 'modsure'; import { moderator } from './moderation.js'; // `text` is the submitted post from your request handler. try { const result = await moderator.moderate(text); if (result.allowed) { // Your application saves/publishes this exact text here. } else { // Leave the post unpublished and return these reasons to the author. const reasons = result.violations.map(({ id, message }) => ({ rule: id, message, })); console.log(reasons); } } catch (error) { if (!(error instanceof ModerationError)) throw error; // No decision was made. Keep the draft and let the author retry. // Your application should return an unavailable response, not publish. console.error(error.code); } ``` Load `.env` when starting your server, replacing `server.js` with your application's entry point: ```sh node --env-file=.env server.js ``` If your framework already loads `.env`, use its normal start command. Modsure does not automatically load `.env` or discover `modsure.config.js`: your application loads the environment and imports the config explicitly. **Modsure returns decisions; your application handles publishing and displaying feedback.** Any flagged rule makes `allowed` false. Errors never count as an approved post. Invalid configuration or empty text raises `TypeError`. Keep the rules under your site's control. With Jev, post text and rule descriptions are sent to TypeSafe; with Laya, they are passed to your local client; modsure does not store or log them. ## Try a complete example With the config and `moderation.js` above, create `check-post.js`: ```js import { moderator } from './moderation.js'; const text = process.argv[2] ?? 'Thanks for writing this helpful article!'; const result = await moderator.moderate(text); console.log(JSON.stringify(result, null, 2)); ``` ```sh node --env-file=.env check-post.js 'Thanks for writing this helpful article!' ``` This makes a real API call using your key. For a full social-feed example, use the repository's `demo/` directory. ## Use Laya locally [Laya](https://github.com/NandhaKishorM/laya) is an open-weight decision model. Modsure supports the independent [Node.js ONNX port, @receptron/laya](https://github.com/receptron/laya). Install it only if you want local inference: ```sh npm install @receptron/laya ``` Load the client once at server startup and reuse your existing rules: ```js import { Laya } from '@receptron/laya'; import { createModerator } from 'modsure'; import config from './modsure.config.js'; const laya = await Laya.load(); const moderator = createModerator({ provider: 'laya', laya, rules: config.rules, threshold: config.threshold, }); try { const result = await moderator.moderate('Thanks for the helpful article!'); console.log(result); } finally { // For a long-running server, close only at shutdown after requests finish. await laya.close(); } ``` No TypeSafe key is needed. Modsure calls `systemOne(content, questions)` once per moderation request and validates the same Noul answers, thresholds, and result format as Jev. The returned `model` comes from the client. Modsure does not download, load, or close models. The Node port downloads roughly 1.7 GB of weights on first load; `Laya.load({ modelDir: './onnx' })` uses an existing local bundle. Select checkpoints and runtime options through `Laya.load`, rather than modsure's Jev-only `model` option. The optional runtime has its own dependencies; modsure remains dependency-free. Timeouts and cancellation stop waiting for Laya but cannot interrupt its underlying inference. Wait for outstanding inference before closing the client. The port can truncate input to its checkpoint's context limit (512 tokens for the English checkpoint, including the question header). Evaluate your rules on representative content, including long posts, before using its decisions to publish content; Jev thresholds are not automatically calibrated for Laya. ## Rules and decisions Each rule needs a unique `id` and a plain-language `description` of the site's policy. Optional `message` is the explanation shown to the author; it defaults to the description. Write one clear requirement per rule. For Jev, the library sends one Noul question per rule in a single request to [TypeSafe's Jev API](https://docs.typesafe.ai/introduction/quickstart). A [Noul answer](https://docs.typesafe.ai/primitives/noul) is the probability that the content violates that rule. A rule is flagged when its probability is **greater than or equal to** its threshold. If any rule is flagged, `allowed` is false. Both providers return judgments, not generated explanations. Rejection messages come from your configured rules, so authors see your wording. These are model judgments and can be wrong; test rules and thresholds against representative posts before relying on them. ```js // Example result; probabilities depend on the model's response. { allowed: false, model: 'jev-1.13.0', violations: [{ id: 'no-spam', description: 'No unsolicited advertising or promotional links.', message: 'Please remove advertising or promotional links before posting.', threshold: 0.5, probability: 0.94, violated: true }], checks: [/* every evaluated rule, using the same fields */] } ``` ## Configuration | Option | Default | Meaning | | --- | --- | --- | | `provider` | `jev` | `jev` for the hosted API or `laya` for local inference | | `apiKey` | Required for Jev | Your TypeSafe API key | | `laya` | Required for Laya | Loaded client exposing `systemOne(state, questions)` | | `rules` | Required | Nonempty array of site rules | | `threshold` | `0.5` | Default violation threshold, between 0 and 1 | | `rules[].threshold` | Global threshold | Override for a particular rule | | `model` | `jev-latest` | Jev-only model identifier; select Laya checkpoints when loading the client | | `timeoutMs` | `10000` | Deadline for the API response or waiting for local inference | | `fetch` | Native `fetch` | Optional compatible transport, useful for tests | Raising a threshold requires a higher violation probability to reject. A threshold of 0 flags every answer; 1 flags only an answer of exactly 1. The default is a starting point, not a calibrated policy for every community. Rules are copied when the moderator is created; create a new moderator when policy changes. `moderator.moderate(text, { signal })` accepts an optional `AbortSignal`. Empty or whitespace-only text and invalid configuration raise `TypeError`. Service failures throw `ModerationError`; they never return an accept/reject result: | `code` | Meaning | | --- | --- | | `HTTP_ERROR` | Provider returned an error; `status` contains the HTTP status | | `NETWORK_ERROR` | Jev network/transport failure | | `INFERENCE_ERROR` | Laya client failed during inference | | `TIMEOUT` | Request exceeded its deadline | | `ABORTED` | Caller cancelled the request | | `INVALID_RESPONSE` | Invalid JSON, malformed data, or a missing/invalid rule answer | There are no automatic retries or hidden additional calls. Your site controls retries and how an unavailable moderation service affects posting. Jev limits and billing are controlled by your TypeSafe account. Laya compute and model management belong to your application. ## Development A working social-feed demo and browser recording instructions are in [demo/README.md](demo/README.md). Run `npm run demo` after adding your API key to `.env` to try it locally. ```sh npm test npm pack --dry-run TYPESAFE_API_KEY=your-key node examples/moderate.js 'Text to check' ``` Tests use mocked HTTP responses and require no API key. The example makes a real, billable API request. The package has no build step. ## License MIT. See [LICENSE](LICENSE).