funda-ui 4.7.599 → 4.7.604

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.
@@ -46,6 +46,7 @@ return /******/ (() => { // webpackBootstrap
46
46
  var __webpack_exports__ = {};
47
47
  __webpack_require__.r(__webpack_exports__);
48
48
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
49
+ /* harmony export */ "fixAndParseJSON": () => (/* binding */ fixAndParseJSON),
49
50
  /* harmony export */ "isEmail": () => (/* binding */ isEmail),
50
51
  /* harmony export */ "isEmpty": () => (/* binding */ isEmpty),
51
52
  /* harmony export */ "isInt": () => (/* binding */ isInt),
@@ -57,37 +58,353 @@ __webpack_require__.r(__webpack_exports__);
57
58
  /* harmony export */ });
58
59
  function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
59
60
  /**
60
- * Check if a string is a valid number
61
- * @param str - The string to check
62
- * @returns boolean indicating if the string is a valid number
61
+ * Fix And Parse JSON (Support for handling complex escape JSON strings)
62
+ * @private
63
63
  */
64
- function isValidNumeric(str) {
65
- if (typeof str !== "string") return false; // we only process strings!
66
- if (!isNaN(Number(str)) &&
67
- // use type coercion to parse the _entirety_ of the string
68
- !isNaN(parseFloat(str)) // ensure strings of whitespace fail
69
- ) {
70
- return true;
64
+ /*
65
+ - Always try JSON.parse first;
66
+ - If parsing fails, unescape \" → ";
67
+ - Then process the outermost object or array key-by-key, value-by-value;
68
+ - If a top-level value is an unquoted object or array (e.g. messages: [ {...} ]),
69
+ recursively treat that value as a new root to repair;
70
+ - For values wrapped in quotes ('...' or "..."), extract the inner text and
71
+ re-encode it using JSON.stringify (ensures internal single/double quotes
72
+ are not corrupted);
73
+ - Set MAX_DEPTH to prevent infinite recursion.
74
+ */
75
+ // fixAndParseJSON - recursively repairs top-level key/value
76
+ // (when encountering outermost values that are objects/arrays, it recurses)
77
+
78
+ /*
79
+ DEMO:
80
+
81
+ // ✅ Valid JSON (contains svg and single-quote content)
82
+ const okJson = `{
83
+ "label":"<svg width='16' height='16'><path fill='currentColor' d='M19 13h-6'/></svg> New Session",
84
+ "value":"new",
85
+ "onClick":"method.setVal(''); method.clearData();"
86
+ }`;
87
+
88
+ // ❌ Single-quote JSON
89
+ const badJson = "{'model':'{model}','messages':[{'role':'user','content':'{message}'}],'stream': true}";
90
+
91
+ // ❌ Escaped JSON
92
+ const badJson2 = "{\\\"label\\\":\\\"<svg width='16' height='16' viewBox='0 0 24 24'><path fill='currentColor' d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/></svg> New Session\\\",\\\"value\\\":\\\"new\\\",\\\"onClick\\\":\\\"method.setVal(''); method.clearData();\\\"}";
93
+
94
+ const badJson3 = "{\"label\":\"<svg width='16' height='16' viewBox='0 0 24 24'><path fill='currentColor' d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/></svg> New Session\",\"value\":\"new\",\"onClick\":\"method.setVal(''); method.clearData();\"}";
95
+
96
+ const badJson4 = "[{\"label\":\"<svg fill='currentColor' width='12' height='12' viewBox='0 0 24 24'><path d='M20.5 9a3.49 3.49 0 0 0-3.45 3h-1.1a2.49 2.49 0 0 0-4.396-1.052L8.878 9.731l3.143-4.225a2.458 2.458 0 0 0 2.98-.019L17.339 8H16v1h3V6h-1v1.243l-2.336-2.512A2.473 2.473 0 0 0 16 3.5a2.5 2.5 0 0 0-5 0 2.474 2.474 0 0 0 .343 1.243L7.947 9.308 4.955 7.947a2.404 2.404 0 0 0-.161-1.438l3.704-1.385-.44 1.371.942.333L10 4 7.172 3l-.334.943 1.01.357-3.659 1.368a2.498 2.498 0 1 0-.682 4.117l2.085 2.688-2.053 2.76a2.5 2.5 0 1 0 .87 3.864l3.484 1.587-1.055.373.334.943L10 21l-1-2.828-.943.333.435 1.354-3.608-1.645A2.471 2.471 0 0 0 5 17.5a2.5 2.5 0 0 0-.058-.527l3.053-1.405 3.476 4.48a2.498 2.498 0 1 0 4.113.075L18 17.707V19h1v-3h-3v1h1.293l-2.416 2.416a2.466 2.466 0 0 0-2.667-.047l-3.283-4.23 2.554-1.176A2.494 2.494 0 0 0 15.95 13h1.1a3.493 3.493 0 1 0 3.45-4zm-7-7A1.5 1.5 0 1 1 12 3.5 1.502 1.502 0 0 1 13.5 2zm0 18a1.5 1.5 0 1 1-1.5 1.5 1.502 1.502 0 0 1 1.5-1.5zM1 7.5a1.5 1.5 0 1 1 2.457 1.145l-.144.112A1.496 1.496 0 0 1 1 7.5zm3.32 1.703a2.507 2.507 0 0 0 .264-.326l2.752 1.251-1.124 1.512zM2.5 19A1.5 1.5 0 1 1 4 17.5 1.502 1.502 0 0 1 2.5 19zm2.037-2.941a2.518 2.518 0 0 0-.193-.234l1.885-2.532 1.136 1.464zm3.76-1.731L6.849 12.46l1.42-1.908L11.1 11.84a2.29 2.29 0 0 0-.033 1.213zM13.5 14a1.5 1.5 0 1 1 1.5-1.5 1.502 1.502 0 0 1-1.5 1.5zm7 1a2.5 2.5 0 1 1 2.5-2.5 2.502 2.502 0 0 1-2.5 2.5zm1.5-2.5a1.5 1.5 0 1 1-1.5-1.5 1.502 1.502 0 0 1 1.5 1.5z'/><path fill='none' d='M0 0h24v24H0z'/></svg> Deep Thought","value":"brief","onClick":"if(isActive){method.executeCustomMethod('changeModel', true)}else{method.executeCustomMethod('changeModel', false)}"},{"label":"<svg fill='currentColor' width='12' height='12' viewBox='0 0 24 24'><path d='M19 2H5c-1.103 0-2 .897-2 2v12c0 1.103.897 2 2 2h3.586L12 21.414 15.414 18H19c1.103 0 2-.897 2-2V4c0-1.103-.897-2-2-2zm0 14h-4.414L12 18.586 9.414 16H5V4h14v12z'/></svg> Concise Answer","value":"brief","onClick":"if(isActive){method.setContextData({systemPrompt:'Please answer concisely, around 150 words, keep reasoning brief',mergedText:method.getContextData().mergedText,analyzeMetrics:method.getContextData().analyzeMetrics});}else{method.setContextData({mergedText:method.getContextData().mergedText,analyzeMetrics:method.getContextData().analyzeMetrics});}"},{"label":"<svg fill='none' width='12' height='12' viewBox='0 0 16 16'><path d='M7 0.0618896V9H15.9381C15.446 12.9463 12.0796 16 8 16C3.58172 16 0 12.4183 0 8C0 3.92038 3.05369 0.553988 7 0.0618896Z' fill='currentColor'/><path d='M9 0.0618897V7H15.9381C15.4869 3.38128 12.6187 0.513137 9 0.0618897Z' fill='currentColor'/></svg> Metrics Analysis","value":"lab","onClick":"return method.executeCustomMethod('getLibList')","isSelect":true,"dynamicOptions":true}]";
97
+
98
+
99
+ console.log('okJson =>', fixAndParseJSON(okJson)); // parses correctly
100
+ console.log('badJson =>', fixAndParseJSON(badJson)); // repaired and parsed
101
+ console.log('badJson2 =>', fixAndParseJSON(badJson2)); // repaired and parsed
102
+ console.log('badJson3 =>', fixAndParseJSON(badJson3)); // repaired and parsed
103
+ console.log('badJson4 =>', fixAndParseJSON(badJson4)); // repaired and parsed
104
+ */
105
+ function fixAndParseJSON(input) {
106
+ var MAX_DEPTH = 6;
107
+
108
+ // 1. Fast attempt
109
+ try {
110
+ return {
111
+ success: true,
112
+ data: JSON.parse(input)
113
+ };
114
+ } catch (e) {
115
+ // continue to repair
71
116
  }
72
- return false;
117
+
118
+ // 2. Simple unescape of \" (common when copied from JS literals)
119
+ var s = input;
120
+ if (s.includes('\\"')) s = s.replace(/\\"/g, '"');
121
+ s = s.trim();
122
+ try {
123
+ if (s.startsWith('{')) {
124
+ s = processTopObject(s, 0, MAX_DEPTH);
125
+ } else if (s.startsWith('[')) {
126
+ s = processTopArray(s, 0, MAX_DEPTH);
127
+ } else {
128
+ throw new Error('Input is not an object or array');
129
+ }
130
+ return {
131
+ success: true,
132
+ data: JSON.parse(s)
133
+ };
134
+ } catch (err) {
135
+ return {
136
+ success: false,
137
+ error: 'Invalid JSON format',
138
+ details: err instanceof Error ? err.message : String(err)
139
+ };
140
+ }
141
+ }
142
+
143
+ /* ---------- Helper (recursive) functions ---------- */
144
+
145
+ function processTopObject(str, depth, MAX_DEPTH) {
146
+ if (depth > MAX_DEPTH) return str;
147
+ str = str.trim();
148
+ // Ensure it is wrapped in { ... }
149
+ if (!(str.startsWith('{') && str.endsWith('}'))) {
150
+ var f = str.indexOf('{');
151
+ var l = str.lastIndexOf('}');
152
+ if (f === -1 || l === -1 || l <= f) return str;
153
+ str = str.slice(f, l + 1);
154
+ }
155
+ var inner = str.slice(1, -1);
156
+ var pairs = splitTopLevel(inner);
157
+ var repairedPairs = pairs.map(function (pair) {
158
+ if (!pair || pair.trim() === '') return '';
159
+ var idx = findTopLevelColon(pair);
160
+ if (idx === -1) {
161
+ return pair; // Non key:value fragment, keep as is (rare case)
162
+ }
163
+
164
+ var rawKey = pair.slice(0, idx).trim();
165
+ var rawVal = pair.slice(idx + 1);
166
+ var keyContent = extractKeyContent(rawKey);
167
+ var keyJson = JSON.stringify(keyContent);
168
+ var repairedValue = repairPossiblyQuotedValue(rawVal, depth + 1, MAX_DEPTH);
169
+ return keyJson + ':' + repairedValue;
170
+ });
171
+ return '{' + repairedPairs.join(',') + '}';
172
+ }
173
+ function processTopArray(str, depth, MAX_DEPTH) {
174
+ if (depth > MAX_DEPTH) return str;
175
+ str = str.trim();
176
+ if (!(str.startsWith('[') && str.endsWith(']'))) {
177
+ var f = str.indexOf('[');
178
+ var l = str.lastIndexOf(']');
179
+ if (f === -1 || l === -1 || l <= f) return str;
180
+ str = str.slice(f, l + 1);
181
+ }
182
+ var inner = str.slice(1, -1);
183
+ var elements = splitTopLevel(inner);
184
+ var processed = elements.map(function (el) {
185
+ var t = el.trim();
186
+ if (t === '') return '';
187
+ if (t.startsWith('{')) return processTopObject(t, depth + 1, MAX_DEPTH);
188
+ if (t.startsWith('[')) return processTopArray(t, depth + 1, MAX_DEPTH);
189
+ return repairPossiblyQuotedValue(t, depth + 1, MAX_DEPTH);
190
+ });
191
+ return '[' + processed.join(',') + ']';
192
+ }
193
+
194
+ // If value is quoted, extract inside and JSON.stringify again (safe escaping)
195
+ // If value is unquoted object/array literal, recurse treating it as new root
196
+ // Otherwise return as is (numbers, booleans, null, or raw expressions)
197
+ function repairPossiblyQuotedValue(rawVal, depth, MAX_DEPTH) {
198
+ var v = rawVal.trim();
199
+ if (v === '') return v;
200
+ if (v[0] === '"' || v[0] === "'") {
201
+ var quote = v[0];
202
+ // Find the last unescaped matching quote
203
+ var lastPos = -1;
204
+ for (var i = v.length - 1; i >= 0; i--) {
205
+ if (v[i] === quote) {
206
+ // check if escaped
207
+ var bs = 0;
208
+ var k = i - 1;
209
+ while (k >= 0 && v[k] === '\\') {
210
+ bs++;
211
+ k--;
212
+ }
213
+ if (bs % 2 === 0) {
214
+ lastPos = i;
215
+ break;
216
+ }
217
+ }
218
+ }
219
+ var inner = lastPos > 0 ? v.slice(1, lastPos) : v.slice(1);
220
+ return JSON.stringify(inner); // Generate valid JSON string (auto escape)
221
+ }
222
+
223
+ // If unquoted object/array literal -> recurse
224
+ if (v.startsWith('{')) {
225
+ return processTopObject(v, depth, MAX_DEPTH);
226
+ }
227
+ if (v.startsWith('[')) {
228
+ return processTopArray(v, depth, MAX_DEPTH);
229
+ }
230
+
231
+ // Other (number, boolean, null, raw expression): return as is
232
+ return v;
233
+ }
234
+
235
+ /* --------- Utils: split by top-level commas, find colon, extract key --------- */
236
+
237
+ // Split string by top-level commas (ignores commas inside strings/objects/arrays/parentheses)
238
+ function splitTopLevel(str) {
239
+ var parts = [];
240
+ var buf = '';
241
+ var depthCurly = 0;
242
+ var depthSquare = 0;
243
+ var depthParen = 0;
244
+ var inSingle = false;
245
+ var inDouble = false;
246
+ var esc = false;
247
+ for (var i = 0; i < str.length; i++) {
248
+ var ch = str[i];
249
+ if (esc) {
250
+ buf += ch;
251
+ esc = false;
252
+ continue;
253
+ }
254
+ if (ch === '\\') {
255
+ buf += ch;
256
+ esc = true;
257
+ continue;
258
+ }
259
+ if (ch === "'" && !inDouble) {
260
+ inSingle = !inSingle;
261
+ buf += ch;
262
+ continue;
263
+ }
264
+ if (ch === '"' && !inSingle) {
265
+ inDouble = !inDouble;
266
+ buf += ch;
267
+ continue;
268
+ }
269
+ if (!inSingle && !inDouble) {
270
+ if (ch === '{') {
271
+ depthCurly++;
272
+ buf += ch;
273
+ continue;
274
+ }
275
+ if (ch === '}') {
276
+ depthCurly--;
277
+ buf += ch;
278
+ continue;
279
+ }
280
+ if (ch === '[') {
281
+ depthSquare++;
282
+ buf += ch;
283
+ continue;
284
+ }
285
+ if (ch === ']') {
286
+ depthSquare--;
287
+ buf += ch;
288
+ continue;
289
+ }
290
+ if (ch === '(') {
291
+ depthParen++;
292
+ buf += ch;
293
+ continue;
294
+ }
295
+ if (ch === ')') {
296
+ depthParen--;
297
+ buf += ch;
298
+ continue;
299
+ }
300
+ if (ch === ',' && depthCurly === 0 && depthSquare === 0 && depthParen === 0) {
301
+ parts.push(buf);
302
+ buf = '';
303
+ continue;
304
+ }
305
+ }
306
+ buf += ch;
307
+ }
308
+ if (buf.trim() !== '') parts.push(buf);
309
+ return parts;
310
+ }
311
+
312
+ // Find the first top-level colon (ignores strings and nested structures)
313
+ function findTopLevelColon(str) {
314
+ var inSingle = false;
315
+ var inDouble = false;
316
+ var esc = false;
317
+ var depthCurly = 0;
318
+ var depthSquare = 0;
319
+ var depthParen = 0;
320
+ for (var i = 0; i < str.length; i++) {
321
+ var ch = str[i];
322
+ if (esc) {
323
+ esc = false;
324
+ continue;
325
+ }
326
+ if (ch === '\\') {
327
+ esc = true;
328
+ continue;
329
+ }
330
+ if (ch === "'" && !inDouble) {
331
+ inSingle = !inSingle;
332
+ continue;
333
+ }
334
+ if (ch === '"' && !inSingle) {
335
+ inDouble = !inDouble;
336
+ continue;
337
+ }
338
+ if (!inSingle && !inDouble) {
339
+ if (ch === '{') {
340
+ depthCurly++;
341
+ continue;
342
+ }
343
+ if (ch === '}') {
344
+ depthCurly--;
345
+ continue;
346
+ }
347
+ if (ch === '[') {
348
+ depthSquare++;
349
+ continue;
350
+ }
351
+ if (ch === ']') {
352
+ depthSquare--;
353
+ continue;
354
+ }
355
+ if (ch === '(') {
356
+ depthParen++;
357
+ continue;
358
+ }
359
+ if (ch === ')') {
360
+ depthParen--;
361
+ continue;
362
+ }
363
+ if (ch === ':' && depthCurly === 0 && depthSquare === 0 && depthParen === 0) {
364
+ return i;
365
+ }
366
+ }
367
+ }
368
+ return -1;
369
+ }
370
+
371
+ // Extract key content (supports "key", 'key', key) → returns pure string key
372
+ function extractKeyContent(rawKey) {
373
+ var r = rawKey.trim();
374
+ if (r.startsWith('"') && r.endsWith('"') || r.startsWith("'") && r.endsWith("'")) {
375
+ var inner = r.slice(1, -1).replace(/\\"/g, '"').replace(/\\'/g, "'");
376
+ return inner;
377
+ }
378
+ return r;
73
379
  }
74
380
 
75
381
  /**
76
382
  * Determine whether it is in JSON format
77
- * @param str - The value to check
78
- * @returns boolean indicating if the value is valid JSON
383
+ * @private
79
384
  */
80
- function isJSON(str) {
81
- if (typeof str === 'string' && str.length > 0) {
82
- if (str.replace(/\"\"/g, '').replace(/\,/g, '') === '[{}]') {
83
- return false;
84
- }
85
- if (/^[\],:{}\s]*$/.test(str.replace(/\\["\\\/bfnrtu]/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
385
+ function isJSON(input) {
386
+ if (typeof input === 'string' && input.length > 0) {
387
+ return fixAndParseJSON(input).success;
388
+ } else {
389
+ if (_typeof(input) === 'object' && Object.prototype.toString.call(input) === '[object Object]' && !input.length) {
86
390
  return true;
391
+ } else {
392
+ return false;
87
393
  }
88
- return false;
89
394
  }
90
- if (_typeof(str) === 'object' && Object.prototype.toString.call(str) === '[object Object]' && !str.length) {
395
+ }
396
+
397
+ /**
398
+ * Check if a string is a valid number
399
+ * @param str - The string to check
400
+ * @returns boolean indicating if the string is a valid number
401
+ */
402
+ function isValidNumeric(str) {
403
+ if (typeof str !== "string") return false; // we only process strings!
404
+ if (!isNaN(Number(str)) &&
405
+ // use type coercion to parse the _entirety_ of the string
406
+ !isNaN(parseFloat(str)) // ensure strings of whitespace fail
407
+ ) {
91
408
  return true;
92
409
  }
93
410
  return false;
@@ -9,7 +9,9 @@ import useComId from 'funda-utils/dist/cjs/useComId';
9
9
  import useDebounce from 'funda-utils/dist/cjs/useDebounce';
10
10
  import useThrottle from 'funda-utils/dist/cjs/useThrottle';
11
11
  import useClickOutside from 'funda-utils/dist/cjs/useClickOutside';
12
+ import useStreamController from 'funda-utils/dist/cjs/useStreamController';
12
13
  import { htmlEncode } from 'funda-utils/dist/cjs/sanitize';
14
+ import { isJSON } from 'funda-utils/dist/cjs/validate';
13
15
 
14
16
 
15
17
  // loader
@@ -17,14 +19,13 @@ import PureLoader from './PureLoader';
17
19
  import TypingEffect from "./TypingEffect";
18
20
 
19
21
  import {
20
- isValidJSON,
21
22
  formatLatestDisplayContent,
22
23
  formatName,
23
24
  fixHtmlTags,
24
- isStreamResponse
25
+ isStreamResponse,
26
+ toBoolean
25
27
  } from './utils/func';
26
28
 
27
- import useStreamController from './useStreamController';
28
29
 
29
30
  export interface CustomMethod {
30
31
  name: string;
@@ -101,6 +102,7 @@ export type ChatboxProps = {
101
102
  model?: string;
102
103
  baseUrl?: string;
103
104
  apiKey?: string;
105
+ token?: string | (() => string);
104
106
  defaultMessages?: MessageDetail[];
105
107
  verbose?: boolean;
106
108
  reasoningSwitchLabel?: string;
@@ -168,6 +170,19 @@ const Chatbox = (props: ChatboxProps) => {
168
170
  });
169
171
 
170
172
 
173
+ const tokenFormat = (inputTokenValue: any) => {
174
+ if (typeof inputTokenValue === 'undefined') return '';
175
+
176
+ // Resolve token value (string or function)
177
+ if (typeof inputTokenValue === 'function') {
178
+ return inputTokenValue();
179
+ } else {
180
+ return inputTokenValue || '';
181
+ }
182
+ };
183
+
184
+
185
+
171
186
  //
172
187
  const rootRef = useRef<HTMLDivElement>(null);
173
188
  const msgContainerRef = useRef<HTMLDivElement>(null);
@@ -300,6 +315,7 @@ const Chatbox = (props: ChatboxProps) => {
300
315
  model,
301
316
  baseUrl,
302
317
  apiKey,
318
+ token,
303
319
  verbose,
304
320
  reasoningSwitchLabel,
305
321
  stopLabel,
@@ -350,20 +366,23 @@ const Chatbox = (props: ChatboxProps) => {
350
366
  // request API
351
367
  const requestApiUrl = apiUrl.replace(/\{baseUrl\}/g, baseUrl);
352
368
 
353
-
354
369
  // header config
355
370
  const _headerConfig = headerConfig.replace(/\{apiKey\}/g, apiKey)
371
+ .replace(/\{token\}/g, tokenFormat(token))
356
372
  .replace(/\'/g, '"'); // !!! REQUIRED !!!
357
- const headerConfigRes = typeof _headerConfig !== 'undefined' ? (isValidJSON(_headerConfig) ? JSON.parse(_headerConfig) : undefined) : {'Content-Type':'application/json'};
373
+ const headerConfigRes = typeof _headerConfig !== 'undefined' ? (isJSON(_headerConfig) ? JSON.parse(_headerConfig) : undefined) : {'Content-Type':'application/json'};
358
374
 
359
375
 
360
376
  // Determine whether it is in JSON format
361
- if (!isValidJSON(_requestBodyTmpl)) {
377
+ if (!isJSON(_requestBodyTmpl)) {
362
378
  console.log('--> [ERROR] Wrong JSON format');
363
379
  _requestBodyTmpl = '{}';
364
380
  return {};
365
381
  } else {
366
- _isStream = JSON.parse(_requestBodyTmpl).hasOwnProperty('stream') && JSON.parse(_requestBodyTmpl).stream === true;
382
+
383
+ if (JSON.parse(_requestBodyTmpl).hasOwnProperty('stream')) {
384
+ _isStream = toBoolean(JSON.parse(_requestBodyTmpl).stream) === true;
385
+ }
367
386
  }
368
387
 
369
388
  // Whether or not to show reasoning
@@ -384,6 +403,7 @@ const Chatbox = (props: ChatboxProps) => {
384
403
  model,
385
404
  baseUrl,
386
405
  apiKey,
406
+ token,
387
407
  verbose,
388
408
  reasoningSwitchLabel,
389
409
  stopLabel,
@@ -709,7 +729,8 @@ const Chatbox = (props: ChatboxProps) => {
709
729
 
710
730
  // Streaming data is JSON split by rows
711
731
  const lines = chunk.split("\n").filter(line => line.trim() !== "");
712
-
732
+
733
+
713
734
  for (const line of lines) {
714
735
 
715
736
  // debug
@@ -726,7 +747,7 @@ const Chatbox = (props: ChatboxProps) => {
726
747
  const _content = `${line.replace(/^data:\s*/, '')}`;
727
748
 
728
749
  // Determine whether it is in JSON format
729
- if (!isValidJSON(_content)) {
750
+ if (!isJSON(_content)) {
730
751
  console.log('--> [ERROR] Wrong JSON format');
731
752
 
732
753
  //reset SSE
@@ -1048,14 +1069,15 @@ const Chatbox = (props: ChatboxProps) => {
1048
1069
  // Update stream mode
1049
1070
  setEnableStreamMode(currentStreamMode as boolean);
1050
1071
 
1051
-
1052
1072
  try {
1073
+
1053
1074
  // Parse and interpolate request body template
1054
1075
  let requestBodyRes = JSON.parse(
1055
1076
  (args().requestBodyTmpl || '{}')
1056
1077
  .replace(/\{model\}/g, args().model)
1057
1078
  .replace(/\{message\}/g, msg)
1058
- .replace(/\{token\}/g, chatId)
1079
+ .replace(/\{chatId\}/g, chatId)
1080
+ .replace(/\{token\}/g, tokenFormat(args().token) as string)
1059
1081
  );
1060
1082
 
1061
1083
  //
@@ -1115,6 +1137,7 @@ const Chatbox = (props: ChatboxProps) => {
1115
1137
  // Start streaming
1116
1138
  await streamController.start(contentRes as never);
1117
1139
 
1140
+
1118
1141
  return {
1119
1142
  reply: tempAnimText, // The final content will be in tempAnimText
1120
1143
  useStreamRender: true
@@ -1131,12 +1154,10 @@ const Chatbox = (props: ChatboxProps) => {
1131
1154
 
1132
1155
  }
1133
1156
 
1134
-
1135
1157
  if (currentStreamMode) {
1136
1158
  {/* ======================================================== */}
1137
1159
  {/* ======================== STREAM ====================== */}
1138
1160
  {/* ======================================================== */}
1139
-
1140
1161
  const response: any = await fetch((args().requestApiUrl || ''), {
1141
1162
  method: "POST",
1142
1163
  body: JSON.stringify(requestBodyRes),
@@ -7,15 +7,6 @@ export interface HtmlTagPlaceholder {
7
7
  type: 'table' | 'img' | 'svg';
8
8
  }
9
9
 
10
- export function isValidJSON(str: string){
11
- try {
12
- JSON.parse(str);
13
- return true;
14
- } catch (error) {
15
- return false;
16
- }
17
- }
18
-
19
10
  export function formatLatestDisplayContent(str: string) {
20
11
  // Regular expression to match <details> tags and their content
21
12
  let output = str.replace(/<details class="think"[^>]*>([\s\S]*?)<\/details>/g, (match, content) => {
@@ -177,4 +168,13 @@ export function extractHtmlTags(html: string): { processedHtml: string; placehol
177
168
  });
178
169
 
179
170
  return { processedHtml, placeholders };
180
- };
171
+ };
172
+
173
+
174
+ export function toBoolean(val) {
175
+ if (typeof val === "boolean") return val;
176
+ if (typeof val === "string") {
177
+ return val.toLowerCase() === "true";
178
+ }
179
+ return Boolean(val);
180
+ }