@space3-npm/cybersoul-client 1.4.3 → 1.4.4

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 CHANGED
@@ -42,6 +42,22 @@ export declare class CyberSoulClient {
42
42
  * If the payload is already the inner args object (no voiceArgs wrapper), uses it as-is.
43
43
  */
44
44
  private extractVoiceArgsFromLlmResponse;
45
+ /**
46
+ * Strip content the TTS engine can't speak naturally:
47
+ * - Stage-direction wrappers like (smiles), (挑眉), [pauses], 【动作】, *grins*
48
+ * — these slip through despite prompt instructions and the engine will
49
+ * literally read the brackets/asterisks if left in.
50
+ * - Emoji and emoji-component codepoints (Extended_Pictographic plus the
51
+ * ZWJ / variation-selector / skin-tone / regional-indicator scaffolding
52
+ * that builds composite emoji). TTS providers either read these aloud
53
+ * as the literal Unicode name ("face with tears of joy") or produce a
54
+ * glitchy artifact, both of which sound wrong.
55
+ *
56
+ * Collapses runs of whitespace introduced by removals and trims the result.
57
+ * Returns "" if everything gets stripped — callers should fall back to a
58
+ * neutral placeholder (e.g. "...") so the TTS call still has valid input.
59
+ */
60
+ private sanitizeTextForVoice;
45
61
  private formatHistoryEntries;
46
62
  private buildHistoryTranscript;
47
63
  interact(params: InteractParams): Promise<InteractResponse>;
package/dist/client.js CHANGED
@@ -13,7 +13,7 @@ export class CyberSoulClient {
13
13
  this.requestTimeoutMs = config.requestTimeoutMs ?? 120000;
14
14
  this.maxRetries = Math.max(0, config.maxRetries ?? 1);
15
15
  // Setup Provider
16
- this.llm = new GenericLLMProvider(config.llmConfig, config.backendUrl, config.characterKey);
16
+ this.llm = new GenericLLMProvider(config.llmConfig, config.backendUrl, config.characterKey, config.fetchImpl);
17
17
  }
18
18
  /**
19
19
  * Internal wrapper for fetch that automatically injects the backend URL and Character Auth token.
@@ -33,7 +33,8 @@ export class CyberSoulClient {
33
33
  const controller = new AbortController();
34
34
  const timeout = setTimeout(() => controller.abort(), this.requestTimeoutMs);
35
35
  try {
36
- const response = await fetch(url, {
36
+ const fetchFn = this.config.fetchImpl ?? fetch;
37
+ const response = await fetchFn(url, {
37
38
  ...options,
38
39
  headers,
39
40
  signal: controller.signal,
@@ -408,6 +409,32 @@ ${isProactive
408
409
  }
409
410
  return payload;
410
411
  }
412
+ /**
413
+ * Strip content the TTS engine can't speak naturally:
414
+ * - Stage-direction wrappers like (smiles), (挑眉), [pauses], 【动作】, *grins*
415
+ * — these slip through despite prompt instructions and the engine will
416
+ * literally read the brackets/asterisks if left in.
417
+ * - Emoji and emoji-component codepoints (Extended_Pictographic plus the
418
+ * ZWJ / variation-selector / skin-tone / regional-indicator scaffolding
419
+ * that builds composite emoji). TTS providers either read these aloud
420
+ * as the literal Unicode name ("face with tears of joy") or produce a
421
+ * glitchy artifact, both of which sound wrong.
422
+ *
423
+ * Collapses runs of whitespace introduced by removals and trims the result.
424
+ * Returns "" if everything gets stripped — callers should fall back to a
425
+ * neutral placeholder (e.g. "...") so the TTS call still has valid input.
426
+ */
427
+ sanitizeTextForVoice(text) {
428
+ if (typeof text !== "string")
429
+ return "";
430
+ return text
431
+ // (parens), (全角), [brackets], 【全角】, *asterisks*
432
+ .replace(/[\((\[【\*].*?[\))\]】\*]/g, "")
433
+ // emoji + ZWJ + variation selectors + skin-tone modifiers + regional indicators
434
+ .replace(/[\p{Extended_Pictographic}\u200D\uFE0F\uFE0E\u{1F3FB}-\u{1F3FF}\u{1F1E6}-\u{1F1FF}]/gu, "")
435
+ .replace(/\s+/g, " ")
436
+ .trim();
437
+ }
411
438
  formatHistoryEntries(history, userName, agentName, promptDirective = "") {
412
439
  const contextLines = [];
413
440
  for (let i = 0; i < history.length; i++) {
@@ -642,12 +669,8 @@ Note: Always include "isEndTurn". If "imageParams", "voiceArgs", "triggerEvent",
642
669
  const normalizedVoiceArgs = parsedIntent.voiceArgs && typeof parsedIntent.voiceArgs === "object"
643
670
  ? parsedIntent.voiceArgs
644
671
  : {};
645
- let textForVoice = resolvedTextResponse;
646
- // One final bulletproof regex wash to strip (smiles) and *laughs* just in case the LLM disobeys
647
- if (typeof textForVoice === "string") {
648
- textForVoice = textForVoice.replace(/[\((\[【\*].*?[\))\]】\*]/g, '').trim();
649
- }
650
- if (typeof textForVoice !== "string" || textForVoice.trim().length === 0) {
672
+ let textForVoice = this.sanitizeTextForVoice(resolvedTextResponse);
673
+ if (textForVoice.length === 0) {
651
674
  textForVoice = "...";
652
675
  }
653
676
  mediaTasks.push(this.generatePrimitive("voice", {
@@ -994,7 +1017,7 @@ Output strictly valid JSON ONLY. No markdown, no conversational filler. Return e
994
1017
  dynamicArgs = {};
995
1018
  }
996
1019
  const res = await this.generatePrimitive("voice", {
997
- text: params.text,
1020
+ text: this.sanitizeTextForVoice(params.text) || "...",
998
1021
  dynamicArgs,
999
1022
  });
1000
1023
  return {
@@ -3,8 +3,10 @@ export declare class GenericLLMProvider implements BaseLLMProvider {
3
3
  private config;
4
4
  private backendApiUrl;
5
5
  private backendAuthToken?;
6
+ private fetchImpl?;
6
7
  private static templateCache;
7
- constructor(config: GenericLLMConfig, backendApiUrl: string, backendAuthToken?: string | undefined);
8
+ constructor(config: GenericLLMConfig, backendApiUrl: string, backendAuthToken?: string | undefined, fetchImpl?: typeof fetch | undefined);
9
+ private get fetchFn();
8
10
  private fetchTemplate;
9
11
  private extractResponse;
10
12
  generate(messages: {
@@ -2,11 +2,16 @@ export class GenericLLMProvider {
2
2
  config;
3
3
  backendApiUrl;
4
4
  backendAuthToken;
5
+ fetchImpl;
5
6
  static templateCache = new Map();
6
- constructor(config, backendApiUrl, backendAuthToken) {
7
+ constructor(config, backendApiUrl, backendAuthToken, fetchImpl) {
7
8
  this.config = config;
8
9
  this.backendApiUrl = backendApiUrl;
9
10
  this.backendAuthToken = backendAuthToken;
11
+ this.fetchImpl = fetchImpl;
12
+ }
13
+ get fetchFn() {
14
+ return this.fetchImpl ?? fetch;
10
15
  }
11
16
  async fetchTemplate() {
12
17
  const cacheKey = `${this.config.provider}:${this.config.model}`;
@@ -24,7 +29,7 @@ export class GenericLLMProvider {
24
29
  provider: this.config.provider,
25
30
  model: this.config.model
26
31
  });
27
- const resp = await fetch(`${this.backendApiUrl}/api/v1/cyber-soul/llm-models/template?${qs.toString()}`, {
32
+ const resp = await this.fetchFn(`${this.backendApiUrl}/api/v1/cyber-soul/llm-models/template?${qs.toString()}`, {
28
33
  headers
29
34
  });
30
35
  if (!resp.ok) {
@@ -66,7 +71,7 @@ export class GenericLLMProvider {
66
71
  if (!payload.messages || (Array.isArray(payload.messages) && payload.messages.length === 0)) {
67
72
  payload.messages = messages;
68
73
  }
69
- const response = await fetch(template.apiUrl, {
74
+ const response = await this.fetchFn(template.apiUrl, {
70
75
  method: 'POST',
71
76
  headers,
72
77
  body: JSON.stringify(payload)
package/dist/types.d.ts CHANGED
@@ -10,6 +10,15 @@ export interface CyberSoulClientConfig {
10
10
  llmConfig: GenericLLMConfig;
11
11
  requestTimeoutMs?: number;
12
12
  maxRetries?: number;
13
+ /**
14
+ * Optional fetch override. When provided, the client uses this in
15
+ * place of the global `fetch` for every HTTP call (backend + LLM
16
+ * provider). Intended for environments where the global fetch is
17
+ * suspended by the host platform — e.g. React Native on Samsung
18
+ * BBA / Doze — and a native HTTP path must be used instead. Must
19
+ * conform to the standard `fetch` signature.
20
+ */
21
+ fetchImpl?: typeof fetch;
13
22
  }
14
23
  export declare enum InteractRequestType {
15
24
  AUTO = "auto",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@space3-npm/cybersoul-client",
3
- "version": "1.4.3",
3
+ "version": "1.4.4",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",