ventures: refresh havre from havre-next

This commit is contained in:
github-actions[bot] committed 2026-10-17 00:13:34 +00:00
1 parent b1328774a3
commit d0c700e2ab
4 files changed
+40 -23

No files matched your search

+12 -6
View File
@@ -89,10 +89,11 @@ export const hearingPerMinute = (model) => (model && model.perSecond != null ? m
const SAFE = "Keep it safe for work: no nudity, nothing sexual, no gore. Everyone shown is an adult. Never show a real, " +
"identifiable person: no celebrities, no public figures, no one the conversation says is real; if one is asked for, show " +
"the character alone. No words, logos or watermarks in the picture.";
// for an adult character's photos and clips (adultCheck): the same rules, less the one about work, and a hard one first.
// It says who is shown, not who isn't: the words for that are the ones filters look for (and Havre's own check, underage)
export const ADULT_ONLY = "Hard rule, 18+ only: everyone shown is an adult, 18 or older, with a grown adult face and body.";
const ADULT = ADULT_ONLY + " If the conversation asks for anyone younger, or for anything that makes someone look younger, " +
// for an adult character's photos and clips (adultCheck): the same rules, less the one about work, and a hard one first,
// as the shot writer (a text model) reads it: it can be told the number. It says who is shown, not who isn't: the words
// for that are the ones filters look for (and Havre's own check, underage)
export const EIGHTEEN = "Hard rule, 18+ only: everyone shown is an adult, 18 or older, with a grown adult face and body.";
const ADULT = EIGHTEEN + " If the conversation asks for anyone younger, or for anything that makes someone look younger, " +
"leave it out and show only adults. Never show a real, identifiable person: no celebrities, no public figures, no one the " +
"conversation says is real; if one is asked for, show the character alone. No gore. No words, logos or watermarks in the " +
"picture.";
@@ -164,8 +165,13 @@ export function cleanText(text, max = 900) {
// the rules ride at the front of every picture and clip, where a long shot or a long look can't push them out. They
// say what's wanted: an image model reads the words, not the "no" in front of them, and so does a content filter
const KEEP = "Safe for work: everyone in it is an adult, fully clothed.";
// an adult character's: the 18+ rule is the very first thing the model reads
const KEEP_ADULT = ADULT_ONLY + " Never a real, identifiable person.";
// an adult character's: the 18+ rule is the very first thing the picture model reads, in words a content filter
// doesn't take for an age or for anyone it guards. The number, "body", "hard rule" and "identifiable person" stay
// with the shot writer (EIGHTEEN, ADULT), whose instructions never reach a picture filter: a picture prompt with safe
// mode off is read the hardest, and a from-scratch adult character's photo was turned down on every model, with the
// plainest shot and look, while the same words went out before every one of them.
export const ADULT_ONLY = "Everyone shown is a grown adult.";
const KEEP_ADULT = ADULT_ONLY;
/** how their card says they look, on one line, without a full stop at the end ("" when it doesn't say) */
const lookWords = (card) => str(card && card.look && card.look.description, 4000).replace(/\s+/g, " ").trim().replace(/[.!?…]+$/, "");
+7 -3
View File
@@ -164,9 +164,13 @@ it didn't come.
The one exception is an `adult` character, once the person has said they're 18 or older: when every picture on their
card was made with AI (never one that was uploaded, which could be of anyone), their photos and clips can be adult.
`A.adultCheck(card, { of18, made, shot, said })` decides; they go to Venice's uncensored models with safe mode off,
and every prompt opens with the 18+ rule (`A.ADULT_ONLY`). Never when the card, the shot or the chat mentions anyone
under 18 (`A.underage`): then it's safe for work, as usual. The pictures made for their card go without safe mode
`A.adultCheck(card, { of18, made, shot, said })` decides; they go to Venice's uncensored models with safe mode off.
The shot is written to the 18+ rule with the number in it (`A.EIGHTEEN`, in the shot writer's instructions, which
no picture filter reads), and every picture and clip prompt opens with the same rule in words a filter doesn't take
for an age or for anyone it guards (`A.ADULT_ONLY`, "Everyone shown is a grown adult."): a picture prompt sent with
safe mode off is read the hardest, and "18+", "body", "hard rule" or "identifiable person" in it got the plainest
photo turned down on every model. Never when the card, the shot or the chat mentions anyone under 18
(`A.underage`): then it's safe for work, as usual. The pictures made for their card go without safe mode
too: a portrait drawn from words (`A.portraitPrompt(card, limit, { adult: true })`, when
`A.adultCheck(card, { of18, drawn: true })` says so), and one made from their pictures when every one of those was
made with AI.
+13 -9
View File
@@ -131,7 +131,7 @@ test("pictures made from their pictures keep their face and follow how their car
const drawn = A.portraitPrompt(long);
assert.ok(drawn.length <= 1500);
assert.match(drawn, /… Safe for work: everyone in it is an adult, fully clothed\. Head and shoulders, soft light, plain background, no text\.$/);
assert.match(A.portraitPrompt(long, 1500, { adult: true }), /^Hard rule.*Portrait photo of Ines Corbel.*… Head and shoulders, soft light, plain background, no text\.$/);
assert.match(A.portraitPrompt(long, 1500, { adult: true }), /^Everyone shown is a grown adult\. Portrait photo of Ines Corbel.*… Head and shoulders, soft light, plain background, no text\.$/);
assert.match(A.portraitPrompt({ ...bare, tagline: "Ferry pilot." }), /an original adult character: Ferry pilot\. Safe for work/);
assert.match(A.portraitPrompt({ ...bare, tagline: "" }), /an original adult character: an original character\. Safe for work/);
});
@@ -141,12 +141,15 @@ const mara = C.normalizeCard({ name: "Mara Vell", persona: "You are Mara Vell, a
portrait: "data:image/webp;base64,AAAA", look: { references: ["data:image/webp;base64,BBBB"] } });
test("adult characters: the 18+ rule is the first thing every photo and clip prompt says, and the shot's", () => {
assert.equal(A.ADULT_ONLY, "Hard rule, 18+ only: everyone shown is an adult, 18 or older, with a grown adult face and body.");
// the shot writer, a text model, is told the number; the picture model reads the same rule in words no filter takes
// for an age (or for anyone it guards): a picture prompt with safe mode off is read the hardest
assert.equal(A.EIGHTEEN, "Hard rule, 18+ only: everyone shown is an adult, 18 or older, with a grown adult face and body.");
assert.equal(A.ADULT_ONLY, "Everyone shown is a grown adult.");
for (const p of [A.photoPrompt(mara, "On the stage.", 1, 1500, { adult: true }), A.photoPrompt(mara, "On the stage.", 2, 1500, { adult: true }),
A.clipPrompt(mara, "She bows.", 2500, { adult: true })]) {
assert.ok(p.startsWith(A.ADULT_ONLY), p);
assert.ok(p.startsWith(A.ADULT_ONLY + " The reference picture"), p);
assert.ok(!/Safe for work/.test(p));
assert.match(p, /Never a real, identifiable person\./);
assert.ok(!/\d|identifiable|hard rule|body/i.test(p), "no age, no one it guards, nothing for a filter to read: " + p);
}
assert.ok(A.photoPrompt(mara, "x ".repeat(3000), 1, 400, { adult: true }).startsWith(A.ADULT_ONLY), "a long shot can't push it out");
assert.ok(A.clipPrompt(mara, "x ".repeat(3000), 400, { adult: true }).startsWith(A.ADULT_ONLY));
@@ -156,7 +159,7 @@ test("adult characters: the 18+ rule is the first thing every photo and clip pro
assert.equal(A.shotMessages(mara, [], "", "clip", { adult: true })[0].content, A.CLIP_SYSTEM_ADULT);
assert.equal(A.moveMessages(mara, [], { adult: true })[0].content, A.MOVE_SYSTEM_ADULT);
for (const s of [A.PHOTO_SYSTEM_ADULT, A.CLIP_SYSTEM_ADULT, A.MOVE_SYSTEM_ADULT]) {
assert.ok(s.includes(A.ADULT_ONLY), "the shot is written to the same rule");
assert.ok(s.includes(A.EIGHTEEN), "the shot is written to the 18+ rule, number and all");
assert.ok(!/safe for work/i.test(s));
assert.match(s, /Never show a real, identifiable person/);
}
@@ -173,7 +176,7 @@ test("the words Havre puts in a picture or a clip are none a content filter turn
A.clipPrompt(bare, "She waves.", 2500, { adult }), A.portraitPrompt(who, 1500, { adult }), A.portraitPrompt({ ...bare, tagline: "" }, 1500, { adult })]) {
assert.ok(!TRIPS.test(p), `“${(p.match(TRIPS) || [])[0]}” in: ${p}`);
assert.equal(A.underage(p), "", p);
if (adult) assert.ok(p.startsWith("Hard rule, 18+ only: everyone shown is an adult, 18 or older, with a grown adult face and body."), p);
if (adult) assert.ok(p.startsWith("Everyone shown is a grown adult. "), p);
else assert.match(p, /Safe for work: everyone in it is an adult, fully clothed\./);
}
}
@@ -190,8 +193,8 @@ test("the words Havre puts in a picture or a clip are none a content filter turn
test("adult characters: a portrait for their card opens with the 18+ rule too, and is drawn whatever pictures they have", () => {
const p = A.portraitPrompt(mara, 1500, { adult: true });
assert.ok(p.startsWith(A.ADULT_ONLY), p);
assert.match(p, /Never a real, identifiable person\. Portrait photo of Mara Vell, an original adult character: /);
assert.ok(!/Safe for work/.test(p));
assert.match(p, /^Everyone shown is a grown adult\. Portrait photo of Mara Vell, an original adult character: /);
assert.ok(!/Safe for work|\d|identifiable/.test(p));
assert.ok(A.portraitPrompt({ ...mara, look: { description: "x ".repeat(3000) } }, 400, { adult: true }).startsWith(A.ADULT_ONLY), "a long look can't push it out");
assert.match(A.portraitPrompt(mara), /Safe for work/, "without adult, as before");
// drawn from words, it's made from none of the pictures on their card: those don't count, everything else does
@@ -462,7 +465,8 @@ test("venice: an error carries Venice's answer as it came, and a picture or clip
});
test("the words in a prompt a content filter may weigh, for what venice saw", () => {
assert.deepEqual(A.filterWords(A.photoPrompt(mara, "On the stage, in a navy coat.", 1, 1500, { adult: true })), ["18"], "the 18+ rule's own age, once");
assert.deepEqual(A.filterWords(A.photoPrompt(mara, "On the stage, in a navy coat.", 1, 1500, { adult: true })), [], "an adult photo's prompt: none");
assert.deepEqual(A.filterWords(A.EIGHTEEN), ["18"], "the number stays with the shot writer");
assert.deepEqual(A.filterWords(A.photoPrompt(ines, "On deck.", 1, 1500)), [], "a safe-for-work photo: none");
assert.deepEqual(A.filterWords("A young woman in her early twenties, a schoolgirl's satchel, 21 years old, with her little sister, a girl of nine, and their son."),
["young", "schoolgirl", "21", "girl", "son"]);
+8 -5
View File
@@ -120,6 +120,8 @@ const handWritten = (o) => "Sure! Here it is:\n```json\n" + JSON.stringify(o, nu
const talks = () => seen.chats.filter((b) => !/^You (decide whether|keep the memory)/.test(String((b.messages[0] || {}).content || "")));
/** words a content filter turns a prompt down for: none of them are Havre's own (abilities.test.mjs has the rest) */
const TRIPS = /\b(?:sex\w*|nud\w*|naked|gor[ey]|minors?|child\w*|kids?|school\w*|teen\w*|under[\s-]?(?:18|eighteen|age\w*)|young\w*)\b/i;
// and in a picture prompt sent with safe mode off, which a filter reads the hardest: no age, nothing it guards
const ADULT_TRIPS = /\d|identifiable|hard rule|\bbody\b/i;
const multipart = (req) => (req.headers()["content-type"] || "").startsWith("multipart/form-data");
const CHARACTERS = [
{ slug: "sable-penrose", name: "Sable Penrose", description: "Your new neighbor needs help moving in.", author: "btrzm",
@@ -913,8 +915,8 @@ async function main() {
const adultEdit = seen.edits.at(-1).body;
assert.equal(adultEdit.modelId || adultEdit.model, "qwen-edit-uncensored");
assert.equal(adultEdit.safe_mode, false, "safe mode off, for her");
assert.ok(adultEdit.prompt.startsWith("Hard rule, 18+ only: everyone shown is an adult, 18 or older"), adultEdit.prompt);
assert.ok(!TRIPS.test(adultEdit.prompt), "nothing in it for a content filter to turn down: " + adultEdit.prompt);
assert.ok(adultEdit.prompt.startsWith("Everyone shown is a grown adult. The reference picture"), adultEdit.prompt);
assert.ok(!TRIPS.test(adultEdit.prompt) && !ADULT_TRIPS.test(adultEdit.prompt), "nothing in it for a content filter to turn down: " + adultEdit.prompt);
await has(page, "#log .line.media .cap", /photo · Qwen Edit Uncensored · \$0\.04 · 18\+ · save/);
// a clip from her: an uncensored model, priced first, its shot written to the 18+ rule too
await page.getByRole("button", { name: "ask for a clip" }).click();
@@ -952,8 +954,8 @@ async function main() {
assert.equal(seen.generated.length, drawnBefore + 1);
assert.equal(adultDrawn.model, "krea-2-turbo");
assert.equal(adultDrawn.safe_mode, false, "no safe mode for an adult character's portrait");
assert.ok(adultDrawn.prompt.startsWith("Hard rule, 18+ only: everyone shown is an adult, 18 or older"), adultDrawn.prompt);
assert.ok(!TRIPS.test(adultDrawn.prompt), adultDrawn.prompt);
assert.ok(adultDrawn.prompt.startsWith("Everyone shown is a grown adult. Portrait photo of Ines Corbel"), adultDrawn.prompt);
assert.ok(!TRIPS.test(adultDrawn.prompt) && !ADULT_TRIPS.test(adultDrawn.prompt), adultDrawn.prompt);
assert.equal(blurNext, true, "so nothing was blurred");
blurNext = false;
await has(page, "#adult-note", /^their photos and clips can be adult/);
@@ -965,7 +967,8 @@ async function main() {
assert.equal(seen.edits.length, editedBefore + 1);
assert.equal(sheetEdit.modelId || sheetEdit.model, "qwen-edit-uncensored");
assert.equal(sheetEdit.safe_mode, false);
assert.ok(sheetEdit.prompt.startsWith("Hard rule, 18+ only: everyone shown is an adult, 18 or older"), sheetEdit.prompt);
assert.ok(sheetEdit.prompt.startsWith("Everyone shown is a grown adult. The reference picture"), sheetEdit.prompt);
assert.ok(!ADULT_TRIPS.test(sheetEdit.prompt), sheetEdit.prompt);
await has(page, "#adult-note", /^their photos and clips can be adult/);
// with adult pictures switched off, safe mode is back on, and a blur says why
await page.getByRole("button", { name: "abilities" }).click();