@space3-npm/cybersoul-client 1.2.0 → 1.2.1
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/client.d.ts +1 -0
- package/dist/client.js +57 -15
- package/dist/types.d.ts +8 -4
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare class CyberSoulClient {
|
|
|
32
32
|
* If the payload is already the inner args object (no voiceArgs wrapper), uses it as-is.
|
|
33
33
|
*/
|
|
34
34
|
private extractVoiceArgsFromLlmResponse;
|
|
35
|
+
private buildHistoryTranscript;
|
|
35
36
|
/**
|
|
36
37
|
* Evaluates and triggers an on-demand event, intelligently deciding if an outfit change is needed.
|
|
37
38
|
*/
|
package/dist/client.js
CHANGED
|
@@ -84,6 +84,9 @@ Interaction Boundaries: ${state.interaction_boundaries || "None"}`);
|
|
|
84
84
|
// [2] SITUATIONAL CONTEXT
|
|
85
85
|
contextParts.push(`\n[SITUATIONAL CONTEXT]
|
|
86
86
|
Current time: ${new Date(state.current_time || Date.now()).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}`);
|
|
87
|
+
if (dyn.ongoingScene) {
|
|
88
|
+
contextParts.push(`Last Known Scene: ${dyn.ongoingScene} (May be outdated if significant time has passed)`);
|
|
89
|
+
}
|
|
87
90
|
if (state.active_event) {
|
|
88
91
|
contextParts.push(`Active Event: ${state.active_event.title} (${state.active_event.narrative_context})`);
|
|
89
92
|
}
|
|
@@ -96,6 +99,24 @@ Current time: ${new Date(state.current_time || Date.now()).toLocaleString("zh-CN
|
|
|
96
99
|
if (localContext) {
|
|
97
100
|
contextParts.push(`Additional Context: ${localContext}`);
|
|
98
101
|
}
|
|
102
|
+
if (state.core_memory) {
|
|
103
|
+
let memoryLines = ["[CORE MEMORY]"];
|
|
104
|
+
const mem = state.core_memory;
|
|
105
|
+
if (mem.relationshipStatus)
|
|
106
|
+
memoryLines.push(`Relationship Status: ${mem.relationshipStatus}`);
|
|
107
|
+
if (mem.identityAnchors?.length)
|
|
108
|
+
memoryLines.push(`Identity Anchors: ${mem.identityAnchors.join(", ")}`);
|
|
109
|
+
if (mem.activeArcs?.length)
|
|
110
|
+
memoryLines.push(`Active Arcs: ${mem.activeArcs.join(", ")}`);
|
|
111
|
+
if (mem.keyEvents?.length)
|
|
112
|
+
memoryLines.push(`Key Events: ${mem.keyEvents.join(", ")}`);
|
|
113
|
+
if (mem.appointments?.length) {
|
|
114
|
+
memoryLines.push(`Appointments: ${mem.appointments.map(a => `[${a.date || ''} ${a.time || ''}] ${a.title} with ${a.withWhom || 'User'}`).join("; ")}`);
|
|
115
|
+
}
|
|
116
|
+
if (memoryLines.length > 1) {
|
|
117
|
+
contextParts.push(`\n${memoryLines.join("\n")}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
99
120
|
// [3] USER CODEX (Relationships dynamically evaluated)
|
|
100
121
|
if (state.user_codex) {
|
|
101
122
|
const { basicInfo, psychological, familiarityScore = 0 } = state.user_codex;
|
|
@@ -144,7 +165,7 @@ ${scenarioContext}
|
|
|
144
165
|
getImageSchemaParams() {
|
|
145
166
|
return `"imageParams": {
|
|
146
167
|
"mode": "structured | full-prompt (use 'full-prompt' for highly dynamic actions)",
|
|
147
|
-
"full_prompt": "Use only if mode is full-prompt. Highly detailed visual description in ENGLISH. CRITICAL: MUST use a strict first-person perspective exclusively from the USER's eyes. DO NOT describe the user (e.g., 'a man', 'the driver') as visible in the scene because the camera IS the user. Start with 'POV: '. Describe ONLY the character looking back at the camera and their immediate surroundings. MUST align with the character's current Active
|
|
168
|
+
"full_prompt": "Use only if mode is full-prompt. Highly detailed visual description in ENGLISH. CRITICAL: MUST use a strict first-person perspective exclusively from the USER's eyes. DO NOT describe the user (e.g., 'a man', 'the driver') as visible in the scene because the camera IS the user. Start with 'POV: '. Describe ONLY the character looking back at the camera and their immediate surroundings. MUST align with the character's current Active exposure state or Wardrobe depends on the scene",
|
|
148
169
|
"expression": "seductive | cute | happy | sleepy | dazed | pleased | default (Strictly choose ONE from this exact list. DO NOT invent new words like 'shy'.)",
|
|
149
170
|
"condition": "normal | sweaty | wet | messy | oily (Strictly choose ONE from this exact list.)",
|
|
150
171
|
"view_angle": "front | side | high_angle | from_below | boyfriend_view | selfie | mirror (Strictly choose ONE from this exact list.)",
|
|
@@ -214,6 +235,21 @@ ${scenarioContext}
|
|
|
214
235
|
}
|
|
215
236
|
return payload;
|
|
216
237
|
}
|
|
238
|
+
buildHistoryTranscript(history, state) {
|
|
239
|
+
if (!history || history.length === 0)
|
|
240
|
+
return "";
|
|
241
|
+
const recentHistory = history.slice(-20);
|
|
242
|
+
const agentName = state.dynamic_context?.agentNickname || state.name || "Agent";
|
|
243
|
+
const userName = state.dynamic_context?.userNickname || "User";
|
|
244
|
+
const mapped = recentHistory.map((msg) => {
|
|
245
|
+
const speaker = msg.role === 'user' ? userName : (msg.role === 'assistant' || msg.role === 'agent' ? agentName : msg.role);
|
|
246
|
+
const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
|
|
247
|
+
const action = msg.actionText ? ` ${msg.actionText}` : "";
|
|
248
|
+
const media = msg.mediaHint ? ` [${msg.mediaHint}]` : "";
|
|
249
|
+
return `${speaker}:${action} ${content}${media}`;
|
|
250
|
+
});
|
|
251
|
+
return `[CHAT HISTORY]\n${mapped.join('\n')}\n\n`;
|
|
252
|
+
}
|
|
217
253
|
/**
|
|
218
254
|
* Evaluates and triggers an on-demand event, intelligently deciding if an outfit change is needed.
|
|
219
255
|
*/
|
|
@@ -242,15 +278,15 @@ You MUST output ONLY a valid JSON object matching this exact structure:
|
|
|
242
278
|
}
|
|
243
279
|
|
|
244
280
|
CRITICAL: Output MUST be ONLY valid JSON with no markdown block wrappers. Do NOT wrap the JSON in \`\`\`json or add conversational text.`;
|
|
281
|
+
const transcript = this.buildHistoryTranscript(params.interactParams?.history, state);
|
|
282
|
+
const userMessage = params.interactParams?.userMessage ?
|
|
283
|
+
`${state.dynamic_context?.userNickname || "User"}: ${params.interactParams.userMessage}` :
|
|
284
|
+
`Event Proposal: ${params.eventDescription}`;
|
|
245
285
|
const promptMessages = [
|
|
246
286
|
{ role: "system", content: systemPrompt },
|
|
247
|
-
...(params.interactParams?.history || []).map((msg) => ({
|
|
248
|
-
role: msg.role,
|
|
249
|
-
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)
|
|
250
|
-
})),
|
|
251
287
|
{
|
|
252
288
|
role: "user",
|
|
253
|
-
content: `${
|
|
289
|
+
content: `${transcript}${userMessage}\n\n**CRITICAL REMINDER**: You MUST output your final response exactly in the JSON format specified in the system prompt. DO NOT output plain text directly. CRITICAL: You must properly escape all newlines inside string values using \\n. Never use raw, unescaped line breaks inside the JSON strings.`,
|
|
254
290
|
},
|
|
255
291
|
];
|
|
256
292
|
// 3. Evaluate with LLM
|
|
@@ -324,12 +360,12 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
324
360
|
{
|
|
325
361
|
${this.getImageSchemaParams()}
|
|
326
362
|
}`;
|
|
363
|
+
const transcript = this.buildHistoryTranscript(params.interactParams?.history, state);
|
|
327
364
|
const promptMessages = [
|
|
328
365
|
{ role: "system", content: prompt },
|
|
329
|
-
...(params.interactParams?.history || []),
|
|
330
366
|
{
|
|
331
367
|
role: "user",
|
|
332
|
-
content:
|
|
368
|
+
content: `${transcript}Scene Description: "${params.sceneDescription}"\n\n**CRITICAL REMINDER**: You MUST output your final response exactly in the JSON format specified in the system prompt. DO NOT output plain text dialogue directly. CRITICAL: You must properly escape all newlines inside string values using \\n. Never use raw, unescaped line breaks inside the JSON strings. For 'imageParams', ALL values MUST be in ENGLISH ONLY without exception, and you MUST use the exact English enum strings provided.`,
|
|
333
369
|
},
|
|
334
370
|
];
|
|
335
371
|
const llmRes = await this.llm.generate(promptMessages, 800, 0.4);
|
|
@@ -359,12 +395,12 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
359
395
|
{
|
|
360
396
|
${this.getVoiceSchemaFromState(state)}
|
|
361
397
|
}`;
|
|
398
|
+
const transcript = this.buildHistoryTranscript(params.interactParams?.history, state);
|
|
362
399
|
const promptMessages = [
|
|
363
400
|
{ role: "system", content: prompt },
|
|
364
|
-
...(params.interactParams?.history || []),
|
|
365
401
|
{
|
|
366
402
|
role: "user",
|
|
367
|
-
content:
|
|
403
|
+
content: `${transcript}Text: "${params.text}"\n\n**CRITICAL REMINDER**: You MUST output your final response exactly in the JSON format specified in the system prompt. DO NOT output plain text dialogue directly. CRITICAL: You must properly escape all newlines inside string values using \\n. Never use raw, unescaped line breaks inside the JSON strings.`,
|
|
368
404
|
},
|
|
369
405
|
];
|
|
370
406
|
const llmRes = await this.llm.generate(promptMessages, 800, 0.3);
|
|
@@ -530,9 +566,9 @@ Voice direction for voiceArgs: ${this.getVoiceDirectorInstruction(state)}
|
|
|
530
566
|
|
|
531
567
|
Output JSON Schema:
|
|
532
568
|
{
|
|
533
|
-
"textResponse": "The clean spoken dialogue ONLY
|
|
569
|
+
"textResponse": "The clean spoken dialogue ONLY. CRITICAL: Strictly NO parentheses, NO actions, NO tone descriptors. Tone/voice descriptors MUST go inside voiceArgs, and physical actions MUST go inside actionText. If nothing to speak, output an empty string.",
|
|
534
570
|
"actionText": "Any non-verbal actions, inner thoughts, or scene descriptions in parentheses (e.g. '(低头看向你)'). Output empty string if none.",
|
|
535
|
-
"stateUpdate": { "temperatureDelta": 1, "userNickname": "What you now call the user", "agentNickname": "What the user calls you", "talkingStyle": "Current mood/style of talking" },
|
|
571
|
+
"stateUpdate": { "temperatureDelta": 1, "userNickname": "What you now call the user", "agentNickname": "What the user calls you", "talkingStyle": "Current mood/style of talking", "ongoingScene": "A concise 1-sentence description of the current physical scene and activity. Update this if the physical scene or activity shifts. Output empty string if the scene has concluded or significant time has passed." },
|
|
536
572
|
"userAnalysis": { "newFactsLearned": [{ "category": "occupation", "value": "Software Engineer" }] },
|
|
537
573
|
"triggerEvent": {
|
|
538
574
|
${this.getEventSchemaParams(state.dynamic_context?.userNickname)}
|
|
@@ -541,17 +577,19 @@ Output JSON Schema:
|
|
|
541
577
|
${this.getVoiceSchemaFromState(state)}
|
|
542
578
|
}
|
|
543
579
|
Note: If "imageParams", "voiceArgs", "triggerEvent", or "userAnalysis" are not needed, set their values to null instead of omitting the keys. 'stateUpdate' MUST NEVER BE NULL. Output MUST be ONLY valid JSON with no markdown block wrappers. CRITICAL: Ensure your JSON has exactly one root object \`{\` and ends with exactly one \`}\` without any trailing garbage or extra brackets.`;
|
|
580
|
+
const transcript = this.buildHistoryTranscript(params.history, state);
|
|
581
|
+
const userName = state.dynamic_context?.userNickname || "User";
|
|
544
582
|
const promptMessages = [
|
|
545
583
|
{ role: "system", content: systemPrompt },
|
|
546
|
-
...(params.history || []),
|
|
547
584
|
{
|
|
548
585
|
role: "user",
|
|
549
|
-
content:
|
|
586
|
+
content: transcript + userName + ": " +
|
|
587
|
+
params.userMessage +
|
|
550
588
|
"\n\n**CRITICAL REMINDER**: You MUST output your final response exactly in the JSON format specified in the system prompt. DO NOT output plain text dialogue directly. CRITICAL: You must properly escape all newlines inside string values using \\n. Never use raw, unescaped line breaks inside the JSON strings. For 'imageParams', ALL values MUST be in ENGLISH ONLY without exception, and you MUST use the exact English enum strings provided.",
|
|
551
589
|
},
|
|
552
590
|
];
|
|
553
591
|
// 3. Local Execute LLM
|
|
554
|
-
const rawLlmResponse = await this.llm.generate(promptMessages,
|
|
592
|
+
const rawLlmResponse = await this.llm.generate(promptMessages, 15000, 0.7);
|
|
555
593
|
// console.debug("[CyberSoulClient] Raw LLM Response:", rawLlmResponse);
|
|
556
594
|
let parsedIntent;
|
|
557
595
|
try {
|
|
@@ -616,6 +654,10 @@ Note: If "imageParams", "voiceArgs", "triggerEvent", or "userAnalysis" are not n
|
|
|
616
654
|
? parsedIntent.voiceArgs
|
|
617
655
|
: {};
|
|
618
656
|
let textForVoice = resolvedTextResponse;
|
|
657
|
+
// One final bulletproof regex wash to strip (smiles) and *laughs* just in case the LLM disobeys
|
|
658
|
+
if (typeof textForVoice === "string") {
|
|
659
|
+
textForVoice = textForVoice.replace(/[\((\[【\*].*?[\))\]】\*]/g, '').trim();
|
|
660
|
+
}
|
|
619
661
|
if (typeof textForVoice !== "string" || textForVoice.trim().length === 0) {
|
|
620
662
|
textForVoice = "...";
|
|
621
663
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -16,14 +16,17 @@ export declare enum InteractRequestType {
|
|
|
16
16
|
IMAGE = "image",
|
|
17
17
|
VOICE = "voice"
|
|
18
18
|
}
|
|
19
|
+
export interface HistoryEntry {
|
|
20
|
+
role: string;
|
|
21
|
+
content: string;
|
|
22
|
+
actionText?: string;
|
|
23
|
+
mediaHint?: string;
|
|
24
|
+
}
|
|
19
25
|
export interface InteractParams {
|
|
20
26
|
userMessage: string;
|
|
21
27
|
localContext?: string;
|
|
22
28
|
requestTypes?: InteractRequestType[];
|
|
23
|
-
history?:
|
|
24
|
-
role: string;
|
|
25
|
-
content: string;
|
|
26
|
-
}[];
|
|
29
|
+
history?: HistoryEntry[];
|
|
27
30
|
onTextReady?: (textResponse: string) => void;
|
|
28
31
|
}
|
|
29
32
|
export interface OndemandEventParams {
|
|
@@ -80,6 +83,7 @@ export interface DispatcherIntent {
|
|
|
80
83
|
userNickname?: string;
|
|
81
84
|
agentNickname?: string;
|
|
82
85
|
talkingStyle?: string;
|
|
86
|
+
ongoingScene?: string;
|
|
83
87
|
};
|
|
84
88
|
triggerEvent?: {
|
|
85
89
|
eventTitle?: string;
|