About
What it does
On-device content safety for an app that shows what people post, written as a local Swift package and shipped as a Granite agent package. One call writes Packages/ModerationKit: a keyword filter, an image-safety gate on a Core ML classifier the app provides, a policy the app persists, a Moderator that judges each piece of content into allow, blur or hide with the reason, a SwiftUI censor overlay and a keyword list editor, with their tests; then links it into every app target of the project in the folder.
It is one of the Rao Craft Agents: independent packages that, tagged in prompts along the life of a project, build an app of Loom's quality. moderation generalizes Loom's ModerationKit, its NSFW Shield and its keyword filter.
How an app finds it
ModerationAgent is a GraniteAgentPackage, and it carries a record in its binary:
@used @section("__DATA_CONST,__granite")
let moderationRecord: GraniteRoster.Record = { GraniteRoster.record(ModerationAgent.self) }In Craft, install it from the Agent Store or with craft agents add https://github.com/Rao-Craft-Agents/ModerationAgent.git, then say $moderation in a request:
$moderation keep spoilers and crypto spam out of the recipe feed, and blur food photos the model flagsThe model writes the call; it shows on Craft's agent map as moderation-1, and the files it names are built and tested with the turn.
What a call takes and answers
| Field | ||
|---|---|---|
name | optional | the package's name, module and folder; ModerationKit when left out |
into | optional | the package's folder, relative to the project; Packages/<name> when left out |
keywords | optional | the terms a new install filters: spoiler, buy now, crypto* |
label | optional | the image classifier's label that means unsafe; NSFW when left out |
threshold | optional | the score from 0 to 1 from which an image is blurred; 0.8 when left out |
modelURL | optional | the https URL of an .mlmodel the app downloads once, compiles and keeps |
features | optional | of keywords, images, overlay, editor; all four when left out |
link | optional | whether to link into the .xcodeproj or Package.swift in the folder; true when left out |
directory | the host's | the folder the host works in; a host fills it and does not show it to a model |
{}
{"keywords": ["spoiler", "store-bought", "crypto*"], "threshold": 0.7}
{"modelURL": "https://models.example.com/food-photo-safety.mlmodel", "label": "unsafe"}
{"features": ["keywords", "editor"], "keywords": ["spoiler"]}
{"name": "RecipeSafety", "into": "Packages/RecipeSafety", "link": false}The answer is a ModerationResult: every file written (path, paths, absolute, the project or manifest it edited among them), the package's name and folder, its parts, where it was linked, the files an earlier call wrote that this one left out (removed), and a sentence for anything it changed beside the package, like a deployment target raised.
What it writes
| Part | Files |
|---|---|
| always | Package.swift (iOS 17, macOS 14, Swift 6, no dependencies), ModerationPolicy, ModeratedContent, ModerationReason, Verdict, ModerationAction, Moderator |
keywords | KeywordFilter: every term compiled once into a tree of words and matched in one pass; whole words, any case, accents or width, phrases, * for any letters or any one word; the term, field and words of each match; KeywordRule, a term with its switch and fields |
images | ImageSafety, CoreMLImageSafety: a classifier the app bundles, keeps compiled, or downloads once from a URL, compiled on the device and kept in Application Support; run through Vision on a queue of its own; images loaded with URLSession, decoded to 512 pixels, their scores kept by URL |
overlay | censorOverlay(_:): blurred under the reason's symbol and words until a tap or a click, hidden content folded to a line with a Show button, hidden from VoiceOver while covered |
editor | KeywordListEditor: the filter's switch, what a match does, add and remove terms, each term's switch and fields |
| tests | the keyword filter, the policy, and the moderator with a stub image screen |
Moderator is an actor: judging never runs on the main thread, a verdict is kept by the content's id while the content and the policy stay the same, and an image that cannot be screened never hides content. No model ships in the package.
How the package is made
| Step | What |
|---|---|
| Read | every field checked; every problem said at once, and nothing written |
| Write | every file from a template, the call's name, terms, label, threshold and URL in it; parts left out leave no code behind |
| Link | the project file edited as text, as Xcode writes it: the package reference, a product dependency and a build file for every application target, a deployment target below iOS 17 or macOS 14 raised; or the manifest's dependency, products and platforms; a second call adds nothing |
| Check | every file staged, parsed with swiftc -parse, then moved into the project together |
| Again | the same name writes the package again and removes the parts left out; a folder holding another package is refused |
No model is asked: the same call makes the same files every time.
Testing
swift test # the record, the schema, the guide, every set of parts, both linkers, a call through the roster
MODERATION_BUILD=1 swift test # the packages built and tested, for macOS and the iOS simulator, linked into an app and a packageThe gated test builds and tests the whole package and three smaller ones, builds it for the iOS simulator, builds an app project it is linked into for macOS and iOS, and runs a tool in a package it is linked into that scores images with a real (tiny, made-for-the-test) Core ML classifier: downloaded, compiled, kept, and loaded again from the cache.
MIT licensed.
What a call takes
moderation(…)
| Field | Type | Note |
|---|---|---|
| name | string | the package's name, which is also its module, product and folder name; ModerationKit when left out |
| into | string | the package's folder, relative to the project; Packages/<name> when left out, and never inside a folder whose files join the app targets |
| keywords | [string] | the terms a new install filters, one word or phrase an item, matched as whole words in any case or accents, * for any letters: spoiler, buy now, crypto* |
| label | string | the image classifier's label that means unsafe, whose confidence is an image's score; NSFW when left out |
| threshold | number | the score from 0 to 1 from which an image is blurred; 0.8 when left out |
| modelURL | string | the https URL of an .mlmodel the app downloads once, compiles on the device and keeps; no model ships in the package |
| features | [keywords| | the parts to write, of keywords, images, overlay and editor; all four when left out (the editor brings keywords) |
| link | boolean | whether to link the package into every app target of the .xcodeproj, or the Package.swift, in the project's folder; true when left out |
Distribution
The package
- Repository
- github.com/Rao-Craft-Agents/ModerationAgent · verified on GitHub
- Product
- ModerationAgent in ModerationAgent
- Pinned to
- 38bef50
- Granite
- deb1914
Creator
@rao · rao.nyc · 0 followers · More by @rao
Ambient, Craft and Veil. New York.
Moderation
Free