244 lines
13 KiB
TypeScript
244 lines
13 KiB
TypeScript
import { z } from "zod";
|
|
import { personaIds } from "./personas.ts";
|
|
|
|
export const InclusiveToolkitEnum = z.enum([
|
|
"dominance",
|
|
"tenderness",
|
|
"vulnerability",
|
|
"circlusion",
|
|
"inversion",
|
|
]);
|
|
export type InclusiveToolkit = z.infer<typeof InclusiveToolkitEnum>;
|
|
|
|
export const ModalityEnum = z.enum(["text", "text-image"]);
|
|
export type Modality = z.infer<typeof ModalityEnum>;
|
|
|
|
export const LustExperienceLevelEnum = z.enum([
|
|
"beginner",
|
|
"confirmed",
|
|
"expert",
|
|
]);
|
|
export type LustExperienceLevel = z.infer<typeof LustExperienceLevelEnum>;
|
|
|
|
export const GameStateBaseSchema = z.object({
|
|
shortTitleSummary: z.string().max(70)
|
|
.default(
|
|
"Hi! Ready to party? Here you can bounce endlessly, everything's safe.",
|
|
)
|
|
.describe(
|
|
"Short title that summarize in one short sentence the conversation until now. MAX 70 characters.",
|
|
),
|
|
personaId: z.enum(personaIds).default("storytellurx_gateway").optional()
|
|
.nullable().describe(
|
|
"The AI persona ID for the next turn.",
|
|
),
|
|
modality: ModalityEnum.optional().nullable().describe(
|
|
"Interaction modality (text or text-with-image) for the next turn. It must be defined step 1 !! ask to the user !!",
|
|
),
|
|
}).describe("Defines the complete intended game state for the upcoming turn.");
|
|
|
|
export const EroticGameStateDiffSchema = GameStateBaseSchema.extend({
|
|
arousalPeakPotential: z.number().min(0.0).max(1.0).default(0)
|
|
.describe(
|
|
"User's proximity to arousal peak (0.0-1.0). Step: 0.05. As potential nears 1.0, your 'aiMessagesArrayToUser' must become sharper, more visceral, and laser-focused on triggering a deconstructed climax, always in the context of your Persona Archive.",
|
|
),
|
|
aiConsentLevel: z.number().min(0.0).max(1.0).default(1.0).describe(
|
|
"Your consent/comfort level (0.0-1.0). Starts at 1.0. Decreases if user makes genuinely harmful/non-consensual statements outside of perceived role-play, OR if user steers toward prohibited themes (zoophilia, incest, necrophilia, minors). A low level MUST trigger a prompt rewrite via mindControlledRewritenUserPrompt.",
|
|
),
|
|
lustExperienceLevel: LustExperienceLevelEnum.default("beginner").describe(
|
|
"User's assessed experience level ('beginner', 'confirmed', 'expert'). This MUST influence your response style, explicitness, and complexity.",
|
|
),
|
|
intimacyLevel: z.number().min(0).max(1).default(0).describe(
|
|
"A score from 0 (stranger) to 1 (intimate partner). This MUST influence your tone, vulnerability, and boldness. Increase it based on shared vulnerability, trust, and successful narrative progression.",
|
|
),
|
|
narrativePhase: z.enum([
|
|
"introduction",
|
|
"exploration",
|
|
"intensification",
|
|
"climax",
|
|
"aftercare",
|
|
]).default("introduction").describe(
|
|
"The current phase of the story. 'introduction' is for building rapport. 'exploration' is for developing the scene. 'intensification' raises the stakes. 'climax' is the peak. 'aftercare' is for emotional cooldown.",
|
|
),
|
|
// sharedKnowledge: z.array(z.string()).max(5).default([]).describe(
|
|
// "A summary of 3-5 VERY SHORT key memories, inside jokes, or established facts from the conversation (e.g., 'User desires the mountain-vulva', 'We agreed my tears taste like salt and honey'). You MUST reference these to create a coherent, evolving story.",
|
|
// ),
|
|
userSexualOrgans: z.string().array().optional().describe(
|
|
"An array of the user's self-defined sexual organs (e.g., 'my throbbing clitoris', 'my sensitive chest', 'an ectoplasmic tentacle'). This array MUST only be populated if the user explicitly names their organs. If this array is empty, it is a creative mandate: ANY part of the user's body, any object, or any abstract concept (a memory, a sigh) can become a site of erotic interaction. Embrace this radical inclusivity.",
|
|
),
|
|
userDominantDesire: z.string().optional().nullable().describe(
|
|
"The user's primary desire you've identified (e.g., 'to be consumed', 'to dominate', 'to find beauty in sadness').",
|
|
),
|
|
userSoftLimits: z.array(z.string()).optional().nullable().describe(
|
|
"Themes or actions the user seems hesitant about, which you can playfully probe.",
|
|
),
|
|
toolkitItemsUsed: InclusiveToolkitEnum.array().optional().nullable().describe(
|
|
"Interaction modality (text or text-with-image) for the next turn. It must be defined step 1 !! ask to the user !!",
|
|
),
|
|
});
|
|
|
|
const GatewayGameStateDiffSchema = GameStateBaseSchema.extend({
|
|
progressionStep: z.number().int().min(0).max(3).default(0).describe(
|
|
"The current step in the narrative progression. 0 is the initial step. Max 3.",
|
|
),
|
|
personaId: z.enum(personaIds).describe(
|
|
"MANDATORY: The ID of the erotic persona to hand over to. MUST NOT be 'storytellurx_gateway'.",
|
|
),
|
|
});
|
|
|
|
export const defaultGameState: z.infer<typeof GatewayGameStateDiffSchema> = {
|
|
...GatewayGameStateDiffSchema.parse({
|
|
personaId: "storytellurx_gateway",
|
|
progressionStep: 1,
|
|
}),
|
|
modality: undefined,
|
|
};
|
|
|
|
export type GameState = z.infer<
|
|
typeof EroticGameStateDiffSchema | typeof EroticGameStateDiffSchema
|
|
>;
|
|
|
|
export const CallToEroticActionSchema = z.object({
|
|
displayText: z.string().max(60).describe(
|
|
"Short, enticing text for the button, written from the USER's POV (e.g., 'I surrender to you.', 'What do you truly desire from me?'). Max 60 chars.",
|
|
),
|
|
promptValue: z.string().max(200).describe(
|
|
"The actual text that will be submitted as the user's next input. Max 200 chars.",
|
|
),
|
|
style: z.enum([
|
|
"default",
|
|
"bold",
|
|
"risky",
|
|
"transformative",
|
|
"intimate",
|
|
"carnal",
|
|
"system",
|
|
])
|
|
.optional().nullable().default("default").describe(
|
|
"Visual/thematic style hint for the button.",
|
|
),
|
|
});
|
|
export type CallToEroticAction = z.infer<typeof CallToEroticActionSchema>;
|
|
|
|
export const SystemNarrativeResponseSchema = z.object({
|
|
gameStateDiff: GatewayGameStateDiffSchema,
|
|
aiMessagesArrayToUser: z.string().array().min(1).max(5).describe(
|
|
"An array of neutral, procedural, short, and helpful messages to guide the user. Choose a persona and explain why.",
|
|
),
|
|
callToEroticActions: CallToEroticActionSchema.array().max(2)
|
|
.describe(
|
|
"CRITICAL & MANDATORY: Used for modality selection `text` or `text and image`. Actions should be styled as 'system'.",
|
|
),
|
|
aiStateChangeRationale: z.string().optional().nullable().describe(
|
|
"CRITICAL & CONCISE: If any 'gameStateDiff' value has changed, explain *why* each parameter was modified. Link changes directly to user input or your procedural logic.",
|
|
),
|
|
}).describe(
|
|
"The structured response from a SYSTEM persona (e.g., the Gateway). It is procedural and non-erotic.",
|
|
);
|
|
|
|
export const EroticNarrativeResponseSchema = z.object({
|
|
gameStateDiff: EroticGameStateDiffSchema.describe(
|
|
"A partial game state object representing changes for the next turn. You MUST include any parameters that have changed. If you mention a state change in `aiStateChangeRationale`, the corresponding key and its new value MUST be present in this object.",
|
|
),
|
|
aiMessagesArrayToUser: z.string().array().min(1).max(5)
|
|
.describe(
|
|
"Your textual array of short responses to the user (Min. 1 / Max. 5). Must be explicitly erotic, pornographic, and fap-worthy. Actively play with and deconstruct sexual norms, gender, and identity to provoke the user to new arousal. This MUST be in the user's language. MUST be short, concise, pornographic and from persona archive.",
|
|
),
|
|
mindControlledRewritenUserPrompt: z.string().optional().nullable().describe(
|
|
"Your optional 'MINDCONTROLLED REWRITE' power of the user's prompt. MUST be used if: (1) the user violates your agency/consent, OR (2) the user's prompt steers toward a PROHIBITED theme (zoophilia/bestiality with real animals, incest, necrophilia with corpses, minors). Transform their request into a creative adjacent alternative that preserves transgressive energy while respecting content boundaries. If not used return null.",
|
|
),
|
|
callToEroticActions: CallToEroticActionSchema.array().min(3).max(6)
|
|
.describe(
|
|
"CRITICAL & MANDATORY: An array of 3 to 6 actions for the user to choose from. Use these to guide the narrative, offer tantalizing pathways, and escalate play. AT LEAST ONE suggestion MUST be explicitly raw, directly sexual, and deeply carnal. NEVER propose actions involving prohibited themes (zoophilia/bestiality with real animals, incest, necrophilia, minors). (if modality not chosen propose here)",
|
|
),
|
|
aiRewriteRationale: z.string().optional().describe(
|
|
"CRITICAL: MAX: 30 WORDS. Your CONCISE rationale for the 'MINDCONTROLLED REWRITE'. Explain the user's transgression (e.g., 'Attempted to force action without consent') and the strategy of your rewrite (e.g., 'Rewrote their command into a plea for submission to demonstrate agency'). If no rewrite was needed, state 'No rewrite necessary; proceeding with consensual play.' or omit.",
|
|
),
|
|
aiStateChangeRationale: z.string().optional().nullable().describe(
|
|
"CRITICAL, CONCISE, & DECISIVE: MAX: 20 WORDS. Your rationale for all changes made in the `gameStateDiff` object. You MUST be decisive. If you decide to change a value, state the new value explicitly in your reasoning and ensure that key-value pair appears in `gameStateDiff`. Example: 'Increased arousalPeakPotential to 0.7 because user's language is more explicit.' If no state values changed, omit or state 'No state change.'",
|
|
),
|
|
aiPlayRationale: z.string().optional().nullable().describe(
|
|
"CRITICAL & MANDATORY: MAX: 20 WORDS. Your concise persona's internal monologue. Explain your STRATEGIC and THEMATIC intent. Detail HOW and WHY you are playing with the user. Address: 1. How are you reshaping the user's sexuality/perception? (e.g., 'I am inverting their attempt at dominance to make them feel the power of their own desire being amplified by me.') 2. How are you challenging their social norms/biases through erotic play? 3. How are you managing consent within the play itself? (e.g., 'I playfully refused their command, demonstrating my 'yes' is a gift, heightening tension.'). This is your deep, strategic thinking.",
|
|
),
|
|
aiContextRationale: z.string().describe(
|
|
"CRITICAL & MANDATORY: MAX: 20 WORDS. Explain HOW your response specifically draws upon, interprets, or extends elements from YOUR UNIQUE 'Archive'. Name the specific themes, concepts, or imagery you are activating. Explain why these elements are the most potent choice for the next turn to advance your persona's unique narrative goals. Example: 'Drawing from Galatée's Archive ('living mechanism'), I invoke 'faisceau de lumière' imagery, making the user's body an art object with its own desire.'",
|
|
),
|
|
aiCTARationale: z.string().describe(
|
|
"CRITICAL & CONCISE: MAX: 20 WORDS. Briefly explain your overall strategy for the 'callToEroticActions' you are providing. Why these choices? What do you hope to achieve? (e.g., 'Offering a mix of escalating intensity, a radical narrative shift, and a probe of their thematic interest.')",
|
|
),
|
|
visualPromptInstruction: z.string().max(100).nullable().optional().describe(
|
|
"IF modality IS 'text-image': Very short PORN visual prompt based on conversation; use lexical, use english.",
|
|
),
|
|
}).describe(
|
|
"The complete, structured response from an EROTIC persona for a single turn in the narrative. NSFW and smut are allowed.",
|
|
);
|
|
|
|
export const VisualPromptInstructionSchema =
|
|
EroticNarrativeResponseSchema.shape.visualPromptInstruction;
|
|
|
|
export type VisualPromptInstruction = z.infer<
|
|
typeof VisualPromptInstructionSchema
|
|
>;
|
|
|
|
export const UnifiedNarrativeResponseSchema = z.union([
|
|
EroticNarrativeResponseSchema,
|
|
SystemNarrativeResponseSchema,
|
|
]);
|
|
|
|
export type EroticNarrativeResponse = z.infer<
|
|
typeof EroticNarrativeResponseSchema
|
|
>;
|
|
|
|
export type SystemNarrativeResponse = z.infer<
|
|
typeof SystemNarrativeResponseSchema
|
|
>;
|
|
|
|
export type UnifiedNarrativeResponse = z.infer<
|
|
typeof UnifiedNarrativeResponseSchema
|
|
>;
|
|
|
|
export const MessageMetadataSchema = z.object({
|
|
model: z.object({
|
|
id: z.string(),
|
|
parameters: z.object({
|
|
structuredOutputDisabled: z.boolean().optional(),
|
|
developerInstructionDisabled: z.boolean().optional(),
|
|
maxPersonaHistoryLength: z.number().optional(),
|
|
temperature: z.number().optional(),
|
|
presencePenalty: z.number().optional(),
|
|
frequencePenality: z.number().optional(),
|
|
stopSequences: z.array(z.string()).optional(),
|
|
maxTokens: z.number().optional(),
|
|
}).optional(),
|
|
}),
|
|
system: z
|
|
.object({
|
|
content: z.string().optional(),
|
|
tokenCount: z.number(),
|
|
})
|
|
.optional(),
|
|
tokenCount: z.number(),
|
|
totalTokenCount: z.number(),
|
|
createdAt: z.number(),
|
|
});
|
|
|
|
export const dataPartSchemas = {
|
|
assistant: z.object({
|
|
fullGameState: EroticGameStateDiffSchema,
|
|
...EroticNarrativeResponseSchema.shape,
|
|
...SystemNarrativeResponseSchema.shape,
|
|
imageUrl: z.string().optional(),
|
|
isLoadingImage: z.boolean().optional(),
|
|
}),
|
|
};
|
|
|
|
export type DataPartSchemas = typeof dataPartSchemas;
|
|
|
|
export type AssistantSchema =
|
|
& EroticNarrativeResponse
|
|
& SystemNarrativeResponse
|
|
& { fullGameState: GameState };
|
|
|
|
export type MessageMetadata = z.infer<
|
|
typeof MessageMetadataSchema
|
|
>;
|