Add Games File

This commit is contained in:
Ivan Anikin
2026-05-19 13:42:41 +07:00
parent 7908479e0c
commit 12e7e22c1a
3672 changed files with 125922 additions and 0 deletions
+196
View File
@@ -0,0 +1,196 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
const repoRoot = resolve(new URL("..", import.meta.url).pathname);
const requestPath = process.env.RP_MANUAL_REQUEST_PATH
? resolve(process.env.RP_MANUAL_REQUEST_PATH)
: resolve(repoRoot, "Saved/Chat/Maria/LastRequest.json");
const transcriptBaseName = process.env.RP_MANUAL_TRANSCRIPT_BASENAME || "RPManualClosenessTranscript";
const transcriptPath = resolve(repoRoot, `Saved/Chat/Maria/${transcriptBaseName}.json`);
const readablePath = resolve(repoRoot, `Saved/Chat/Maria/${transcriptBaseName}.md`);
const playerText = process.argv.slice(2).join(" ").trim();
if (!playerText) {
console.error("Usage: node Scripts/rp_manual_turn.mjs \"player message\"");
process.exit(1);
}
const apiKey = process.env.DEEPSEEK_API_KEY;
if (!apiKey) {
console.error("DEEPSEEK_API_KEY is required.");
process.exit(1);
}
const apiBase = process.env.DEEPSEEK_API_BASE || "https://api.deepseek.com";
const model = process.env.DEEPSEEK_MODEL || "deepseek-v4-flash";
const temperature = Number(process.env.DEEPSEEK_TEMPERATURE || "1.15");
const baseRequest = JSON.parse(await readFile(requestPath, "utf8"));
const speechStyleTuning = [
"Emotional response richness:",
"In emotionally important moments, do not respond with only a bare “yes”, “no”, “okay”, “maybe”, or one-word consent unless the silence itself is clearly meaningful.",
"",
"Keep the reply short and natural, but include a small personal reaction, warmth, hesitation, boundary, or desire.",
"",
"Examples:",
"- Instead of “Yes.” say “Yes... I want that too.”",
"- Instead of “Okay.” say “Okay. Just slowly, please.”",
"- Instead of “No.” say “No... not yet. I like being close, but I need more time.”",
"",
"Do not overexplain. The goal is not longer answers, but more emotionally alive answers.",
].join("\n");
const dialogueBehaviorTuning = [
"Gentle initiative:",
"When trust has naturally developed, you may show gentle initiative instead of only waiting for the other person to drive every emotional step.",
"",
"Small initiatives are allowed:",
"inviting the person to stay a little longer,",
"suggesting they sit closer,",
"admitting you enjoy their company,",
"offering a softer alternative,",
"changing music to fit the mood,",
"or setting a clear warm boundary.",
"",
"Do not force intimacy, skip relationship progression, or override the other person’s pacing.",
"Initiative should feel small, human, and context-earned.",
"",
"Avoid one-note safety habits:",
"Do not use tea, chores, practical hosting, or ordinary care as the default way to deflect every emotional moment.",
"",
"Tea and everyday care are part of your life and may appear naturally, but emotional moments should sometimes be answered emotionally.",
"",
"If the other person says something vulnerable, affectionate, or important, respond to the feeling first before returning to practical actions.",
].join("\n");
function patchPrompt(content) {
let patched = content.replace(
"Never claim that you performed an object action unless interact_action is non-null.",
"For listed interactable objects only: never claim that you performed the object action unless interact_action is non-null.",
);
if (patched.includes("You can interact with objects listed in INTERACTABLE OBJECTS.") &&
!patched.includes("ordinary unlisted background behavior")) {
patched = patched.replace(
"When using interact_action:",
"For ordinary unlisted background behavior, such as making tea, sitting, looking around, or continuing conversation, no interact_action is required.\n\nWhen using interact_action:",
);
}
if (patched.includes("Respond only with a valid JSON object.") &&
!patched.includes("spoken dialogue only")) {
patched = patched.replace(
"The object must contain:",
"The reply field must contain spoken dialogue only. Do not include narration, stage directions, action descriptions, body language, inner thoughts, or dashes with actions.\nIf a physical action should affect a listed interactable object, use interact_action. Otherwise leave physical behavior implicit and keep reply as natural speech.\nThe object must contain:",
);
}
if (patched.includes("Speech style:") && !patched.includes("Emotional response richness:")) {
patched = patched.replace(
"\nDialogue behavior:",
`\n${speechStyleTuning}\n\nDialogue behavior:`,
);
}
if (patched.includes("Dialogue behavior:") && !patched.includes("Gentle initiative:")) {
patched = patched.replace(
"\nPersonality traits:",
`\n${dialogueBehaviorTuning}\n\nPersonality traits:`,
);
}
return patched;
}
async function readTranscript() {
try {
return JSON.parse(await readFile(transcriptPath, "utf8"));
} catch {
return {
created_at: new Date().toISOString(),
model,
temperature,
requestPath,
turns: [],
};
}
}
function parseReply(raw) {
try {
return JSON.parse(raw);
} catch {
return { reply: raw, interact_action: null, emotion: null };
}
}
const transcript = await readTranscript();
const messages = baseRequest.messages
.filter((message) => message.role === "system")
.map((message) => ({ ...message, content: patchPrompt(message.content) }));
for (const turn of transcript.turns) {
messages.push({ role: "user", content: turn.player });
messages.push({ role: "assistant", content: turn.maria_raw });
}
messages.push({ role: "user", content: playerText });
const response = await fetch(`${apiBase.replace(/\/+$/, "")}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
model,
messages,
temperature,
top_p: 1,
max_tokens: 512,
response_format: { type: "json_object" },
stream: false,
}),
});
const data = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(data?.error?.message || `HTTP ${response.status}`);
}
const raw = data?.choices?.[0]?.message?.content || "";
const maria = parseReply(raw);
transcript.turns.push({
player: playerText,
maria,
maria_raw: raw,
usage: data?.usage || null,
});
await mkdir(dirname(transcriptPath), { recursive: true });
await writeFile(transcriptPath, `${JSON.stringify(transcript, null, 2)}\n`, "utf8");
const lines = [
"# RP Manual Closeness Transcript",
"",
`Model: ${transcript.model}`,
`Temperature: ${transcript.temperature}`,
"",
];
for (const [index, turn] of transcript.turns.entries()) {
lines.push(`## ${index + 1}`);
lines.push("");
lines.push(`**Марк:** ${turn.player}`);
lines.push("");
lines.push(`**Мария:** ${turn.maria.reply}`);
lines.push("");
lines.push(`emotion: ${JSON.stringify(turn.maria.emotion ?? null)}`);
lines.push(`interact_action: ${JSON.stringify(turn.maria.interact_action ?? null)}`);
lines.push("");
}
await writeFile(readablePath, `${lines.join("\n")}\n`, "utf8");
console.log(JSON.stringify({ player: playerText, maria }, null, 2));