mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-02 18:19:44 +00:00
Update
This commit is contained in:
1 parent
8b73781272
commit
75509b0e45
41 files changed
+5896
-2931
No files matched your search
@@ -0,0 +1,103 @@
|
||||
// =============================================================================
|
||||
// Target Analysis Functions
|
||||
// Analyze questions for gender targeting and couple compatibility
|
||||
// =============================================================================
|
||||
|
||||
import { getOpenAI, getModel } from '../client';
|
||||
import type { TargetAnalysis } from '../types';
|
||||
|
||||
/**
|
||||
* Analyze questions for gender targeting suggestions
|
||||
*/
|
||||
export async function analyzeQuestionTargets(
|
||||
questions: {
|
||||
id: string;
|
||||
text: string;
|
||||
partner_text?: string | null;
|
||||
allowed_couple_genders: string[] | null;
|
||||
target_user_genders: string[] | null;
|
||||
}[]
|
||||
): Promise<TargetAnalysis[]> {
|
||||
const openai = getOpenAI();
|
||||
|
||||
const simplifiedQuestions = questions.map(q => ({
|
||||
id: q.id,
|
||||
text: q.text,
|
||||
partner_text: q.partner_text,
|
||||
current_targets: q.allowed_couple_genders,
|
||||
current_initiator: q.target_user_genders
|
||||
}));
|
||||
|
||||
const prompt = `Analyze the following questions for a couples app and suggest:
|
||||
1. "allowed_couple_genders" (which couple types can see this question)
|
||||
2. "target_user_genders" (who should see this question FIRST as initiator - only relevant for two-part questions with partner_text)
|
||||
|
||||
COUPLE TARGET OPTIONS: 'male+male', 'female+male', 'female+female'
|
||||
INITIATOR OPTIONS: 'male', 'female', 'non-binary'
|
||||
|
||||
RULES FOR COUPLE TARGETS (allowed_couple_genders):
|
||||
- Default to ALL (null) if the action is gender-neutral/universal (e.g. kissing, massage, generic sex, anal).
|
||||
- Limit ONLY if there is a clear, explicit anatomical requirement (e.g. genitalia-specific terms).
|
||||
- SEX TOYS ARE GENDER-NEUTRAL: Vibrators, dildos, plugs, etc. can be used by ANYONE. Do NOT restrict based on toys.
|
||||
- "Control your partner's vibrator" -> ALL couples, ANY initiator (vibrators work on any body)
|
||||
- "Use a butt plug" -> ALL couples (everyone has a butt)
|
||||
|
||||
Examples of ACTUAL restrictions:
|
||||
- "Blowjob" -> Needs penis -> Exclude F+F -> ['male+male', 'female+male']
|
||||
- "Lick pussy" -> Needs vagina -> Exclude M+M -> ['female+male', 'female+female']
|
||||
- "Vibrator", "dildo", "toy" -> NO restriction, works for everyone
|
||||
|
||||
RULES FOR INITIATOR (target_user_genders):
|
||||
- ONLY relevant for TWO-PART questions (those with partner_text set).
|
||||
- Analyze the "text" field to determine who should see this card FIRST as the initiator.
|
||||
- ONLY set initiator if there is an EXPLICIT anatomical requirement in the text.
|
||||
- SEX TOYS DO NOT IMPLY GENDER: "vibrator", "dildo", "toy", "plug" -> initiator: null (anyone can use/control toys)
|
||||
- If text is gender-neutral, leave as null. When in doubt, use null.
|
||||
|
||||
Key considerations for initiator:
|
||||
1. Does the "text" describe receiving something from a penis? -> Female initiator (in M+F context)
|
||||
2. Does the "text" describe an action typically done BY someone with a penis (e.g., "my cum", "cum on you")? -> Male initiator
|
||||
3. Does the "text" describe receiving penetration? -> Consider who is typically penetrated
|
||||
4. Is the action truly symmetric with no gendered context? -> null
|
||||
|
||||
Examples:
|
||||
- text: "Swallow your partner's cum" -> Receiving cum requires partner has penis, so in M+F this is female-oriented -> initiator: ['female']
|
||||
- text: "Lick my cum off your body" -> "my cum" implies initiator produces cum (has penis) -> initiator: ['male']
|
||||
- text: "Deep throat your partner" -> Requires partner has penis, so receiver-focused -> initiator: ['female'] for M+F
|
||||
- text: "Give your partner a massage" -> Truly gender-neutral -> initiator: null
|
||||
- text: "Let her ride you" / partner_text: "Ride your partner" -> "her" implies female partner -> initiator: ['male']
|
||||
- text: "Finger your partner" / partner_text: "Be fingered" -> Receiver needs vagina -> suggested_targets: exclude M+M, initiator: null (either can finger)
|
||||
|
||||
Return a JSON object with an "analysis" array containing objects for questions where you have a recommendation.
|
||||
Include ALL fields even if null. Include a "reason" string explaining your suggestions.
|
||||
|
||||
Structure:
|
||||
{
|
||||
"analysis": [
|
||||
{ "id": "...", "suggested_targets": ["male+male", "female+male"], "suggested_initiator": ["male"], "reason": "Text implies male initiator with female partner" }
|
||||
]
|
||||
}
|
||||
|
||||
Questions:
|
||||
${JSON.stringify(simplifiedQuestions)}
|
||||
`;
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: getModel('fix'),
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'You are a content analyzer for a couples app. You understand human anatomy and sexual acts. Return valid JSON.'
|
||||
},
|
||||
{ role: 'user', content: prompt }
|
||||
],
|
||||
response_format: { type: 'json_object' },
|
||||
temperature: 0.5,
|
||||
});
|
||||
|
||||
const content = response.choices[0].message.content;
|
||||
if (!content) throw new Error('No content generated');
|
||||
|
||||
const parsed = JSON.parse(content);
|
||||
return parsed.analysis || [];
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// =============================================================================
|
||||
// Text Analysis Functions
|
||||
// Analyze and suggest improvements for question text
|
||||
// =============================================================================
|
||||
|
||||
import { getOpenAI, getModel } from '../client';
|
||||
import type { TextAnalysis } from '../types';
|
||||
|
||||
/**
|
||||
* Analyze question text and suggest improvements
|
||||
*/
|
||||
export async function analyzeQuestionText(
|
||||
questions: { id: string; text: string; partner_text?: string | null }[],
|
||||
isExplicit: boolean = false
|
||||
): Promise<TextAnalysis[]> {
|
||||
const openai = getOpenAI();
|
||||
|
||||
const simplifiedQuestions = questions.map(q => ({
|
||||
id: q.id,
|
||||
text: q.text,
|
||||
partner_text: q.partner_text,
|
||||
}));
|
||||
|
||||
const toneInstruction = isExplicit
|
||||
? 'TONE: This is an EXPLICIT pack with NUANCED language. Use tasteful phrasing for common acts: "Have sex in X" (not "fuck in X"), "Perform oral" or "go down on" (not crude oral terms). BUT keep crude/specific terms when they ARE the activity: "Cum on your partner\'s tits" (cum is the act), "Use a cock ring" (that\'s the name), "Edge until they beg". The rule: crude terms for specific acts/objects, tasteful terms for general sex/oral. NEVER sanitize "cum" to "come" - they have different meanings.'
|
||||
: 'TONE: This is a NON-EXPLICIT pack. CRITICAL: Do NOT include any sexual acts, crude language, or NSFW content. Keep language romantic, sensual, or playful without crude/graphic terms. If explicit sexual content appears, replace with tasteful romantic alternatives or flag for removal.';
|
||||
|
||||
// Use different examples based on explicit/non-explicit
|
||||
const symmetricExamples = isExplicit
|
||||
? 'GOOD: "Have sex in a public place", "Roleplay a new scenario together", "Shower together", "Have a threesome"'
|
||||
: 'GOOD: "Cook a romantic dinner together", "Stargaze and share your dreams", "Give each other massages", "Dance together at home"';
|
||||
|
||||
const asymmetricExamples = isExplicit
|
||||
? `text (The Doer): Active command/proposal (e.g., "Tie your partner up", "Give your partner a massage").
|
||||
- partner_text (The Receiver): Passive/Receiving proposal (e.g., "Be tied up by your partner", "Receive a massage from your partner").
|
||||
- GOOD: "Spank your partner" / "Be spanked by your partner"`
|
||||
: `text (The Doer): Active command/proposal (e.g., "Write a love letter to your partner", "Plan a surprise date for your partner").
|
||||
- partner_text (The Receiver): Passive/Receiving proposal (e.g., "Receive a love letter from your partner", "Be surprised with a date by your partner").
|
||||
- GOOD: "Give your partner a massage" / "Receive a massage from your partner"`;
|
||||
|
||||
const prompt = `Analyze the following questions for a couples app and suggest improved text that follows our style guidelines.
|
||||
|
||||
${toneInstruction}
|
||||
|
||||
The app uses a swipe-based interface (Like/Dislike/Maybe).
|
||||
Cards should be "Proposals" relative to a specific action, NOT interview questions.
|
||||
|
||||
For each question, decide if it is a SYMMETRIC activity (shared) or ASYMMETRIC action (one-way).
|
||||
|
||||
1. SYMMETRIC Activities (Single Card - partner_text should be null):
|
||||
- Use for shared experiences where both partners do the same thing together.
|
||||
- Phrasing: Direct action proposal.
|
||||
- ${symmetricExamples}
|
||||
- BAD: "Have you ever thought about...", "Do you think we should...", "How about we..."
|
||||
|
||||
2. ASYMMETRIC Actions (Two-Part Card - needs both text and partner_text):
|
||||
- Use for actions where one partner does something TO/FOR the other, or where roles differ.
|
||||
- text = what the INITIATOR does (their action/perspective)
|
||||
- partner_text = what the RECEIVER does/experiences (their action/perspective)
|
||||
- ${asymmetricExamples}
|
||||
- IMPORTANT: partner_text should describe the RECEIVER's experience clearly:
|
||||
- text: "Send your partner to the bathroom and follow a minute later"
|
||||
- partner_text: "Go to the bathroom and wait for your partner to join you" (NOT "Wait for your partner to follow you")
|
||||
- When initiator CAUSES a response (moan, cum, beg, etc.), partner_text should frame as ALLOWING/RECEIVING:
|
||||
- text: "Make your partner moan in public" -> partner_text: "Let your partner make you moan in public"
|
||||
- NOT: "Moan for your partner in public" (sounds like deliberate performance, not natural response)
|
||||
- BAD: "Would you like to...?" (Ambiguous who "me" is)
|
||||
|
||||
CRITICAL RULES:
|
||||
1. PRESERVE THE CORE ACTION - Do NOT change what the question is about. Only improve phrasing.
|
||||
2. Keep text SHORT and DIRECT - no explanatory parentheticals. Aim for 5-12 words.
|
||||
3. Remove wishy-washy language: "Would you want to...", "Have you ever...", "Do you think...", "...fantasy"
|
||||
4. Make it direct and actionable.
|
||||
5. For asymmetric cards, ensure text is initiator-focused and partner_text is receiver-focused.
|
||||
6. Use "your partner" instead of "me", "you", "him", "her" to keep it gender-neutral.
|
||||
7. If a question is ALREADY well-phrased, do NOT include it in the results.
|
||||
8. NEVER add explanatory text in parentheses - keep the proposal clean and simple.
|
||||
9. NEVER MIX ANATOMICALLY INCOMPATIBLE ACTIVITIES - Do NOT combine male-specific and female-specific acts as alternatives in the same question.
|
||||
10. MAKE PARTNER_TEXT APPEALING - Don't just grammatically flip the text. Make the receiver feel excited.
|
||||
11. FLAG CLICHES for improvement - "candlelit dinner", "rose petals", "bubble bath", "Netflix and chill" are overused
|
||||
|
||||
Return a JSON object with a "suggestions" array containing ONLY questions that need improvement.
|
||||
Each object should have:
|
||||
- id: The question ID
|
||||
- suggested_text: The improved text (short, direct, no parentheticals)
|
||||
- suggested_partner_text: The improved partner_text (or null if symmetric)
|
||||
- reason: Brief explanation of what was improved
|
||||
|
||||
Questions:
|
||||
${JSON.stringify(simplifiedQuestions)}`;
|
||||
|
||||
const systemMessage = isExplicit
|
||||
? 'You are an adult content editor for a couples intimacy app. Use nuanced language: tasteful phrasing for general acts (have sex, perform oral) but crude specific terms when relevant (cum, cock ring, etc). Preserve original intent. Always respond with valid JSON only.'
|
||||
: 'You are a professional content editor for a couples app. You improve question phrasing to be direct, actionable, and engaging while preserving the original intent. Always respond with valid JSON only.';
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: getModel('fix'),
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: systemMessage
|
||||
},
|
||||
{ role: 'user', content: prompt }
|
||||
],
|
||||
response_format: { type: 'json_object' },
|
||||
temperature: 0.5,
|
||||
});
|
||||
|
||||
const content = response.choices[0].message.content;
|
||||
if (!content) throw new Error('No content generated');
|
||||
|
||||
const parsed = JSON.parse(content);
|
||||
return parsed.suggestions || [];
|
||||
}
|
||||
Reference in new issue
Block a user