@space3-npm/cybersoul-client 1.4.4 → 1.4.7
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 +26 -2
- package/dist/client.js +100 -10
- package/dist/types.d.ts +34 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CyberSoulClientConfig, InteractParams, ProactiveParams, ProactiveResponse, OndemandEventParams, OndemandEventResponse, DispatcherIntent, InteractResponse, CharacterState, CoreMemory, UserCodex, HistoryEntry, LikedPicture } from "./types.js";
|
|
1
|
+
import { CyberSoulClientConfig, InteractParams, ProactiveParams, ProactiveResponse, OndemandEventParams, OndemandEventResponse, DispatcherIntent, InteractResponse, CharacterState, CoreMemory, UserCodex, HistoryEntry, LikedPicture, PersistedDynamicContext, SupportedLLMModel } from "./types.js";
|
|
2
2
|
export declare class CyberSoulClient {
|
|
3
3
|
private config;
|
|
4
4
|
private llm;
|
|
@@ -14,6 +14,18 @@ export declare class CyberSoulClient {
|
|
|
14
14
|
private fetchRemoteState;
|
|
15
15
|
private getWardrobePromptStr;
|
|
16
16
|
private generatePrimitive;
|
|
17
|
+
/**
|
|
18
|
+
* PATCH the backend dynamic context. The server applies stage-based
|
|
19
|
+
* dampening, familiarity soft-caps, hard floor, and stage re-evaluation,
|
|
20
|
+
* then returns the *authoritative* persisted `temperature` and
|
|
21
|
+
* `relationshipStage`. We surface those so callers (and ultimately the UI)
|
|
22
|
+
* can avoid recomputing the delta locally — local math would diverge from
|
|
23
|
+
* the server because the LLM-supplied `temperatureDelta` is just raw intent.
|
|
24
|
+
*
|
|
25
|
+
* Returns `null` when there's nothing to send, or when the request fails
|
|
26
|
+
* (failure is non-fatal for the chat turn; callers must treat `null` as
|
|
27
|
+
* "no fresh server snapshot available").
|
|
28
|
+
*/
|
|
17
29
|
private _updateDynamicContextInternal;
|
|
18
30
|
private normalizeRequestTypes;
|
|
19
31
|
private getElapsedTimeInfo;
|
|
@@ -78,6 +90,7 @@ export declare class CyberSoulClient {
|
|
|
78
90
|
interactParams?: InteractParams;
|
|
79
91
|
}): Promise<{
|
|
80
92
|
imageUrl: string;
|
|
93
|
+
imageMediaId?: string;
|
|
81
94
|
}>;
|
|
82
95
|
/**
|
|
83
96
|
* Manually synthesize voice audio outside of chat flow.
|
|
@@ -87,16 +100,27 @@ export declare class CyberSoulClient {
|
|
|
87
100
|
interactParams?: InteractParams;
|
|
88
101
|
}): Promise<{
|
|
89
102
|
audioUrl: string;
|
|
103
|
+
audioMediaId?: string;
|
|
90
104
|
durationSec?: number;
|
|
91
105
|
}>;
|
|
92
106
|
/**
|
|
93
107
|
* Fetches the current dynamic context and daily state.
|
|
94
108
|
*/
|
|
95
109
|
getState(): Promise<CharacterState>;
|
|
110
|
+
/**
|
|
111
|
+
* List the public LLM models the backend currently supports, including the
|
|
112
|
+
* `customConfigDefinition` schema for each model's `customSettings`.
|
|
113
|
+
*
|
|
114
|
+
* Use this to discover valid `provider` / `model` strings and the keys
|
|
115
|
+
* each model accepts via `llmConfig.customSettings`.
|
|
116
|
+
*/
|
|
117
|
+
listSupportedLLMs(): Promise<SupportedLLMModel[]>;
|
|
96
118
|
/**
|
|
97
119
|
* Updates the character's relationship temperature or mood.
|
|
120
|
+
* Returns the server-authoritative post-write `{ temperature, relationshipStage }`
|
|
121
|
+
* snapshot (or `null` if there was nothing to send / the request failed).
|
|
98
122
|
*/
|
|
99
|
-
updateDynamicContext(stateUpdate: DispatcherIntent["stateUpdate"], userAnalysis?: DispatcherIntent["userAnalysis"]): Promise<
|
|
123
|
+
updateDynamicContext(stateUpdate: DispatcherIntent["stateUpdate"], userAnalysis?: DispatcherIntent["userAnalysis"]): Promise<PersistedDynamicContext | null>;
|
|
100
124
|
/**
|
|
101
125
|
* Gift a new outfit to the character's wardrobe inventory.
|
|
102
126
|
*/
|
package/dist/client.js
CHANGED
|
@@ -116,9 +116,21 @@ export class CyberSoulClient {
|
|
|
116
116
|
}
|
|
117
117
|
return res.json();
|
|
118
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* PATCH the backend dynamic context. The server applies stage-based
|
|
121
|
+
* dampening, familiarity soft-caps, hard floor, and stage re-evaluation,
|
|
122
|
+
* then returns the *authoritative* persisted `temperature` and
|
|
123
|
+
* `relationshipStage`. We surface those so callers (and ultimately the UI)
|
|
124
|
+
* can avoid recomputing the delta locally — local math would diverge from
|
|
125
|
+
* the server because the LLM-supplied `temperatureDelta` is just raw intent.
|
|
126
|
+
*
|
|
127
|
+
* Returns `null` when there's nothing to send, or when the request fails
|
|
128
|
+
* (failure is non-fatal for the chat turn; callers must treat `null` as
|
|
129
|
+
* "no fresh server snapshot available").
|
|
130
|
+
*/
|
|
119
131
|
async _updateDynamicContextInternal(stateUpdate, userAnalysis) {
|
|
120
132
|
if (!stateUpdate && !userAnalysis)
|
|
121
|
-
return;
|
|
133
|
+
return null;
|
|
122
134
|
// Map TS schema intent (temperatureDelta) to match Backend payload schema (temperature)
|
|
123
135
|
const payload = { ...stateUpdate };
|
|
124
136
|
if (userAnalysis) {
|
|
@@ -132,10 +144,39 @@ export class CyberSoulClient {
|
|
|
132
144
|
const normalizedOngoingScene = this.normalizeOngoingSceneState(payload.ongoingScene);
|
|
133
145
|
payload.ongoingScene = normalizedOngoingScene || null;
|
|
134
146
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
let res;
|
|
148
|
+
try {
|
|
149
|
+
res = await this.apiFetch("/api/v1/cyber-soul/characters/dynamic-context", {
|
|
150
|
+
method: "PATCH",
|
|
151
|
+
body: JSON.stringify(payload),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
console.error("Failed to update dynamic context", e);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
console.error(`Failed to update dynamic context: HTTP ${res.status}`);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const body = (await res.json());
|
|
164
|
+
const temperature = typeof body.dynamicContext?.temperature === "number" &&
|
|
165
|
+
Number.isFinite(body.dynamicContext.temperature)
|
|
166
|
+
? body.dynamicContext.temperature
|
|
167
|
+
: undefined;
|
|
168
|
+
const relationshipStage = typeof body.relationshipStage === "string"
|
|
169
|
+
? body.relationshipStage
|
|
170
|
+
: undefined;
|
|
171
|
+
if (temperature === undefined && relationshipStage === undefined) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return { temperature, relationshipStage };
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
console.error("Failed to parse dynamic-context PATCH response", e);
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
139
180
|
}
|
|
140
181
|
normalizeRequestTypes(requestTypes) {
|
|
141
182
|
let normalized = requestTypes;
|
|
@@ -601,9 +642,15 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
601
642
|
};
|
|
602
643
|
}
|
|
603
644
|
// console.debug("[CyberSoulClient] Parsed Intent:", parsedIntent);
|
|
604
|
-
// 4. Update Backend State async
|
|
645
|
+
// 4. Update Backend State async (in parallel with media generation
|
|
646
|
+
// below). We keep the promise so we can resolve the
|
|
647
|
+
// server-authoritative `temperature` / `relationshipStage` and
|
|
648
|
+
// return it in the final response — clients cannot reproduce the
|
|
649
|
+
// server's stage dampening + soft caps locally, so this is the only
|
|
650
|
+
// reliable source of truth.
|
|
651
|
+
let persistedStatePromise = Promise.resolve(null);
|
|
605
652
|
if (parsedIntent && (parsedIntent.stateUpdate || parsedIntent.userAnalysis)) {
|
|
606
|
-
this._updateDynamicContextInternal(parsedIntent.stateUpdate, parsedIntent.userAnalysis);
|
|
653
|
+
persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate, parsedIntent.userAnalysis);
|
|
607
654
|
}
|
|
608
655
|
const resolvedTextResponse = typeof parsedIntent.textResponse === "string" &&
|
|
609
656
|
parsedIntent.textResponse.trim().length > 0
|
|
@@ -622,7 +669,9 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
622
669
|
// 5. Build Final Media Calls parallel
|
|
623
670
|
const mediaTasks = [];
|
|
624
671
|
let finalImageUrl = undefined;
|
|
672
|
+
let finalImageMediaId = undefined;
|
|
625
673
|
let finalAudioUrl = undefined;
|
|
674
|
+
let finalAudioMediaId = undefined;
|
|
626
675
|
let finalDurationSec = undefined;
|
|
627
676
|
// Output Event Trigger
|
|
628
677
|
if (parsedIntent.triggerEvent) {
|
|
@@ -656,6 +705,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
656
705
|
mediaTasks.push(this.generatePrimitive("image", imagePayload)
|
|
657
706
|
.then((res) => {
|
|
658
707
|
finalImageUrl = res.image_url;
|
|
708
|
+
finalImageMediaId = res.id;
|
|
659
709
|
})
|
|
660
710
|
.catch((e) => {
|
|
661
711
|
console.error("[CyberSoulClient] Image generation failed:", e);
|
|
@@ -679,6 +729,7 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
679
729
|
})
|
|
680
730
|
.then((res) => {
|
|
681
731
|
finalAudioUrl = res.audio_url;
|
|
732
|
+
finalAudioMediaId = res.id;
|
|
682
733
|
finalDurationSec = res.duration_sec;
|
|
683
734
|
})
|
|
684
735
|
.catch((e) => {
|
|
@@ -689,18 +740,26 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
689
740
|
}
|
|
690
741
|
// Wait for image/voice gens to return successfully
|
|
691
742
|
await Promise.all(mediaTasks);
|
|
743
|
+
// Await the dynamic-context PATCH alongside media so the final
|
|
744
|
+
// response carries the server's authoritative temperature/stage.
|
|
745
|
+
// This adds at most ~1 small request to the critical path; in
|
|
746
|
+
// practice the PATCH usually resolves before media generation.
|
|
747
|
+
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
|
692
748
|
return {
|
|
693
749
|
status: "success",
|
|
694
750
|
textResponse: resolvedTextResponse || "...",
|
|
695
751
|
actionText: parsedIntent.actionText || "",
|
|
696
752
|
imageUrl: finalImageUrl,
|
|
753
|
+
imageMediaId: finalImageMediaId,
|
|
697
754
|
audioUrl: finalAudioUrl,
|
|
755
|
+
audioMediaId: finalAudioMediaId,
|
|
698
756
|
likePreviousPicture: parsedIntent.likePreviousPicture,
|
|
699
757
|
durationSec: finalDurationSec,
|
|
700
758
|
triggeredEvent: parsedIntent.triggerEvent || undefined,
|
|
701
759
|
stateUpdate: parsedIntent.stateUpdate,
|
|
702
760
|
userAnalysis: parsedIntent.userAnalysis,
|
|
703
761
|
isEndTurn: parsedIntent.isEndTurn,
|
|
762
|
+
persistedDynamicContext,
|
|
704
763
|
};
|
|
705
764
|
}
|
|
706
765
|
catch (error) {
|
|
@@ -909,9 +968,11 @@ You MUST output ONLY a valid JSON object matching exactly this structure:
|
|
|
909
968
|
reason: parsedIntent.skipReason || "Character decided to skip proactive message based on mood/stage."
|
|
910
969
|
};
|
|
911
970
|
}
|
|
912
|
-
// Update Remote state if needed
|
|
971
|
+
// Update Remote state if needed (capture promise for authoritative
|
|
972
|
+
// server snapshot — see notes in interact()).
|
|
973
|
+
let persistedStatePromise = Promise.resolve(null);
|
|
913
974
|
if (parsedIntent.stateUpdate) {
|
|
914
|
-
this._updateDynamicContextInternal(parsedIntent.stateUpdate)
|
|
975
|
+
persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate);
|
|
915
976
|
}
|
|
916
977
|
const resolvedTextResponse = typeof parsedIntent.textResponse === "string" &&
|
|
917
978
|
parsedIntent.textResponse.trim().length > 0
|
|
@@ -929,21 +990,26 @@ You MUST output ONLY a valid JSON object matching exactly this structure:
|
|
|
929
990
|
}
|
|
930
991
|
// Handle Optional Media (Image only for proactive to save compute normally, but you can extend)
|
|
931
992
|
let finalImageUrl = undefined;
|
|
993
|
+
let finalImageMediaId = undefined;
|
|
932
994
|
if (parsedIntent.imageParams) {
|
|
933
995
|
try {
|
|
934
996
|
const res = await this.generatePrimitive("image", parsedIntent.imageParams);
|
|
935
997
|
finalImageUrl = res.image_url;
|
|
998
|
+
finalImageMediaId = res.id;
|
|
936
999
|
}
|
|
937
1000
|
catch (e) {
|
|
938
1001
|
console.error("[CyberSoulClient] Proactive Image generation failed:", e);
|
|
939
1002
|
}
|
|
940
1003
|
}
|
|
1004
|
+
const persistedDynamicContext = (await persistedStatePromise) ?? undefined;
|
|
941
1005
|
return {
|
|
942
1006
|
status: "success",
|
|
943
1007
|
textResponse: parsedIntent.textResponse,
|
|
944
1008
|
actionText: parsedIntent.actionText,
|
|
945
1009
|
imageUrl: finalImageUrl,
|
|
946
|
-
|
|
1010
|
+
imageMediaId: finalImageMediaId,
|
|
1011
|
+
stateUpdate: parsedIntent.stateUpdate,
|
|
1012
|
+
persistedDynamicContext,
|
|
947
1013
|
};
|
|
948
1014
|
}
|
|
949
1015
|
catch (error) {
|
|
@@ -984,6 +1050,7 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
984
1050
|
const res = await this.generatePrimitive("image", imageParams);
|
|
985
1051
|
return {
|
|
986
1052
|
imageUrl: res.image_url,
|
|
1053
|
+
imageMediaId: res.id,
|
|
987
1054
|
};
|
|
988
1055
|
}
|
|
989
1056
|
/**
|
|
@@ -1022,6 +1089,7 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1022
1089
|
});
|
|
1023
1090
|
return {
|
|
1024
1091
|
audioUrl: res.audio_url,
|
|
1092
|
+
audioMediaId: res.id,
|
|
1025
1093
|
durationSec: res.duration_sec,
|
|
1026
1094
|
};
|
|
1027
1095
|
}
|
|
@@ -1031,8 +1099,30 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
|
|
|
1031
1099
|
async getState() {
|
|
1032
1100
|
return this.fetchRemoteState();
|
|
1033
1101
|
}
|
|
1102
|
+
/**
|
|
1103
|
+
* List the public LLM models the backend currently supports, including the
|
|
1104
|
+
* `customConfigDefinition` schema for each model's `customSettings`.
|
|
1105
|
+
*
|
|
1106
|
+
* Use this to discover valid `provider` / `model` strings and the keys
|
|
1107
|
+
* each model accepts via `llmConfig.customSettings`.
|
|
1108
|
+
*/
|
|
1109
|
+
async listSupportedLLMs() {
|
|
1110
|
+
const res = await this.apiFetch("/api/v1/cyber-soul/llm-models");
|
|
1111
|
+
if (!res.ok) {
|
|
1112
|
+
throw new Error(`Failed to list supported LLMs: ${res.status}`);
|
|
1113
|
+
}
|
|
1114
|
+
const body = (await res.json());
|
|
1115
|
+
if (Array.isArray(body))
|
|
1116
|
+
return body;
|
|
1117
|
+
if (body && typeof body === "object" && Array.isArray(body.data)) {
|
|
1118
|
+
return body.data;
|
|
1119
|
+
}
|
|
1120
|
+
throw new Error("Unexpected response shape from /llm-models");
|
|
1121
|
+
}
|
|
1034
1122
|
/**
|
|
1035
1123
|
* Updates the character's relationship temperature or mood.
|
|
1124
|
+
* Returns the server-authoritative post-write `{ temperature, relationshipStage }`
|
|
1125
|
+
* snapshot (or `null` if there was nothing to send / the request failed).
|
|
1036
1126
|
*/
|
|
1037
1127
|
async updateDynamicContext(stateUpdate, userAnalysis) {
|
|
1038
1128
|
return this._updateDynamicContextInternal(stateUpdate, userAnalysis);
|
package/dist/types.d.ts
CHANGED
|
@@ -41,6 +41,18 @@ export interface InteractMetadata {
|
|
|
41
41
|
triggerEvent?: DispatcherIntent["triggerEvent"];
|
|
42
42
|
likePreviousPicture?: boolean;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Server-authoritative snapshot returned by PATCH /characters/dynamic-context
|
|
46
|
+
* after the backend applies stage dampening, familiarity soft caps, hard
|
|
47
|
+
* floors, rounding, and stage re-evaluation. Use this instead of recomputing
|
|
48
|
+
* the delta on the client.
|
|
49
|
+
*/
|
|
50
|
+
export interface PersistedDynamicContext {
|
|
51
|
+
/** Persisted absolute temperature (0-100), post all server-side adjustments. */
|
|
52
|
+
temperature?: number;
|
|
53
|
+
/** Persisted relationship stage label after re-evaluation. */
|
|
54
|
+
relationshipStage?: string;
|
|
55
|
+
}
|
|
44
56
|
export interface ProactiveParams {
|
|
45
57
|
history?: HistoryEntry[];
|
|
46
58
|
maxUnreplied?: number;
|
|
@@ -54,8 +66,12 @@ export interface ProactiveResponse {
|
|
|
54
66
|
textResponse?: string;
|
|
55
67
|
actionText?: string;
|
|
56
68
|
imageUrl?: string;
|
|
69
|
+
imageMediaId?: string;
|
|
57
70
|
audioUrl?: string;
|
|
71
|
+
audioMediaId?: string;
|
|
58
72
|
stateUpdate?: DispatcherIntent["stateUpdate"];
|
|
73
|
+
/** Server-authoritative post-write snapshot (see PersistedDynamicContext). */
|
|
74
|
+
persistedDynamicContext?: PersistedDynamicContext;
|
|
59
75
|
error?: string;
|
|
60
76
|
}
|
|
61
77
|
export interface InteractParams {
|
|
@@ -102,7 +118,9 @@ export interface InteractResponse {
|
|
|
102
118
|
textResponse: string;
|
|
103
119
|
actionText?: string;
|
|
104
120
|
imageUrl?: string;
|
|
121
|
+
imageMediaId?: string;
|
|
105
122
|
audioUrl?: string;
|
|
123
|
+
audioMediaId?: string;
|
|
106
124
|
likePreviousPicture?: boolean;
|
|
107
125
|
durationSec?: number;
|
|
108
126
|
triggeredEvent?: {
|
|
@@ -114,6 +132,8 @@ export interface InteractResponse {
|
|
|
114
132
|
stateUpdate?: DispatcherIntent["stateUpdate"];
|
|
115
133
|
userAnalysis?: DispatcherIntent["userAnalysis"];
|
|
116
134
|
isEndTurn?: boolean;
|
|
135
|
+
/** Server-authoritative post-write snapshot (see PersistedDynamicContext). */
|
|
136
|
+
persistedDynamicContext?: PersistedDynamicContext;
|
|
117
137
|
error?: string;
|
|
118
138
|
}
|
|
119
139
|
export interface OngoingSceneState {
|
|
@@ -287,6 +307,20 @@ export interface IVoiceModel {
|
|
|
287
307
|
isPublic: boolean;
|
|
288
308
|
pointsPerGeneration: number;
|
|
289
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Public LLM model entry returned by `GET /api/v1/cyber-soul/llm-models`.
|
|
312
|
+
*
|
|
313
|
+
* - `provider` is the value to pass as `llmConfig.provider`.
|
|
314
|
+
* - `name` is the value to pass as `llmConfig.model`.
|
|
315
|
+
* - `customConfigDefinition` describes the keys (and their constraints) that
|
|
316
|
+
* the model accepts via `llmConfig.customSettings`.
|
|
317
|
+
*/
|
|
318
|
+
export interface SupportedLLMModel {
|
|
319
|
+
id: string;
|
|
320
|
+
name: string;
|
|
321
|
+
provider: string;
|
|
322
|
+
customConfigDefinition: IModelCustomConfigField[];
|
|
323
|
+
}
|
|
290
324
|
export interface ICharacterProfile {
|
|
291
325
|
id: string;
|
|
292
326
|
name: string;
|