@space3-npm/cybersoul-client 1.4.25 → 1.4.27
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 +6 -0
- package/dist/client.js +76 -13
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -151,6 +151,12 @@ export declare class CyberSoulClient {
|
|
|
151
151
|
* snapshot (or `null` if there was nothing to send / the request failed).
|
|
152
152
|
*/
|
|
153
153
|
updateDynamicContext(stateUpdate: DispatcherIntent["stateUpdate"], userAnalysis?: DispatcherIntent["userAnalysis"]): Promise<PersistedDynamicContext | null>;
|
|
154
|
+
/**
|
|
155
|
+
* Restores the server-side relationship temperature to an exact absolute
|
|
156
|
+
* value. Used by chat recall, where inverse deltas are not accurate once the
|
|
157
|
+
* backend has applied dampening, caps, and stage re-evaluation.
|
|
158
|
+
*/
|
|
159
|
+
restoreDynamicContextTemperature(temperatureAbsolute: number): Promise<PersistedDynamicContext | null>;
|
|
154
160
|
/**
|
|
155
161
|
* Gift a new outfit to the character's wardrobe inventory.
|
|
156
162
|
* Returns the number of wardrobe items the backend created (the
|
package/dist/client.js
CHANGED
|
@@ -809,21 +809,49 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
809
809
|
"\n\nReturn only valid JSON matching the schema. Escape newlines inside JSON strings with \\n. Keep imageParams values in ENGLISH and use the provided enums.",
|
|
810
810
|
},
|
|
811
811
|
];
|
|
812
|
-
// 3. Local Execute LLM
|
|
813
|
-
|
|
814
|
-
//
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
812
|
+
// 3. Local Execute LLM (retry on non-actionable parse).
|
|
813
|
+
// A "non-actionable" parse yields no usable `textResponse` AND no
|
|
814
|
+
// media intent — i.e. the LLM hiccuped (empty body, truncated or
|
|
815
|
+
// garbled JSON, or an empty textResponse field). Retrying the
|
|
816
|
+
// generation is far better than silently echoing the user's own
|
|
817
|
+
// message back as the character's reply (the historical
|
|
818
|
+
// `: params.userMessage` fallback below), which downstream
|
|
819
|
+
// consumers correctly reject as a non-actionable response.
|
|
820
|
+
const MAX_DISPATCH_ATTEMPTS = 3;
|
|
821
|
+
let parsedIntent = { textResponse: "" };
|
|
822
|
+
for (let attempt = 1; attempt <= MAX_DISPATCH_ATTEMPTS; attempt++) {
|
|
823
|
+
const rawLlmResponse = await this.llm.generate(promptMessages, 15000, 0.7);
|
|
824
|
+
// console.debug("[CyberSoulClient] Raw LLM Response:", rawLlmResponse);
|
|
825
|
+
try {
|
|
826
|
+
parsedIntent = robustJsonParse(rawLlmResponse, "Dispatcher fallback", { textResponse: "", actionText: "", isEndTurn: false });
|
|
827
|
+
}
|
|
828
|
+
catch (e) {
|
|
829
|
+
console.warn("[CyberSoulClient] JSON parse failed, falling back to raw text:", e);
|
|
830
|
+
// Fallback robust mode - just text if completely broken
|
|
831
|
+
parsedIntent = {
|
|
832
|
+
textResponse: rawLlmResponse.replace(/^[\`\s]+|[\`\s]+$/g, "").trim(),
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
// console.debug("[CyberSoulClient] Parsed Intent:", parsedIntent);
|
|
836
|
+
const hasUsableText = typeof parsedIntent.textResponse === "string" &&
|
|
837
|
+
parsedIntent.textResponse.trim().length > 0;
|
|
838
|
+
const hasMediaIntent = !!parsedIntent.imageParams || !!parsedIntent.voiceArgs;
|
|
839
|
+
if (hasUsableText || hasMediaIntent) {
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
console.warn(`[CyberSoulClient] interact produced a non-actionable intent (attempt ${attempt}/${MAX_DISPATCH_ATTEMPTS}); ${attempt < MAX_DISPATCH_ATTEMPTS ? "retrying" : "giving up"}.`);
|
|
818
843
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
844
|
+
// After exhausting retries, fail fast instead of echoing the user's
|
|
845
|
+
// own message back as the character's reply. The `: params.userMessage`
|
|
846
|
+
// fallback used below is only ever reached for media-only turns now.
|
|
847
|
+
{
|
|
848
|
+
const finalHasUsableText = typeof parsedIntent.textResponse === "string" &&
|
|
849
|
+
parsedIntent.textResponse.trim().length > 0;
|
|
850
|
+
const finalHasMediaIntent = !!parsedIntent.imageParams || !!parsedIntent.voiceArgs;
|
|
851
|
+
if (!finalHasUsableText && !finalHasMediaIntent) {
|
|
852
|
+
throw new Error("LLM returned a non-actionable response after retries (no usable textResponse and no media intent). Check LLM template/provider alignment.");
|
|
853
|
+
}
|
|
825
854
|
}
|
|
826
|
-
// console.debug("[CyberSoulClient] Parsed Intent:", parsedIntent);
|
|
827
855
|
// 4. Update Backend State async (in parallel with media generation
|
|
828
856
|
// below). We keep the promise so we can resolve the
|
|
829
857
|
// server-authoritative `temperature` / `relationshipStage` and
|
|
@@ -1491,6 +1519,41 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1491
1519
|
async updateDynamicContext(stateUpdate, userAnalysis) {
|
|
1492
1520
|
return this._updateDynamicContextInternal(stateUpdate, userAnalysis);
|
|
1493
1521
|
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Restores the server-side relationship temperature to an exact absolute
|
|
1524
|
+
* value. Used by chat recall, where inverse deltas are not accurate once the
|
|
1525
|
+
* backend has applied dampening, caps, and stage re-evaluation.
|
|
1526
|
+
*/
|
|
1527
|
+
async restoreDynamicContextTemperature(temperatureAbsolute) {
|
|
1528
|
+
if (!Number.isFinite(temperatureAbsolute))
|
|
1529
|
+
return null;
|
|
1530
|
+
const normalizedAbsolute = Math.max(0, Math.min(100, Math.round(temperatureAbsolute * 10) / 10));
|
|
1531
|
+
try {
|
|
1532
|
+
const res = await this.apiFetch("/api/v1/cyber-soul/characters/dynamic-context", {
|
|
1533
|
+
method: "PATCH",
|
|
1534
|
+
body: JSON.stringify({ temperatureAbsolute: normalizedAbsolute }),
|
|
1535
|
+
});
|
|
1536
|
+
if (!res.ok)
|
|
1537
|
+
return null;
|
|
1538
|
+
const payload = (await res.json());
|
|
1539
|
+
if (payload?.status !== "success")
|
|
1540
|
+
return null;
|
|
1541
|
+
if (typeof payload.dynamicContext?.temperature !== "number")
|
|
1542
|
+
return null;
|
|
1543
|
+
if (!Number.isFinite(payload.dynamicContext.temperature))
|
|
1544
|
+
return null;
|
|
1545
|
+
return {
|
|
1546
|
+
temperature: payload.dynamicContext.temperature,
|
|
1547
|
+
relationshipStage: typeof payload.relationshipStage === "string"
|
|
1548
|
+
? payload.relationshipStage
|
|
1549
|
+
: undefined,
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
catch (e) {
|
|
1553
|
+
console.error("restoreDynamicContextTemperature failed", e);
|
|
1554
|
+
return null;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1494
1557
|
/**
|
|
1495
1558
|
* Gift a new outfit to the character's wardrobe inventory.
|
|
1496
1559
|
* Returns the number of wardrobe items the backend created (the
|