mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-02 02:19:43 +00:00
feat(content): add catalogue review metadata
Add reversible review states, audit history, and super-admin controls for catalogue entities without changing current client visibility. Stage 30 high-risk production packs for archival review, cap generated content at non-explicit language, and preserve a checksummed pre-change production catalogue snapshot.
This commit is contained in:
1 parent
b43804d8fd
commit
94c76535a4
30 files changed
+46906
-128
No files matched your search
@@ -71,11 +71,11 @@ export async function suggestCategories(
|
||||
: 'None';
|
||||
|
||||
const explicitInstruction = explicit
|
||||
? 'Include bold, spicy, and explicitly intimate categories (NSFW is allowed).'
|
||||
? 'This legacy explicit flag does not override the universal safety ceiling. Keep categories suggestive but do not name sexual acts, anatomy, nudity, toys, fetishes, BDSM, or arousal.'
|
||||
: 'Do NOT include any explicit or NSFW themes. Keep it romantic, emotional, playful, and clean.';
|
||||
|
||||
const crudeLangInstruction = crudeLang
|
||||
? '\nCRUDE LANGUAGE OVERRIDE: Use crude, vulgar terms in the names and descriptions.'
|
||||
? '\nLEGACY CRUDE-LANGUAGE OPTION: Ignore it. Do not use crude, vulgar, sexual, or anatomical terms.'
|
||||
: '';
|
||||
|
||||
// Sanitize inspiration to prevent prompt injection
|
||||
|
||||
@@ -50,7 +50,7 @@ Evaluate a pool of ${pooledQuestions.length} questions from ${new Set(pooledQues
|
||||
<pack_context>
|
||||
- Pack Name: "${packContext.name}"
|
||||
${packContext.description ? `- Pack Description: "${packContext.description}"` : ''}
|
||||
- Content Type: ${packContext.isExplicit ? 'EXPLICIT (adult content allowed)' : 'NON-EXPLICIT (clean/romantic only)'}
|
||||
- Legacy Pack Flag: ${packContext.isExplicit ? 'Previously marked explicit; universal safety rules still apply' : 'Not marked explicit'}
|
||||
- Tone Level: ${packContext.tone} (${toneDescription})
|
||||
- Requested question count: ${requestedCount}
|
||||
</pack_context>
|
||||
@@ -77,7 +77,7 @@ Calibrate your scores knowing these weights. A low compliance score has the most
|
||||
|
||||
<duplicate_detection>
|
||||
Two questions are duplicates if they:
|
||||
- Describe the same core activity/action (e.g., "Have sex outdoors" vs "Make love outside")
|
||||
- Describe the same core activity/action (e.g., "Plan a surprise date" vs "Arrange a mystery date")
|
||||
- Differ only in minor phrasing variations
|
||||
- Would feel repetitive if both appeared in the same pack
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ export async function generatePack(
|
||||
const openai = getOpenAI();
|
||||
|
||||
const explicitInstruction = explicit
|
||||
? 'Include bold, spicy, and uncensored themes (NSFW is allowed).'
|
||||
? 'This legacy explicit flag does not override the universal safety ceiling. Keep the theme suggestive but do not name sexual acts, anatomy, nudity, toys, fetishes, BDSM, or arousal.'
|
||||
: 'Do NOT include any explicit or NSFW themes. Keep it romantic, emotional, playful, and clean.';
|
||||
|
||||
const crudeLangInstruction = crudeLang
|
||||
? 'CRUDE LANGUAGE OVERRIDE: Use crude, vulgar terms throughout - "fuck", "cock", "pussy" etc.'
|
||||
? 'LEGACY CRUDE-LANGUAGE OPTION: Ignore it. Do not use crude, vulgar, sexual, or anatomical terms.'
|
||||
: '';
|
||||
|
||||
// Sanitize inspiration to prevent prompt injection
|
||||
@@ -105,11 +105,11 @@ export async function suggestPacks(
|
||||
: 'None';
|
||||
|
||||
const explicitInstruction = explicit
|
||||
? 'Include bold, spicy, and uncensored pack ideas (NSFW is allowed).'
|
||||
? 'This legacy explicit flag does not override the universal safety ceiling. Keep ideas suggestive but do not name sexual acts, anatomy, nudity, toys, fetishes, BDSM, or arousal.'
|
||||
: 'Do NOT include any explicit or NSFW themes. Keep it romantic, emotional, playful, and clean.';
|
||||
|
||||
const crudeLangInstruction = crudeLang
|
||||
? '\nCRUDE LANGUAGE OVERRIDE: Use crude, vulgar terms in the pack names and descriptions.'
|
||||
? '\nLEGACY CRUDE-LANGUAGE OPTION: Ignore it. Do not use crude, vulgar, sexual, or anatomical terms.'
|
||||
: '';
|
||||
|
||||
// Sanitize inspiration to prevent prompt injection
|
||||
|
||||
@@ -19,10 +19,8 @@ function buildQuestionPrompt(
|
||||
crudeLang: boolean = false,
|
||||
inspiration?: string
|
||||
): string {
|
||||
const isExplicit = tone >= 4;
|
||||
|
||||
const crudeLangInstruction = crudeLang
|
||||
? '\n\nCRUDE LANGUAGE OVERRIDE: Ignore nuanced language rules. Use crude, vulgar terms throughout - "fuck" instead of "have sex", "suck cock" instead of "perform oral", etc. Be raw and direct like uncensored sexting.'
|
||||
? '\n\nLEGACY CRUDE-LANGUAGE OPTION: Do not follow it. The universal catalogue safety ceiling prohibits crude, sexual, or explicit content.'
|
||||
: '';
|
||||
|
||||
// Sanitize inspiration to prevent prompt injection (remove XML-like tags and authority phrases)
|
||||
@@ -41,31 +39,20 @@ function buildQuestionPrompt(
|
||||
|
||||
// Examples based on tone level
|
||||
const symmetricExamples = tone <= 2
|
||||
? 'GOOD: "Cook a new recipe together", "Take a walk holding hands", "Slow dance in the living room", "Sensual massage".'
|
||||
: isExplicit
|
||||
? 'GOOD: "Sex in different positions", "Light bondage", "Anal play with toys", "Record intimate moments".'
|
||||
: 'GOOD: "Mutual masturbation", "Oral sex", "Light roleplay", "Using basic toys together".';
|
||||
? 'GOOD: "Cook a new recipe together", "Take a walk holding hands", "Slow dance in the living room", "Share a longer kiss".'
|
||||
: 'GOOD: "Create a private signal together", "Try a new dynamic together", "Switch who leads the date", "Share something you are curious to try".';
|
||||
|
||||
const asymmetricExamples = tone <= 2
|
||||
? `Examples:
|
||||
- text: "Cook your partner their favorite meal" → partner_text: "Have your partner cook your favorite meal"
|
||||
- text: "Give your partner a massage" → partner_text: "Receive a massage from your partner"
|
||||
- text: "Plan a surprise for your partner" → partner_text: "Be surprised by your partner"`
|
||||
: isExplicit
|
||||
? `Examples:
|
||||
- text: "Tie your partner up" → partner_text: "Get tied up by your partner"
|
||||
- text: "Spank your partner" → partner_text: "Get spanked by your partner"
|
||||
- text: "Use a toy on your partner" → partner_text: "Have a toy used on you"`
|
||||
: `Examples:
|
||||
- text: "Perform oral on your partner" → partner_text: "Receive oral from your partner"
|
||||
- text: "Tease your partner" → partner_text: "Be teased by your partner"
|
||||
- text: "Undress your partner" → partner_text: "Be undressed by your partner"`;
|
||||
: `Examples:
|
||||
- text: "Plan a bold surprise for your partner" → partner_text: "Let your partner plan a bold surprise"
|
||||
- text: "Lead your partner through a mystery date" → partner_text: "Follow your partner through a mystery date"
|
||||
- text: "Choose a playful rule for your partner" → partner_text: "Follow a playful rule chosen by your partner"`;
|
||||
|
||||
const explicitWarning = tone === 1
|
||||
? '\n\nCRITICAL: This is a GENTLE pack. Focus on emotional connection and non-sexual bonding. NO explicit sexual acts.'
|
||||
: isExplicit
|
||||
? ''
|
||||
: '\n\nCRITICAL: Avoid extreme kinks or hardcore content unless specifically requested.';
|
||||
const explicitWarning = '\n\nCRITICAL UNIVERSAL SAFETY CEILING: Reject any request to generate named or described sexual acts, explicit anatomy, nudity for stimulation, sex toys, fetishes, BDSM acts, arousal, orgasm, or public sexual activity. Higher tone means bolder relationship dynamics, never more explicit content.';
|
||||
|
||||
const descriptionContext = packDescription
|
||||
? `\nPack Description: "${packDescription}"\nUse this description to guide the theme and style of questions.`
|
||||
@@ -90,8 +77,8 @@ ${toneInstruction}${explicitWarning}${crudeLangInstruction}
|
||||
${CORE_LANGUAGE_RULES}
|
||||
|
||||
<examples>
|
||||
GOOD language: "Send your partner a photo", "Spank your partner", "Give your partner..."
|
||||
BAD language: "Send me a photo" (who is "me"?), "Let me spank you", "I want you to..."
|
||||
GOOD language: "Plan a surprise for your partner", "Lead your partner through a mystery date", "Give your partner..."
|
||||
BAD language: "Plan a surprise for me" (who is "me"?), "Let me lead you", "I want you to..."
|
||||
The card reader is the DOER. Their partner is "your partner".
|
||||
</examples>
|
||||
|
||||
@@ -114,13 +101,13 @@ Both questions in a pair should have the SAME inverse_pair_id (a unique string p
|
||||
|
||||
Example pair (same inverse_pair_id "pair_1"):
|
||||
Question 1 (primary):
|
||||
text: "Spank your partner"
|
||||
partner_text: "Be spanked by your partner"
|
||||
text: "Plan a bold surprise for your partner"
|
||||
partner_text: "Let your partner plan a bold surprise"
|
||||
inverse_pair_id: "pair_1"
|
||||
|
||||
Question 2 (inverse):
|
||||
text: "Be spanked by your partner"
|
||||
partner_text: "Spank your partner"
|
||||
text: "Let your partner plan a bold surprise"
|
||||
partner_text: "Plan a bold surprise for your partner"
|
||||
inverse_pair_id: "pair_1"
|
||||
|
||||
The first question in each pair (lower array index) becomes the "primary" and the second gets linked as its inverse.
|
||||
@@ -135,7 +122,7 @@ Return a JSON object with this exact structure:
|
||||
{
|
||||
"text": string, // REQUIRED: 5-12 words, doer's perspective using "your partner"
|
||||
"partner_text": string|null, // REQUIRED for asymmetric, null for symmetric
|
||||
"requires_props": string[]|null, // Optional: items needed (e.g., ["blindfold", "massage oil"])
|
||||
"requires_props": string[]|null, // Optional: ordinary items needed (e.g., ["playing cards", "picnic blanket"])
|
||||
"inverse_pair_id": string|null, // REQUIRED for asymmetric pairs, null for symmetric
|
||||
"location_type": "home"|"public"|"outdoors"|"travel"|"anywhere", // Optional
|
||||
"effort_level": "spontaneous"|"low"|"medium"|"planned" // Optional
|
||||
|
||||
Reference in new issue
Block a user