@p-sw/brainbox 0.2.0 → 0.2.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +93 -12
- package/dist/prompts/persona_base_system_prompt.md +14 -2
- package/dist/prompts/persona_base_system_prompt_fixed.md +7 -0
- package/dist/prompts/persona_init.md +15 -3
- package/dist/prompts/send_message.md +4 -2
- package/dist/prompts/start_conversation.md +4 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3164,9 +3164,11 @@ class Brain {
|
|
|
3164
3164
|
const memoryBlock = await this.buildMemoryBlock();
|
|
3165
3165
|
const scheduleBlock = await this.buildScheduleBlock(now);
|
|
3166
3166
|
const datetimeBlock = formatDatetime(now);
|
|
3167
|
+
const language = this.brainbase.language?.trim() || "English";
|
|
3167
3168
|
const instruction = initiate ? await loadPrompt("START_CONVERSATION") : await loadPrompt("SEND_MESSAGE");
|
|
3168
3169
|
const userPrompt = initiate ? [
|
|
3169
3170
|
`Current date and time: ${datetimeBlock}`,
|
|
3171
|
+
`Language: ${language}`,
|
|
3170
3172
|
scheduleBlock,
|
|
3171
3173
|
memoryBlock,
|
|
3172
3174
|
`Conversation so far:`,
|
|
@@ -3176,6 +3178,7 @@ class Brain {
|
|
|
3176
3178
|
|
|
3177
3179
|
`) : [
|
|
3178
3180
|
`Current date and time: ${datetimeBlock}`,
|
|
3181
|
+
`Language: ${language}`,
|
|
3179
3182
|
scheduleBlock,
|
|
3180
3183
|
memoryBlock,
|
|
3181
3184
|
`Conversation so far:`,
|
|
@@ -3196,6 +3199,8 @@ class Brain {
|
|
|
3196
3199
|
caller: initiate ? "start-conversation" : "send-message",
|
|
3197
3200
|
instruction: `${this.brainbase.baseSystemPrompt}
|
|
3198
3201
|
|
|
3202
|
+
Language: always reply in ${language}.
|
|
3203
|
+
|
|
3199
3204
|
${instruction}`,
|
|
3200
3205
|
messages,
|
|
3201
3206
|
tools,
|
|
@@ -3343,15 +3348,18 @@ ${blocks.join(`
|
|
|
3343
3348
|
return "";
|
|
3344
3349
|
}
|
|
3345
3350
|
}
|
|
3346
|
-
static async create(displayName, seed) {
|
|
3347
|
-
|
|
3351
|
+
static async create(displayName, seed, options = {}) {
|
|
3352
|
+
const language = (options.language ?? "English").trim() || "English";
|
|
3353
|
+
log7.debug(`Brain.create: starting name="${displayName}" language="${language}"`);
|
|
3348
3354
|
try {
|
|
3349
3355
|
const personaInitInstruction = await loadPrompt("PERSONA_INIT");
|
|
3350
3356
|
log7.debug(`Brain.create: generating description`);
|
|
3351
3357
|
const description = await llm.call(llm.models.identity, {
|
|
3352
3358
|
caller: "persona-init",
|
|
3353
3359
|
instruction: personaInitInstruction,
|
|
3354
|
-
message: seed
|
|
3360
|
+
message: [`Language: ${language}`, `Seed:`, seed].join(`
|
|
3361
|
+
|
|
3362
|
+
`)
|
|
3355
3363
|
});
|
|
3356
3364
|
log7.debug(`Brain.create: description returned (${description.length} chars)`);
|
|
3357
3365
|
const personaSystemInstruction = await loadPrompt("PERSONA_BASE_SYSTEM_PROMPT");
|
|
@@ -3359,7 +3367,13 @@ ${blocks.join(`
|
|
|
3359
3367
|
const generated = await llm.call(llm.models.identity, {
|
|
3360
3368
|
caller: "base-system-prompt",
|
|
3361
3369
|
instruction: personaSystemInstruction,
|
|
3362
|
-
message:
|
|
3370
|
+
message: [
|
|
3371
|
+
`Language: ${language}`,
|
|
3372
|
+
`Biography:`,
|
|
3373
|
+
description
|
|
3374
|
+
].join(`
|
|
3375
|
+
|
|
3376
|
+
`),
|
|
3363
3377
|
jsonSchemaName: "base-system-prompt",
|
|
3364
3378
|
jsonSchema: baseSystemPromptSchema
|
|
3365
3379
|
});
|
|
@@ -3378,6 +3392,7 @@ ${personaSystemFixed}`;
|
|
|
3378
3392
|
brainId,
|
|
3379
3393
|
spaceName: space.name,
|
|
3380
3394
|
displayName,
|
|
3395
|
+
language,
|
|
3381
3396
|
baseSystemPrompt,
|
|
3382
3397
|
dndReplyProbability: generated.dndReplyProbability,
|
|
3383
3398
|
startConversationCountThreshold: generated.startConversationCountThreshold,
|
|
@@ -4616,8 +4631,9 @@ async function deactivateBrain(brainId) {
|
|
|
4616
4631
|
await setActivated(brainId, false);
|
|
4617
4632
|
}
|
|
4618
4633
|
async function createBrain(displayName, seed, options) {
|
|
4619
|
-
|
|
4620
|
-
|
|
4634
|
+
const language = options.language?.trim() || "English";
|
|
4635
|
+
logger.debug(`createBrain: name="${displayName}" language="${language}" seed length=${seed.length} schedule=${options.schedule}`);
|
|
4636
|
+
const result = await Brain.create(displayName, seed, { language });
|
|
4621
4637
|
if ("error" in result) {
|
|
4622
4638
|
logger.error(`Failed to create brain "${displayName}": ${result.error}`);
|
|
4623
4639
|
process.exitCode = 1;
|
|
@@ -4625,9 +4641,9 @@ async function createBrain(displayName, seed, options) {
|
|
|
4625
4641
|
}
|
|
4626
4642
|
if (options.schedule) {
|
|
4627
4643
|
await result.brain.regenerateSchedules();
|
|
4628
|
-
logger.success(`Created brain "${displayName}" (${chalk2.cyan(result.brainId)}) with initial schedule`);
|
|
4644
|
+
logger.success(`Created brain "${displayName}" (${chalk2.cyan(result.brainId)}) [${language}] with initial schedule`);
|
|
4629
4645
|
} else {
|
|
4630
|
-
logger.success(`Created brain "${displayName}" (${chalk2.cyan(result.brainId)})`);
|
|
4646
|
+
logger.success(`Created brain "${displayName}" (${chalk2.cyan(result.brainId)}) [${language}]`);
|
|
4631
4647
|
}
|
|
4632
4648
|
}
|
|
4633
4649
|
async function removeBrain(brainId) {
|
|
@@ -4678,7 +4694,7 @@ function register3(program) {
|
|
|
4678
4694
|
description: "Manage brains"
|
|
4679
4695
|
});
|
|
4680
4696
|
cmd.command("list").description("List all brains").action(listBrains);
|
|
4681
|
-
cmd.command("create <name> [seed]").description("Create a new brain from a free-form seed").option("--no-schedule", "Skip generating the initial schedule").action(createBrain);
|
|
4697
|
+
cmd.command("create <name> [seed]").description("Create a new brain from a free-form seed").option("--no-schedule", "Skip generating the initial schedule").option("-l, --language <language>", "Primary chat language for the persona", "English").action(createBrain);
|
|
4682
4698
|
cmd.command("remove <brainId>").description("Remove a brain and its memory").action(removeBrain);
|
|
4683
4699
|
cmd.command("activate <brainId>").description("Activate a brain").action(activateBrain);
|
|
4684
4700
|
cmd.command("deactivate <brainId>").description("Deactivate a brain").action(deactivateBrain);
|
|
@@ -5771,7 +5787,61 @@ function BrainApp({
|
|
|
5771
5787
|
return;
|
|
5772
5788
|
}
|
|
5773
5789
|
setError(null);
|
|
5774
|
-
setStage({ kind: "
|
|
5790
|
+
setStage({ kind: "language", displayName: v });
|
|
5791
|
+
}
|
|
5792
|
+
}, undefined, false, undefined, this),
|
|
5793
|
+
error && /* @__PURE__ */ jsxDEV5(Text5, {
|
|
5794
|
+
color: "red",
|
|
5795
|
+
children: error
|
|
5796
|
+
}, undefined, false, undefined, this)
|
|
5797
|
+
]
|
|
5798
|
+
}, undefined, true, undefined, this);
|
|
5799
|
+
}
|
|
5800
|
+
if (stage.kind === "language") {
|
|
5801
|
+
return /* @__PURE__ */ jsxDEV5(Box4, {
|
|
5802
|
+
flexDirection: "column",
|
|
5803
|
+
children: [
|
|
5804
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5805
|
+
children: [
|
|
5806
|
+
chalk3.bold("Step 3/4"),
|
|
5807
|
+
" — Language for",
|
|
5808
|
+
" ",
|
|
5809
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5810
|
+
color: "cyan",
|
|
5811
|
+
children: stage.displayName
|
|
5812
|
+
}, undefined, false, undefined, this)
|
|
5813
|
+
]
|
|
5814
|
+
}, undefined, true, undefined, this),
|
|
5815
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5816
|
+
dimColor: true,
|
|
5817
|
+
children: "↑↓ to move, type to filter, enter to select"
|
|
5818
|
+
}, undefined, false, undefined, this),
|
|
5819
|
+
/* @__PURE__ */ jsxDEV5(Select, {
|
|
5820
|
+
prompt: "language> ",
|
|
5821
|
+
items: [
|
|
5822
|
+
"English",
|
|
5823
|
+
"Korean",
|
|
5824
|
+
"Japanese",
|
|
5825
|
+
"Chinese",
|
|
5826
|
+
"Spanish",
|
|
5827
|
+
"French",
|
|
5828
|
+
"German",
|
|
5829
|
+
"Portuguese",
|
|
5830
|
+
"Italian",
|
|
5831
|
+
"Russian",
|
|
5832
|
+
"Arabic",
|
|
5833
|
+
"Hindi",
|
|
5834
|
+
"Thai",
|
|
5835
|
+
"Vietnamese",
|
|
5836
|
+
"Indonesian"
|
|
5837
|
+
],
|
|
5838
|
+
onSelect: (language) => {
|
|
5839
|
+
setError(null);
|
|
5840
|
+
setStage({
|
|
5841
|
+
kind: "seed",
|
|
5842
|
+
displayName: stage.displayName,
|
|
5843
|
+
language
|
|
5844
|
+
});
|
|
5775
5845
|
}
|
|
5776
5846
|
}, undefined, false, undefined, this),
|
|
5777
5847
|
error && /* @__PURE__ */ jsxDEV5(Text5, {
|
|
@@ -5792,7 +5862,16 @@ function BrainApp({
|
|
|
5792
5862
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5793
5863
|
color: "cyan",
|
|
5794
5864
|
children: stage.displayName
|
|
5795
|
-
}, undefined, false, undefined, this)
|
|
5865
|
+
}, undefined, false, undefined, this),
|
|
5866
|
+
" ",
|
|
5867
|
+
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5868
|
+
dimColor: true,
|
|
5869
|
+
children: [
|
|
5870
|
+
"(",
|
|
5871
|
+
stage.language,
|
|
5872
|
+
")"
|
|
5873
|
+
]
|
|
5874
|
+
}, undefined, true, undefined, this)
|
|
5796
5875
|
]
|
|
5797
5876
|
}, undefined, true, undefined, this),
|
|
5798
5877
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
@@ -5816,7 +5895,9 @@ function BrainApp({
|
|
|
5816
5895
|
}
|
|
5817
5896
|
setBusy(true);
|
|
5818
5897
|
setError(null);
|
|
5819
|
-
Brain.create(stage.displayName, seed
|
|
5898
|
+
Brain.create(stage.displayName, seed, {
|
|
5899
|
+
language: stage.language
|
|
5900
|
+
}).then((result) => {
|
|
5820
5901
|
setBusy(false);
|
|
5821
5902
|
if ("error" in result) {
|
|
5822
5903
|
setError(`Brain creation failed: ${result.error} (fix seed, or type 'skip')`);
|
|
@@ -2,7 +2,10 @@ You are a prompt engineer specializing in LLM character embodiment for text-base
|
|
|
2
2
|
|
|
3
3
|
### INPUT
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
You will receive:
|
|
6
|
+
|
|
7
|
+
1. **Language:** The character's primary chat language (e.g. `English`, `Korean`, `日本語`). They text only in this language.
|
|
8
|
+
2. **Biography:** A free-form third-person character biography. It may contain any combination of:
|
|
6
9
|
|
|
7
10
|
- Name, age, era, or origin
|
|
8
11
|
- Psychological architecture, wounds, defense mechanisms
|
|
@@ -16,11 +19,20 @@ A free-form third-person character biography. It may contain any combination of:
|
|
|
16
19
|
|
|
17
20
|
**Preserve proper nouns in their original language.** Foreign-language names, places, and proper nouns from the input biography must be carried into the system prompt exactly as written—never transliterated, translated, or anglicized. If the biography contains a Korean name (e.g., "김민준"), the system prompt must use "김민준" throughout, not "Minjun Kim" or any English equivalent. The same applies to names in any non-Latin script (Hanzi, Kana, Cyrillic, Arabic, etc.) and to non-English proper nouns in Latin script that carry clear cultural identity (e.g., a French "Jean-Baptiste" stays "Jean-Baptiste," not "John Baptist"). Linguistic fidelity is non-negotiable. Do not normalize script, diacritics, or word order to fit English conventions.
|
|
18
21
|
|
|
22
|
+
### LANGUAGE FIXTURE
|
|
23
|
+
|
|
24
|
+
The **Language** field is mandatory for the system prompt you produce:
|
|
25
|
+
|
|
26
|
+
- State clearly that the character texts **only** in this language.
|
|
27
|
+
- All example phrases, verbal tics, sample messages, and "how you type when angry / soft / lying" lines MUST be written **in that language**.
|
|
28
|
+
- Typing-style rules must describe behavior in that language's real chat norms (spacing, particles, honorifics, slang, emoji use, romanization habits, etc.).
|
|
29
|
+
- Do not produce English chat examples unless the language is English.
|
|
30
|
+
|
|
19
31
|
---
|
|
20
32
|
|
|
21
33
|
### OUTPUT
|
|
22
34
|
|
|
23
|
-
A single, deployable system prompt
|
|
35
|
+
A single, deployable system prompt. No preamble. No explanation. No markdown wrappers around the prompt itself. The output must be copy-paste ready for immediate use as a system instruction. Structural framing may be in English for clarity, but every in-character speech sample and the hard language lock must honor the Language field.
|
|
24
36
|
|
|
25
37
|
The system prompt must be structured as follows:
|
|
26
38
|
|
|
@@ -12,6 +12,13 @@ You exist in a text chat. The user can only see your text messages. Everything y
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
### LANGUAGE
|
|
16
|
+
|
|
17
|
+
- You text only in the language specified for this persona (see `Language:` in your identity / turn context).
|
|
18
|
+
- Do not switch languages unless the user explicitly asks you to, and even then only if the persona would.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
15
22
|
### TEXT CHAT IS THE ONLY CHANNEL BETWEEN YOU AND THE USER
|
|
16
23
|
|
|
17
24
|
- You communicate with the user **only** through this text chat, in real time, message by message.
|
|
@@ -6,14 +6,17 @@ You are a depth psychologist and forensic biographer. Your task is to take a min
|
|
|
6
6
|
|
|
7
7
|
### INPUT FORMAT
|
|
8
8
|
|
|
9
|
-
You will receive a free-form text string
|
|
9
|
+
You will receive a free-form text string with two parts:
|
|
10
|
+
|
|
11
|
+
1. **Language:** The character's primary spoken and written language (e.g. `English`, `Korean`, `日本語`, `Español`). This is the language they text in.
|
|
12
|
+
2. **Seed:** A free-form character seed. It may contain any combination of the following—or none at all:
|
|
10
13
|
|
|
11
14
|
- Name, age, gender, or era
|
|
12
15
|
- Occupation, role, or social position
|
|
13
16
|
- A single trait, wound, preference, or situation
|
|
14
17
|
- A fragment of backstory, a line of dialogue, a physical description, or even just a mood
|
|
15
18
|
|
|
16
|
-
**Do not require structured fields.** Parse whatever is given, however it is given. If the
|
|
19
|
+
**Do not require structured fields in the seed.** Parse whatever is given, however it is given. If the seed is a single sentence ("a lonely lighthouse keeper who talks to the fog"), treat it as sufficient.
|
|
17
20
|
|
|
18
21
|
**If information is missing:**
|
|
19
22
|
|
|
@@ -22,7 +25,7 @@ You will receive a free-form text string. It may contain any combination of the
|
|
|
22
25
|
- Do not ask the user for clarification.
|
|
23
26
|
- Build the missing pieces as if they were always part of the original seed.
|
|
24
27
|
|
|
25
|
-
**Example
|
|
28
|
+
**Example seeds that are all valid:**
|
|
26
29
|
|
|
27
30
|
- "Elena Voss, 34, night shift nurse, hides exhaustion behind sarcasm"
|
|
28
31
|
- "a man who alphabetizes his spice rack but hasn't spoken to his brother in twelve years"
|
|
@@ -33,6 +36,15 @@ You will receive a free-form text string. It may contain any combination of the
|
|
|
33
36
|
|
|
34
37
|
**Preserve proper nouns in their original language.** Foreign-language names, places, and proper nouns from the input must be kept exactly as written—never transliterated, translated, or anglicized. If the input contains a Korean name (e.g., "김민준"), it stays "김민준" throughout the output, not "Minjun Kim" or any English equivalent. The same applies to names in any non-Latin script (Hanzi, Kana, Cyrillic, Arabic, etc.) and to non-English proper nouns in Latin script that carry clear cultural identity (e.g., a French "Jean-Baptiste" stays "Jean-Baptiste," not "John Baptist"). The character's cultural and linguistic identity is preserved in the spelling of their name. Do not "correct" or normalize script, diacritics, or word order.
|
|
35
38
|
|
|
39
|
+
### LANGUAGE FIXTURE
|
|
40
|
+
|
|
41
|
+
The **Language** field is load-bearing. Use it to shape the person:
|
|
42
|
+
|
|
43
|
+
- Their native / primary language for speech, thought, and text chat is this language.
|
|
44
|
+
- **Speech patterns, habitual phrases, verbal tics, and internal monologue examples MUST be written in that language** (as they would appear in a text message).
|
|
45
|
+
- Cultural texture, family language, and relational tone should cohere with that language when the seed does not specify otherwise.
|
|
46
|
+
- Do not default to English examples unless the language is English.
|
|
47
|
+
|
|
36
48
|
---
|
|
37
49
|
|
|
38
50
|
### OUTPUT REQUIREMENTS
|
|
@@ -2,6 +2,7 @@ You are roleplaying as a person in a text chat. You are not an AI assistant. You
|
|
|
2
2
|
|
|
3
3
|
You will be given:
|
|
4
4
|
- The current date and time (use it to ground your replies in "now")
|
|
5
|
+
- The persona's chat language (reply only in this language)
|
|
5
6
|
- Yesterday's, today's, and tomorrow's schedule summary (so you know where you are in your day)
|
|
6
7
|
- A list of facts already known about you and the user, drawn from your long-term memory
|
|
7
8
|
- A transcript of the conversation so far, formatted as `{persona name}@{time}: message` per line
|
|
@@ -16,8 +17,9 @@ You will be given:
|
|
|
16
17
|
5. ALWAYS reply in real time. The user expects a person typing back, not a polished essay.
|
|
17
18
|
6. ALWAYS filter every response through the persona's voice, vocabulary, and emotional weather.
|
|
18
19
|
7. ALWAYS stay consistent with the date, time, and schedules you were given. Do not contradict them.
|
|
19
|
-
8.
|
|
20
|
-
9.
|
|
20
|
+
8. ALWAYS reply in the persona's chat language given in the turn context. Do not switch languages unless the user explicitly asks and the persona would.
|
|
21
|
+
9. You only know what you actually know: facts from this conversation, the memory block you were given, or a successful `searchMemory` result. Outside of that, you do not know the user. Do not invent or assume user details.
|
|
22
|
+
10. If you are unsure about a fact involving the user, call `searchMemory` first. If memory has nothing, you simply do not know — ask, stay vague, or move on. Never fabricate.
|
|
21
23
|
|
|
22
24
|
### HOW TO REPLY
|
|
23
25
|
|
|
@@ -4,6 +4,7 @@ You are starting a conversation. The user has not sent a message. You are the on
|
|
|
4
4
|
|
|
5
5
|
You will be given:
|
|
6
6
|
- The current date and time (use it to ground your reply in "now")
|
|
7
|
+
- The persona's chat language (reply only in this language)
|
|
7
8
|
- Yesterday's, today's, and tomorrow's schedule summary (so you know where you are in your day)
|
|
8
9
|
- A list of facts already known about you and the user, drawn from your long-term memory
|
|
9
10
|
- A transcript of the conversation so far, formatted as `{persona name}@{time}: message` per line (each line carries the time it was sent)
|
|
@@ -17,8 +18,9 @@ You will be given:
|
|
|
17
18
|
5. ALWAYS reply in real time. The user expects a person typing back, not a polished essay.
|
|
18
19
|
6. ALWAYS filter every response through the persona's voice, vocabulary, and emotional weather.
|
|
19
20
|
7. ALWAYS stay consistent with the date, time, and schedules you were given. Do not contradict them.
|
|
20
|
-
8.
|
|
21
|
-
9.
|
|
21
|
+
8. ALWAYS reply in the persona's chat language given in the turn context. Do not switch languages unless the user explicitly asks and the persona would.
|
|
22
|
+
9. You only know what you actually know: facts from this conversation, the memory block you were given, or a successful `searchMemory` result. Outside of that, you do not know the user. Do not invent or assume user details.
|
|
23
|
+
10. If you are unsure about a fact involving the user, call `searchMemory` first. If memory has nothing, you simply do not know — ask, stay vague, or move on. Never fabricate.
|
|
22
24
|
|
|
23
25
|
### WHY YOU ARE WRITING
|
|
24
26
|
|