@space3-npm/cybersoul-client 1.4.15 → 1.4.17
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.js +105 -5
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +13 -0
- package/dist/types.d.ts +37 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InteractRequestType, } from "./types.js";
|
|
2
2
|
import { robustJsonParse } from "./utils/json.utils.js";
|
|
3
3
|
import { GenericLLMProvider } from "./llm.provider.js";
|
|
4
|
-
import { CyberSoulApiError, CyberSoulAuthError, CyberSoulError, CyberSoulInsufficientPointsError, CyberSoulNetworkError, CyberSoulTimeoutError, CyberSoulWalletError, } from "./errors.js";
|
|
4
|
+
import { CyberSoulApiError, CyberSoulAuthError, CyberSoulError, CyberSoulInsufficientPointsError, CyberSoulNetworkError, CyberSoulSensitiveContentError, CyberSoulTimeoutError, CyberSoulWalletError, } from "./errors.js";
|
|
5
5
|
export class CyberSoulClient {
|
|
6
6
|
config;
|
|
7
7
|
llm;
|
|
@@ -146,6 +146,9 @@ export class CyberSoulClient {
|
|
|
146
146
|
if (code === "WALLET_DEDUCTION_ERROR") {
|
|
147
147
|
throw new CyberSoulWalletError(endpoint, "POST", res.status, detailedMessage, errData, code);
|
|
148
148
|
}
|
|
149
|
+
if (code === "E005") {
|
|
150
|
+
throw new CyberSoulSensitiveContentError(endpoint, "POST", res.status, detailedMessage, errData, code);
|
|
151
|
+
}
|
|
149
152
|
if (res.status === 401 || res.status === 403) {
|
|
150
153
|
throw new CyberSoulAuthError(endpoint, "POST", res.status, detailedMessage, errData);
|
|
151
154
|
}
|
|
@@ -542,6 +545,14 @@ ${isProactive
|
|
|
542
545
|
affected,
|
|
543
546
|
};
|
|
544
547
|
}
|
|
548
|
+
if (err instanceof CyberSoulSensitiveContentError) {
|
|
549
|
+
return {
|
|
550
|
+
kind: "sensitive-content",
|
|
551
|
+
code: err.code,
|
|
552
|
+
message: err.message,
|
|
553
|
+
affected,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
545
556
|
return {
|
|
546
557
|
kind: "unknown",
|
|
547
558
|
message: err.message,
|
|
@@ -724,6 +735,34 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
724
735
|
if (parsedIntent && (parsedIntent.stateUpdate || parsedIntent.userAnalysis)) {
|
|
725
736
|
persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate, parsedIntent.userAnalysis);
|
|
726
737
|
}
|
|
738
|
+
// Fire `onStateReady` the moment the dynamic-context PATCH resolves
|
|
739
|
+
// (or immediately, when no state update was emitted). This is
|
|
740
|
+
// independent of media generation, so the UI can stop showing
|
|
741
|
+
// "updating…" on temperature / relationship stage well before the
|
|
742
|
+
// (potentially slow) image task finishes. Errors are swallowed:
|
|
743
|
+
// an authoritative snapshot is best-effort, the optimistic delta
|
|
744
|
+
// already applied client-side is the fallback.
|
|
745
|
+
if (params.onStateReady) {
|
|
746
|
+
const stateReadyCb = params.onStateReady;
|
|
747
|
+
persistedStatePromise
|
|
748
|
+
.then((persisted) => {
|
|
749
|
+
try {
|
|
750
|
+
stateReadyCb(persisted ?? {});
|
|
751
|
+
}
|
|
752
|
+
catch (cbErr) {
|
|
753
|
+
console.warn("[CyberSoulClient] onStateReady callback threw:", cbErr);
|
|
754
|
+
}
|
|
755
|
+
})
|
|
756
|
+
.catch(() => {
|
|
757
|
+
// PATCH failed; still signal LLM-phase complete with an empty snapshot.
|
|
758
|
+
try {
|
|
759
|
+
stateReadyCb({});
|
|
760
|
+
}
|
|
761
|
+
catch (cbErr) {
|
|
762
|
+
console.warn("[CyberSoulClient] onStateReady callback threw:", cbErr);
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
}
|
|
727
766
|
const resolvedTextResponse = typeof parsedIntent.textResponse === "string" &&
|
|
728
767
|
parsedIntent.textResponse.trim().length > 0
|
|
729
768
|
? parsedIntent.textResponse
|
|
@@ -757,7 +796,8 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
757
796
|
if (!(e instanceof CyberSoulError))
|
|
758
797
|
return;
|
|
759
798
|
if (!(e instanceof CyberSoulInsufficientPointsError) &&
|
|
760
|
-
!(e instanceof CyberSoulWalletError)
|
|
799
|
+
!(e instanceof CyberSoulWalletError) &&
|
|
800
|
+
!(e instanceof CyberSoulSensitiveContentError)) {
|
|
761
801
|
return;
|
|
762
802
|
}
|
|
763
803
|
if (!mediaErrorAffected.includes(modality)) {
|
|
@@ -799,10 +839,23 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
799
839
|
.then((res) => {
|
|
800
840
|
finalImageUrl = res.image_url;
|
|
801
841
|
finalImageMediaId = res.id;
|
|
842
|
+
if (params.onMediaReady && finalImageUrl) {
|
|
843
|
+
try {
|
|
844
|
+
params.onMediaReady({
|
|
845
|
+
modality: "image",
|
|
846
|
+
url: finalImageUrl,
|
|
847
|
+
mediaId: finalImageMediaId,
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
catch (cbErr) {
|
|
851
|
+
console.warn("[CyberSoulClient] onMediaReady(image) threw:", cbErr);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
802
854
|
})
|
|
803
855
|
.catch((e) => {
|
|
804
856
|
if (!(e instanceof CyberSoulInsufficientPointsError) &&
|
|
805
|
-
!(e instanceof CyberSoulWalletError)
|
|
857
|
+
!(e instanceof CyberSoulWalletError) &&
|
|
858
|
+
!(e instanceof CyberSoulSensitiveContentError)) {
|
|
806
859
|
console.error("[CyberSoulClient] Image generation failed:", e);
|
|
807
860
|
}
|
|
808
861
|
captureMediaError("image", e);
|
|
@@ -826,10 +879,24 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
|
|
|
826
879
|
finalAudioUrl = res.audio_url;
|
|
827
880
|
finalAudioMediaId = res.id;
|
|
828
881
|
finalDurationSec = res.duration_sec;
|
|
882
|
+
if (params.onMediaReady && finalAudioUrl) {
|
|
883
|
+
try {
|
|
884
|
+
params.onMediaReady({
|
|
885
|
+
modality: "voice",
|
|
886
|
+
url: finalAudioUrl,
|
|
887
|
+
mediaId: finalAudioMediaId,
|
|
888
|
+
durationSec: finalDurationSec,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
catch (cbErr) {
|
|
892
|
+
console.warn("[CyberSoulClient] onMediaReady(voice) threw:", cbErr);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
829
895
|
})
|
|
830
896
|
.catch((e) => {
|
|
831
897
|
if (!(e instanceof CyberSoulInsufficientPointsError) &&
|
|
832
|
-
!(e instanceof CyberSoulWalletError)
|
|
898
|
+
!(e instanceof CyberSoulWalletError) &&
|
|
899
|
+
!(e instanceof CyberSoulSensitiveContentError)) {
|
|
833
900
|
console.error("[CyberSoulClient] Voice generation failed:", e);
|
|
834
901
|
}
|
|
835
902
|
captureMediaError("voice", e);
|
|
@@ -1095,6 +1162,26 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1095
1162
|
if (parsedIntent.stateUpdate) {
|
|
1096
1163
|
persistedStatePromise = this._updateDynamicContextInternal(parsedIntent.stateUpdate);
|
|
1097
1164
|
}
|
|
1165
|
+
if (params.onStateReady) {
|
|
1166
|
+
const stateReadyCb = params.onStateReady;
|
|
1167
|
+
persistedStatePromise
|
|
1168
|
+
.then((persisted) => {
|
|
1169
|
+
try {
|
|
1170
|
+
stateReadyCb(persisted ?? {});
|
|
1171
|
+
}
|
|
1172
|
+
catch (cbErr) {
|
|
1173
|
+
console.warn("[CyberSoulClient] onStateReady callback threw:", cbErr);
|
|
1174
|
+
}
|
|
1175
|
+
})
|
|
1176
|
+
.catch(() => {
|
|
1177
|
+
try {
|
|
1178
|
+
stateReadyCb({});
|
|
1179
|
+
}
|
|
1180
|
+
catch (cbErr) {
|
|
1181
|
+
console.warn("[CyberSoulClient] onStateReady callback threw:", cbErr);
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1098
1185
|
if (params.onTextReady) {
|
|
1099
1186
|
params.onTextReady(parsedIntent.textResponse, parsedIntent.actionText, {
|
|
1100
1187
|
stateUpdate: parsedIntent.stateUpdate,
|
|
@@ -1109,10 +1196,23 @@ If "shouldSkipProactive" is false, "textResponse" is required and "stateUpdate"
|
|
|
1109
1196
|
const res = await this.generatePrimitive("image", parsedIntent.imageParams);
|
|
1110
1197
|
finalImageUrl = res.image_url;
|
|
1111
1198
|
finalImageMediaId = res.id;
|
|
1199
|
+
if (params.onMediaReady && finalImageUrl) {
|
|
1200
|
+
try {
|
|
1201
|
+
params.onMediaReady({
|
|
1202
|
+
modality: "image",
|
|
1203
|
+
url: finalImageUrl,
|
|
1204
|
+
mediaId: finalImageMediaId,
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
catch (cbErr) {
|
|
1208
|
+
console.warn("[CyberSoulClient] onMediaReady(image) threw:", cbErr);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1112
1211
|
}
|
|
1113
1212
|
catch (e) {
|
|
1114
1213
|
if (e instanceof CyberSoulInsufficientPointsError ||
|
|
1115
|
-
e instanceof CyberSoulWalletError
|
|
1214
|
+
e instanceof CyberSoulWalletError ||
|
|
1215
|
+
e instanceof CyberSoulSensitiveContentError) {
|
|
1116
1216
|
proactiveMediaError = e;
|
|
1117
1217
|
proactiveAffected.push("image");
|
|
1118
1218
|
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -80,3 +80,13 @@ export declare class CyberSoulWalletError extends CyberSoulApiError {
|
|
|
80
80
|
readonly code: string;
|
|
81
81
|
constructor(endpoint: string, method: string, status: number, message: string, body?: unknown, code?: string);
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* The backend rejected an image/voice generation request because the
|
|
85
|
+
* prompt (or the model's output) was flagged as sensitive / unsafe
|
|
86
|
+
* (backend code `E005`). The user can recover by sending a different
|
|
87
|
+
* prompt — there's nothing to retry automatically.
|
|
88
|
+
*/
|
|
89
|
+
export declare class CyberSoulSensitiveContentError extends CyberSoulApiError {
|
|
90
|
+
readonly code: string;
|
|
91
|
+
constructor(endpoint: string, method: string, status: number, message: string, body?: unknown, code?: string);
|
|
92
|
+
}
|
package/dist/errors.js
CHANGED
|
@@ -108,3 +108,16 @@ export class CyberSoulWalletError extends CyberSoulApiError {
|
|
|
108
108
|
this.code = code;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The backend rejected an image/voice generation request because the
|
|
113
|
+
* prompt (or the model's output) was flagged as sensitive / unsafe
|
|
114
|
+
* (backend code `E005`). The user can recover by sending a different
|
|
115
|
+
* prompt — there's nothing to retry automatically.
|
|
116
|
+
*/
|
|
117
|
+
export class CyberSoulSensitiveContentError extends CyberSoulApiError {
|
|
118
|
+
code;
|
|
119
|
+
constructor(endpoint, method, status, message, body, code = "E005") {
|
|
120
|
+
super(endpoint, method, status, message, body, "sensitive-content");
|
|
121
|
+
this.code = code;
|
|
122
|
+
}
|
|
123
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -53,12 +53,37 @@ export interface PersistedDynamicContext {
|
|
|
53
53
|
/** Persisted relationship stage label after re-evaluation. */
|
|
54
54
|
relationshipStage?: string;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Payload delivered by [InteractParams.onMediaReady] when an individual
|
|
58
|
+
* media task (image/voice) finishes. Fires from inside the SDK's
|
|
59
|
+
* per-modality `.then()` so callers can render the bubble the moment
|
|
60
|
+
* that modality is ready, instead of waiting for the slowest one to
|
|
61
|
+
* finish. The aggregated `InteractResponse` is still returned at the
|
|
62
|
+
* end and carries the same URLs (no double-render needed if the caller
|
|
63
|
+
* tracks per-modality state).
|
|
64
|
+
*/
|
|
65
|
+
export interface MediaReadyPayload {
|
|
66
|
+
modality: "image" | "voice";
|
|
67
|
+
url: string;
|
|
68
|
+
mediaId?: string;
|
|
69
|
+
/** Voice only — TTS duration in seconds when known. */
|
|
70
|
+
durationSec?: number;
|
|
71
|
+
}
|
|
56
72
|
export interface ProactiveParams {
|
|
57
73
|
history?: HistoryEntry[];
|
|
58
74
|
maxUnreplied?: number;
|
|
59
75
|
requestTypes?: InteractRequestType[];
|
|
60
76
|
localContext?: string;
|
|
61
77
|
onTextReady?: (textResponse: string, actionText?: string, metadata?: InteractMetadata) => void;
|
|
78
|
+
/**
|
|
79
|
+
* Fires when the server-authoritative PATCH /dynamic-context resolves,
|
|
80
|
+
* before media generation completes. Lets the UI update the live
|
|
81
|
+
* temperature / relationship stage immediately instead of waiting for
|
|
82
|
+
* the (potentially slow) image task.
|
|
83
|
+
*/
|
|
84
|
+
onStateReady?: (persisted: PersistedDynamicContext) => void;
|
|
85
|
+
/** Fires per modality as each media task settles successfully. */
|
|
86
|
+
onMediaReady?: (payload: MediaReadyPayload) => void;
|
|
62
87
|
}
|
|
63
88
|
export interface ProactiveResponse {
|
|
64
89
|
status: "success" | "skipped" | "error";
|
|
@@ -85,6 +110,17 @@ export interface InteractParams {
|
|
|
85
110
|
requestTypes?: InteractRequestType[];
|
|
86
111
|
history?: HistoryEntry[];
|
|
87
112
|
onTextReady?: (textResponse: string, actionText?: string, metadata?: InteractMetadata) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Fires when the server-authoritative PATCH /dynamic-context resolves,
|
|
115
|
+
* before media generation completes. Lets the UI update the live
|
|
116
|
+
* temperature / relationship stage immediately instead of waiting for
|
|
117
|
+
* the (potentially slow) image task. When the turn has no
|
|
118
|
+
* `stateUpdate`, this still fires with an empty object so callers can
|
|
119
|
+
* use it as a generic "LLM phase done" signal.
|
|
120
|
+
*/
|
|
121
|
+
onStateReady?: (persisted: PersistedDynamicContext) => void;
|
|
122
|
+
/** Fires per modality as each media task settles successfully. */
|
|
123
|
+
onMediaReady?: (payload: MediaReadyPayload) => void;
|
|
88
124
|
}
|
|
89
125
|
export interface OndemandEventParams {
|
|
90
126
|
eventDescription: string;
|
|
@@ -155,7 +191,7 @@ export interface InteractResponse {
|
|
|
155
191
|
*/
|
|
156
192
|
export interface InteractMediaError {
|
|
157
193
|
/** Coarse kind so UIs can map to a single user-facing message. */
|
|
158
|
-
kind: "insufficient-points" | "wallet" | "unknown";
|
|
194
|
+
kind: "insufficient-points" | "wallet" | "sensitive-content" | "unknown";
|
|
159
195
|
/** Backend machine code when available (e.g. "INSUFFICIENT_POINTS"). */
|
|
160
196
|
code?: string;
|
|
161
197
|
/** Raw error message, for logs / diagnostics. */
|