@agentscope-ai/chat 1.1.61 → 1.1.62

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.
Files changed (27) hide show
  1. package/components/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.tsx +4 -0
  2. package/components/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatRequest.tsx +19 -1
  3. package/components/Markdown/core/components/CodeBlock.tsx +16 -6
  4. package/components/Stream/index.ts +39 -12
  5. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.js +10 -5
  6. package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatRequest.js +60 -44
  7. package/lib/Markdown/core/components/CodeBlock.js +44 -12
  8. package/lib/Stream/index.d.ts +5 -0
  9. package/lib/Stream/index.js +38 -18
  10. package/package.json +1 -1
  11. package/bin/starter_webui/README.md +0 -75
  12. package/bin/starter_webui/eslint.config.js +0 -28
  13. package/bin/starter_webui/index.html +0 -12
  14. package/bin/starter_webui/package.json +0 -34
  15. package/bin/starter_webui/src/App.tsx +0 -20
  16. package/bin/starter_webui/src/components/Chat/OptionsPanel/FormItem.tsx +0 -37
  17. package/bin/starter_webui/src/components/Chat/OptionsPanel/OptionsEditor.tsx +0 -160
  18. package/bin/starter_webui/src/components/Chat/OptionsPanel/defaultConfig.ts +0 -41
  19. package/bin/starter_webui/src/components/Chat/OptionsPanel/index.tsx +0 -27
  20. package/bin/starter_webui/src/components/Chat/index.tsx +0 -45
  21. package/bin/starter_webui/src/components/Chat/sessionApi/index.ts +0 -53
  22. package/bin/starter_webui/src/main.tsx +0 -9
  23. package/bin/starter_webui/src/vite-env.d.ts +0 -4
  24. package/bin/starter_webui/tsconfig.app.json +0 -24
  25. package/bin/starter_webui/tsconfig.json +0 -7
  26. package/bin/starter_webui/tsconfig.node.json +0 -22
  27. package/bin/starter_webui/vite.config.ts +0 -11
@@ -99,9 +99,13 @@ export default function useChatController() {
99
99
 
100
100
  /**
101
101
  * 处理取消
102
+ * 1. 标记 interrupted 并重置 UI(finishResponse)
103
+ * 2. abort SSE 连接 —— Stream 内部的 Promise.race 会立即 reject AbortError,
104
+ * processSSEResponse 的 catch 会检测 interrupted 状态并调用 cancel API
102
105
  */
103
106
  const handleCancel = useCallback(() => {
104
107
  finishResponse('interrupted');
108
+ currentQARef.current.abortController?.abort();
105
109
  }, [finishResponse]);
106
110
 
107
111
  /**
@@ -92,9 +92,12 @@ export default function useChatRequest(options: UseChatRequestOptions) {
92
92
  return;
93
93
  }
94
94
 
95
+ const abortSignal = currentQARef.current.abortController?.signal;
96
+
95
97
  try {
96
98
  for await (const chunk of Stream({
97
99
  readableStream: response.body,
100
+ signal: abortSignal,
98
101
  })) {
99
102
  if (currentQARef.current.response?.msgStatus === 'interrupted') {
100
103
  currentQARef.current.abortController?.abort();
@@ -137,7 +140,22 @@ export default function useChatRequest(options: UseChatRequestOptions) {
137
140
  }
138
141
  }
139
142
  } catch (error) {
140
- console.error(error);
143
+ if (currentQARef.current.response?.msgStatus === 'interrupted') {
144
+ if (currentApiOptions.cancel) {
145
+ currentApiOptions.cancel({
146
+ session_id: getCurrentSessionId(),
147
+ });
148
+ }
149
+ currentQARef.current.response.cards = [
150
+ {
151
+ code: 'AgentScopeRuntimeResponseCard',
152
+ data: agentScopeRuntimeResponseBuilder.cancel(),
153
+ }
154
+ ];
155
+ updateMessage(currentQARef.current.response);
156
+ } else {
157
+ console.error(error);
158
+ }
141
159
  }
142
160
  }, [getCurrentSessionId, currentQARef, updateMessage, onFinish]);
143
161
 
@@ -41,14 +41,24 @@ function CodeHeader({ lang, content }: { lang: string, content: string }) {
41
41
  URL.revokeObjectURL(url);
42
42
  }, [lang, content]);
43
43
 
44
- const handleCopy = useCallback(() => {
45
- copy(content).then(() => {
46
- clearTimeout(timer.current);
44
+ const handleCopy = useCallback(async () => {
45
+ try {
46
+ if (window.isSecureContext && navigator.clipboard) {
47
+ await navigator.clipboard.writeText(content);
48
+ } else {
49
+ const textarea = document.createElement('textarea');
50
+ textarea.value = content;
51
+ textarea.style.cssText = 'position:fixed;left:-9999px';
52
+ document.body.appendChild(textarea);
53
+ textarea.select();
54
+ document.execCommand('copy');
55
+ document.body.removeChild(textarea);
56
+ }
47
57
  setCopied(true);
48
- timer.current = setTimeout(() => setCopied(false), 2000);
49
- }).catch(() => {
58
+ setTimeout(() => setCopied(false), 1000);
59
+ } catch {
50
60
  console.warn('Copy failed');
51
- });
61
+ }
52
62
  }, [content]);
53
63
 
54
64
  return <div className={prefixCls}>
@@ -137,6 +137,12 @@ export interface StreamOptions<Output> {
137
137
  * @link https://developer.mozilla.org/en-US/docs/Web/API/TransformStream
138
138
  */
139
139
  transformStream?: TransformStream<string, Output>;
140
+
141
+ /**
142
+ * @description 用于中断流读取的 AbortSignal
143
+ * @descriptionEn AbortSignal to cancel stream reading
144
+ */
145
+ signal?: AbortSignal;
140
146
  }
141
147
 
142
148
  type XReadableStream<R = SSEOutput> = ReadableStream<R> & AsyncGenerator<R>;
@@ -151,7 +157,7 @@ function Stream<Output = SSEOutput>(
151
157
  openaiCompatible?: boolean;
152
158
  },
153
159
  ) {
154
- const { readableStream, transformStream } = options;
160
+ const { readableStream, transformStream, signal } = options;
155
161
 
156
162
  if (!(readableStream instanceof ReadableStream)) {
157
163
  throw new Error(
@@ -181,19 +187,40 @@ function Stream<Output = SSEOutput>(
181
187
  stream[Symbol.asyncIterator] = async function* () {
182
188
  const reader = this.getReader();
183
189
 
184
- while (true) {
185
- const { done, value } = await reader.read();
186
- if (done) break;
190
+ try {
191
+ while (true) {
192
+ let readPromise: Promise<ReadableStreamReadResult<Output>> = reader.read();
193
+
194
+ if (signal) {
195
+ readPromise = Promise.race([
196
+ readPromise,
197
+ new Promise<never>((_, reject) => {
198
+ if (signal.aborted) {
199
+ reject(new DOMException('The operation was aborted.', 'AbortError'));
200
+ return;
201
+ }
202
+ signal.addEventListener('abort', () => {
203
+ reject(new DOMException('The operation was aborted.', 'AbortError'));
204
+ }, { once: true });
205
+ }),
206
+ ]);
207
+ }
208
+
209
+ const { done, value } = await readPromise;
210
+ if (done) break;
187
211
 
188
- if (!value) continue;
212
+ if (!value) continue;
189
213
 
190
- // Transformed data through all transform pipes
191
- yield config?.openaiCompatible
192
- ? {
193
- ...value,
194
- data: value.data.slice(1),
195
- }
196
- : value;
214
+ // Transformed data through all transform pipes
215
+ yield config?.openaiCompatible
216
+ ? {
217
+ ...value,
218
+ data: (value as any).data.slice(1),
219
+ }
220
+ : value;
221
+ }
222
+ } finally {
223
+ reader.releaseLock();
197
224
  }
198
225
  };
199
226
 
@@ -139,9 +139,14 @@ export default function useChatController() {
139
139
 
140
140
  /**
141
141
  * 处理取消
142
+ * 1. 标记 interrupted 并重置 UI(finishResponse)
143
+ * 2. abort SSE 连接 —— Stream 内部的 Promise.race 会立即 reject AbortError,
144
+ * processSSEResponse 的 catch 会检测 interrupted 状态并调用 cancel API
142
145
  */
143
146
  var handleCancel = useCallback(function () {
147
+ var _currentQARef$current;
144
148
  finishResponse('interrupted');
149
+ (_currentQARef$current = currentQARef.current.abortController) === null || _currentQARef$current === void 0 || _currentQARef$current.abort();
145
150
  }, [finishResponse]);
146
151
 
147
152
  /**
@@ -184,8 +189,8 @@ export default function useChatController() {
184
189
  */
185
190
  var handleReconnect = useCallback( /*#__PURE__*/function () {
186
191
  var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(sessionId) {
187
- var _currentQARef$current;
188
192
  var _currentQARef$current2;
193
+ var _currentQARef$current3;
189
194
  return _regeneratorRuntime().wrap(function _callee4$(_context4) {
190
195
  while (1) switch (_context4.prev = _context4.next) {
191
196
  case 0:
@@ -200,9 +205,9 @@ export default function useChatController() {
200
205
  // Treat as idle: remove the empty placeholder and reset loading.
201
206
  // HTTP errors and normal SSE completions both call onFinish() → msgStatus becomes 'finished',
202
207
  // so they are correctly excluded from this cleanup.
203
- if (((_currentQARef$current = currentQARef.current.response) === null || _currentQARef$current === void 0 ? void 0 : _currentQARef$current.msgStatus) === 'generating') {
208
+ if (((_currentQARef$current2 = currentQARef.current.response) === null || _currentQARef$current2 === void 0 ? void 0 : _currentQARef$current2.msgStatus) === 'generating') {
204
209
  setLoading(false);
205
- if ((_currentQARef$current2 = currentQARef.current.response) !== null && _currentQARef$current2 !== void 0 && _currentQARef$current2.id) {
210
+ if ((_currentQARef$current3 = currentQARef.current.response) !== null && _currentQARef$current3 !== void 0 && _currentQARef$current3.id) {
206
211
  messageHandler.removeMessageById(currentQARef.current.response.id);
207
212
  }
208
213
  currentQARef.current.response = undefined;
@@ -220,8 +225,8 @@ export default function useChatController() {
220
225
 
221
226
  // 监听会话切换,断开当前 SSE 连接(不通知后端取消)并重置状态
222
227
  useEffect(function () {
223
- var _currentQARef$current3;
224
- (_currentQARef$current3 = currentQARef.current.abortController) === null || _currentQARef$current3 === void 0 || _currentQARef$current3.abort();
228
+ var _currentQARef$current4;
229
+ (_currentQARef$current4 = currentQARef.current.abortController) === null || _currentQARef$current4 === void 0 || _currentQARef$current4.abort();
225
230
  currentQARef.current = {
226
231
  request: undefined,
227
232
  response: undefined,
@@ -102,7 +102,8 @@ export default function useChatRequest(options) {
102
102
  }(), []);
103
103
  var processSSEResponse = useCallback( /*#__PURE__*/function () {
104
104
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(response) {
105
- var currentApiOptions, agentScopeRuntimeResponseBuilder, data, res, _iteratorAbruptCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, chunk, _currentQARef$current, _res$output, _currentQARef$current2, responseParser, chunkData, _res;
105
+ var _currentQARef$current;
106
+ var currentApiOptions, agentScopeRuntimeResponseBuilder, data, res, abortSignal, _iteratorAbruptCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, chunk, _currentQARef$current2, _res$output, _currentQARef$current3, responseParser, chunkData, _res, _currentQARef$current4;
106
107
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
107
108
  while (1) switch (_context2.prev = _context2.next) {
108
109
  case 0:
@@ -144,27 +145,29 @@ export default function useChatRequest(options) {
144
145
  onFinish();
145
146
  return _context2.abrupt("return");
146
147
  case 15:
147
- _context2.prev = 15;
148
+ abortSignal = (_currentQARef$current = currentQARef.current.abortController) === null || _currentQARef$current === void 0 ? void 0 : _currentQARef$current.signal;
149
+ _context2.prev = 16;
148
150
  _iteratorAbruptCompletion2 = false;
149
151
  _didIteratorError2 = false;
150
- _context2.prev = 18;
152
+ _context2.prev = 19;
151
153
  _iterator2 = _asyncIterator(Stream({
152
- readableStream: response.body
154
+ readableStream: response.body,
155
+ signal: abortSignal
153
156
  }));
154
- case 20:
155
- _context2.next = 22;
157
+ case 21:
158
+ _context2.next = 23;
156
159
  return _iterator2.next();
157
- case 22:
160
+ case 23:
158
161
  if (!(_iteratorAbruptCompletion2 = !(_step2 = _context2.sent).done)) {
159
- _context2.next = 39;
162
+ _context2.next = 40;
160
163
  break;
161
164
  }
162
165
  chunk = _step2.value;
163
- if (!(((_currentQARef$current = currentQARef.current.response) === null || _currentQARef$current === void 0 ? void 0 : _currentQARef$current.msgStatus) === 'interrupted')) {
164
- _context2.next = 30;
166
+ if (!(((_currentQARef$current2 = currentQARef.current.response) === null || _currentQARef$current2 === void 0 ? void 0 : _currentQARef$current2.msgStatus) === 'interrupted')) {
167
+ _context2.next = 31;
165
168
  break;
166
169
  }
167
- (_currentQARef$current2 = currentQARef.current.abortController) === null || _currentQARef$current2 === void 0 || _currentQARef$current2.abort();
170
+ (_currentQARef$current3 = currentQARef.current.abortController) === null || _currentQARef$current3 === void 0 || _currentQARef$current3.abort();
168
171
  if (currentApiOptions.cancel) {
169
172
  currentApiOptions.cancel({
170
173
  session_id: getCurrentSessionId()
@@ -175,17 +178,17 @@ export default function useChatRequest(options) {
175
178
  data: agentScopeRuntimeResponseBuilder.cancel()
176
179
  }];
177
180
  updateMessage(currentQARef.current.response);
178
- return _context2.abrupt("break", 39);
179
- case 30:
181
+ return _context2.abrupt("break", 40);
182
+ case 31:
180
183
  responseParser = apiOptionsRef.current.responseParser || JSON.parse;
181
184
  chunkData = responseParser(chunk.data);
182
185
  _res = agentScopeRuntimeResponseBuilder.handle(chunkData);
183
186
  if (!(_res.status !== AgentScopeRuntimeRunStatus.Failed && !((_res$output = _res.output) !== null && _res$output !== void 0 && (_res$output = _res$output[0]) !== null && _res$output !== void 0 && (_res$output = _res$output.content) !== null && _res$output !== void 0 && _res$output.length))) {
184
- _context2.next = 35;
187
+ _context2.next = 36;
185
188
  break;
186
189
  }
187
- return _context2.abrupt("continue", 36);
188
- case 35:
190
+ return _context2.abrupt("continue", 37);
191
+ case 36:
189
192
  if (currentQARef.current.response) {
190
193
  currentQARef.current.response.cards = [{
191
194
  code: 'AgentScopeRuntimeResponseCard',
@@ -197,50 +200,63 @@ export default function useChatRequest(options) {
197
200
  updateMessage(currentQARef.current.response);
198
201
  }
199
202
  }
200
- case 36:
203
+ case 37:
201
204
  _iteratorAbruptCompletion2 = false;
202
- _context2.next = 20;
205
+ _context2.next = 21;
203
206
  break;
204
- case 39:
205
- _context2.next = 45;
207
+ case 40:
208
+ _context2.next = 46;
206
209
  break;
207
- case 41:
208
- _context2.prev = 41;
209
- _context2.t1 = _context2["catch"](18);
210
+ case 42:
211
+ _context2.prev = 42;
212
+ _context2.t1 = _context2["catch"](19);
210
213
  _didIteratorError2 = true;
211
214
  _iteratorError2 = _context2.t1;
212
- case 45:
213
- _context2.prev = 45;
215
+ case 46:
214
216
  _context2.prev = 46;
217
+ _context2.prev = 47;
215
218
  if (!(_iteratorAbruptCompletion2 && _iterator2.return != null)) {
216
- _context2.next = 50;
219
+ _context2.next = 51;
217
220
  break;
218
221
  }
219
- _context2.next = 50;
222
+ _context2.next = 51;
220
223
  return _iterator2.return();
221
- case 50:
222
- _context2.prev = 50;
224
+ case 51:
225
+ _context2.prev = 51;
223
226
  if (!_didIteratorError2) {
224
- _context2.next = 53;
227
+ _context2.next = 54;
225
228
  break;
226
229
  }
227
230
  throw _iteratorError2;
228
- case 53:
229
- return _context2.finish(50);
230
231
  case 54:
231
- return _context2.finish(45);
232
+ return _context2.finish(51);
232
233
  case 55:
233
- _context2.next = 60;
234
+ return _context2.finish(46);
235
+ case 56:
236
+ _context2.next = 61;
234
237
  break;
235
- case 57:
236
- _context2.prev = 57;
237
- _context2.t2 = _context2["catch"](15);
238
- console.error(_context2.t2);
239
- case 60:
238
+ case 58:
239
+ _context2.prev = 58;
240
+ _context2.t2 = _context2["catch"](16);
241
+ if (((_currentQARef$current4 = currentQARef.current.response) === null || _currentQARef$current4 === void 0 ? void 0 : _currentQARef$current4.msgStatus) === 'interrupted') {
242
+ if (currentApiOptions.cancel) {
243
+ currentApiOptions.cancel({
244
+ session_id: getCurrentSessionId()
245
+ });
246
+ }
247
+ currentQARef.current.response.cards = [{
248
+ code: 'AgentScopeRuntimeResponseCard',
249
+ data: agentScopeRuntimeResponseBuilder.cancel()
250
+ }];
251
+ updateMessage(currentQARef.current.response);
252
+ } else {
253
+ console.error(_context2.t2);
254
+ }
255
+ case 61:
240
256
  case "end":
241
257
  return _context2.stop();
242
258
  }
243
- }, _callee2, null, [[3, 11], [15, 57], [18, 41, 45, 55], [46,, 50, 54]]);
259
+ }, _callee2, null, [[3, 11], [16, 58], [19, 42, 46, 56], [47,, 51, 55]]);
244
260
  }));
245
261
  return function (_x2) {
246
262
  return _ref2.apply(this, arguments);
@@ -248,14 +264,14 @@ export default function useChatRequest(options) {
248
264
  }(), [getCurrentSessionId, currentQARef, updateMessage, onFinish]);
249
265
  var request = useCallback( /*#__PURE__*/function () {
250
266
  var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(historyMessages, biz_params) {
251
- var _currentQARef$current3;
267
+ var _currentQARef$current5;
252
268
  var currentApiOptions, _currentApiOptions$en, enableHistoryMessages, abortSignal, response;
253
269
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
254
270
  while (1) switch (_context3.prev = _context3.next) {
255
271
  case 0:
256
272
  currentApiOptions = apiOptionsRef.current;
257
273
  _currentApiOptions$en = currentApiOptions.enableHistoryMessages, enableHistoryMessages = _currentApiOptions$en === void 0 ? false : _currentApiOptions$en;
258
- abortSignal = (_currentQARef$current3 = currentQARef.current.abortController) === null || _currentQARef$current3 === void 0 ? void 0 : _currentQARef$current3.signal;
274
+ abortSignal = (_currentQARef$current5 = currentQARef.current.abortController) === null || _currentQARef$current5 === void 0 ? void 0 : _currentQARef$current5.signal;
259
275
  _context3.prev = 3;
260
276
  if (!currentApiOptions.fetch) {
261
277
  _context3.next = 10;
@@ -315,7 +331,7 @@ export default function useChatRequest(options) {
315
331
  }(), [getCurrentSessionId, currentQARef, processSSEResponse]);
316
332
  var reconnect = useCallback( /*#__PURE__*/function () {
317
333
  var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(sessionId) {
318
- var _currentQARef$current4;
334
+ var _currentQARef$current6;
319
335
  var currentApiOptions, abortSignal, response;
320
336
  return _regeneratorRuntime().wrap(function _callee4$(_context4) {
321
337
  while (1) switch (_context4.prev = _context4.next) {
@@ -327,7 +343,7 @@ export default function useChatRequest(options) {
327
343
  }
328
344
  return _context4.abrupt("return");
329
345
  case 3:
330
- abortSignal = (_currentQARef$current4 = currentQARef.current.abortController) === null || _currentQARef$current4 === void 0 ? void 0 : _currentQARef$current4.signal;
346
+ abortSignal = (_currentQARef$current6 = currentQARef.current.abortController) === null || _currentQARef$current6 === void 0 ? void 0 : _currentQARef$current6.signal;
331
347
  _context4.prev = 4;
332
348
  _context4.next = 7;
333
349
  return currentApiOptions.reconnect({
@@ -1,3 +1,7 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
3
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
4
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
1
5
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
2
6
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
7
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -8,7 +12,6 @@ import { CodeHighlighter, Mermaid } from '@ant-design/x';
8
12
  import { useProviderContext } from "../../..";
9
13
  import { useCallback, useRef, useState } from 'react';
10
14
  import { SparkCopyLine, SparkDownloadLine, SparkTrueLine } from '@agentscope-ai/icons';
11
- import { copy } from "../../../Util/copy";
12
15
  import { jsx as _jsx } from "react/jsx-runtime";
13
16
  import { jsxs as _jsxs } from "react/jsx-runtime";
14
17
  var LANG_EXT_MAP = {
@@ -75,17 +78,46 @@ function CodeHeader(_ref) {
75
78
  a.click();
76
79
  URL.revokeObjectURL(url);
77
80
  }, [lang, content]);
78
- var handleCopy = useCallback(function () {
79
- copy(content).then(function () {
80
- clearTimeout(timer.current);
81
- setCopied(true);
82
- timer.current = setTimeout(function () {
83
- return setCopied(false);
84
- }, 2000);
85
- }).catch(function () {
86
- console.warn('Copy failed');
87
- });
88
- }, [content]);
81
+ var handleCopy = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
82
+ var textarea;
83
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
84
+ while (1) switch (_context.prev = _context.next) {
85
+ case 0:
86
+ _context.prev = 0;
87
+ if (!(window.isSecureContext && navigator.clipboard)) {
88
+ _context.next = 6;
89
+ break;
90
+ }
91
+ _context.next = 4;
92
+ return navigator.clipboard.writeText(content);
93
+ case 4:
94
+ _context.next = 13;
95
+ break;
96
+ case 6:
97
+ textarea = document.createElement('textarea');
98
+ textarea.value = content;
99
+ textarea.style.cssText = 'position:fixed;left:-9999px';
100
+ document.body.appendChild(textarea);
101
+ textarea.select();
102
+ document.execCommand('copy');
103
+ document.body.removeChild(textarea);
104
+ case 13:
105
+ setCopied(true);
106
+ setTimeout(function () {
107
+ return setCopied(false);
108
+ }, 1000);
109
+ _context.next = 20;
110
+ break;
111
+ case 17:
112
+ _context.prev = 17;
113
+ _context.t0 = _context["catch"](0);
114
+ console.warn('Copy failed');
115
+ case 20:
116
+ case "end":
117
+ return _context.stop();
118
+ }
119
+ }, _callee, null, [[0, 17]]);
120
+ })), [content]);
89
121
  return /*#__PURE__*/_jsxs("div", {
90
122
  className: prefixCls,
91
123
  children: [/*#__PURE__*/_jsx("div", {
@@ -24,6 +24,11 @@ export interface StreamOptions<Output> {
24
24
  * @link https://developer.mozilla.org/en-US/docs/Web/API/TransformStream
25
25
  */
26
26
  transformStream?: TransformStream<string, Output>;
27
+ /**
28
+ * @description 用于中断流读取的 AbortSignal
29
+ * @descriptionEn AbortSignal to cancel stream reading
30
+ */
31
+ signal?: AbortSignal;
27
32
  }
28
33
  type XReadableStream<R = SSEOutput> = ReadableStream<R> & AsyncGenerator<R>;
29
34
  /**
@@ -132,7 +132,8 @@ function splitPart() {
132
132
  */
133
133
  function Stream(options, config) {
134
134
  var readableStream = options.readableStream,
135
- transformStream = options.transformStream;
135
+ transformStream = options.transformStream,
136
+ signal = options.signal;
136
137
  if (!(readableStream instanceof ReadableStream)) {
137
138
  throw new Error('The options.readableStream must be an instance of ReadableStream.');
138
139
  }
@@ -151,46 +152,65 @@ function Stream(options, config) {
151
152
 
152
153
  /** support async iterator */
153
154
  stream[Symbol.asyncIterator] = /*#__PURE__*/_wrapAsyncGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
154
- var reader, _yield$_awaitAsyncGen, done, value;
155
+ var reader, readPromise, _yield$_awaitAsyncGen, done, value;
155
156
  return _regeneratorRuntime().wrap(function _callee$(_context) {
156
157
  while (1) switch (_context.prev = _context.next) {
157
158
  case 0:
158
159
  reader = this.getReader();
159
- case 1:
160
+ _context.prev = 1;
161
+ case 2:
160
162
  if (!true) {
161
- _context.next = 15;
163
+ _context.next = 18;
162
164
  break;
163
165
  }
164
- _context.next = 4;
165
- return _awaitAsyncGenerator(reader.read());
166
- case 4:
166
+ readPromise = reader.read();
167
+ if (signal) {
168
+ readPromise = Promise.race([readPromise, new Promise(function (_, reject) {
169
+ if (signal.aborted) {
170
+ reject(new DOMException('The operation was aborted.', 'AbortError'));
171
+ return;
172
+ }
173
+ signal.addEventListener('abort', function () {
174
+ reject(new DOMException('The operation was aborted.', 'AbortError'));
175
+ }, {
176
+ once: true
177
+ });
178
+ })]);
179
+ }
180
+ _context.next = 7;
181
+ return _awaitAsyncGenerator(readPromise);
182
+ case 7:
167
183
  _yield$_awaitAsyncGen = _context.sent;
168
184
  done = _yield$_awaitAsyncGen.done;
169
185
  value = _yield$_awaitAsyncGen.value;
170
186
  if (!done) {
171
- _context.next = 9;
187
+ _context.next = 12;
172
188
  break;
173
189
  }
174
- return _context.abrupt("break", 15);
175
- case 9:
190
+ return _context.abrupt("break", 18);
191
+ case 12:
176
192
  if (value) {
177
- _context.next = 11;
193
+ _context.next = 14;
178
194
  break;
179
195
  }
180
- return _context.abrupt("continue", 1);
181
- case 11:
182
- _context.next = 13;
196
+ return _context.abrupt("continue", 2);
197
+ case 14:
198
+ _context.next = 16;
183
199
  return config !== null && config !== void 0 && config.openaiCompatible ? _objectSpread(_objectSpread({}, value), {}, {
184
200
  data: value.data.slice(1)
185
201
  }) : value;
186
- case 13:
187
- _context.next = 1;
202
+ case 16:
203
+ _context.next = 2;
188
204
  break;
189
- case 15:
205
+ case 18:
206
+ _context.prev = 18;
207
+ reader.releaseLock();
208
+ return _context.finish(18);
209
+ case 21:
190
210
  case "end":
191
211
  return _context.stop();
192
212
  }
193
- }, _callee, this);
213
+ }, _callee, this, [[1,, 18, 21]]);
194
214
  }));
195
215
  return stream;
196
216
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentscope-ai/chat",
3
- "version": "1.1.61",
3
+ "version": "1.1.62",
4
4
  "description": "a free and open-source chat framework for building excellent LLM-powered chat experiences",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": [
@@ -1,75 +0,0 @@
1
- # agentscope-runtime-starter-webui
2
-
3
- ## node version
4
-
5
- > =22
6
-
7
- ## install
8
-
9
- ```
10
- $ npm run install
11
- ```
12
-
13
- ## dev
14
-
15
- ```
16
- $ npm run dev
17
- ```
18
-
19
- ## build
20
-
21
- ```
22
- $ npm run build
23
- ```
24
-
25
- ## Core Code
26
- ```tsx
27
- import { AgentScopeRuntimeWebUI } from '@agentscope-ai/chat';
28
-
29
- const options = {
30
- theme: {
31
- colorPrimary: '#615CED',
32
- darkMode: true,
33
- prefix: 'agentscope-runtime-webui',
34
- leftHeader: {
35
- logo: 'https://img.alicdn.com/imgextra/i2/O1CN01lmoGYn1kjoXATy4PX_!!6000000004720-2-tps-200-200.png',
36
- title: 'Runtime WebUI',
37
- },
38
- },
39
- sender: {
40
- maxLength: 10000,
41
- disclaimer:
42
- 'AI can also make mistakes, so please check carefully and use it with caution',
43
- },
44
-
45
- welcome: {
46
- greeting: 'Hello, how can I help you today?',
47
- description:
48
- 'I am a helpful assistant that can help you with your questions.',
49
- avatar:
50
- 'https://img.alicdn.com/imgextra/i2/O1CN01lmoGYn1kjoXATy4PX_!!6000000004720-2-tps-200-200.png',
51
- prompts: [
52
- {
53
- value: 'Hello',
54
- },
55
- {
56
- value: 'How are you?',
57
- },
58
- {
59
- value: 'What can you do?',
60
- },
61
- ],
62
- },
63
- api: {
64
- baseURL: 'YOUR_API_URL',
65
- token: 'YOUR_API_TOKEN', // is not required
66
- },
67
- };
68
-
69
-
70
- <AgentScopeRuntimeWebUI
71
- options={options}
72
- />
73
-
74
-
75
- ```
@@ -1,28 +0,0 @@
1
- import js from '@eslint/js'
2
- import globals from 'globals'
3
- import reactHooks from 'eslint-plugin-react-hooks'
4
- import reactRefresh from 'eslint-plugin-react-refresh'
5
- import tseslint from 'typescript-eslint'
6
-
7
- export default tseslint.config(
8
- { ignores: ['dist'] },
9
- {
10
- extends: [js.configs.recommended, ...tseslint.configs.recommended],
11
- files: ['**/*.{ts,tsx}'],
12
- languageOptions: {
13
- ecmaVersion: 2020,
14
- globals: globals.browser,
15
- },
16
- plugins: {
17
- 'react-hooks': reactHooks,
18
- 'react-refresh': reactRefresh,
19
- },
20
- rules: {
21
- ...reactHooks.configs.recommended.rules,
22
- 'react-refresh/only-export-components': [
23
- 'warn',
24
- { allowConstantExport: true },
25
- ],
26
- },
27
- },
28
- )
@@ -1,12 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>AgentScope Runtime Starter WebUI</title>
7
- </head>
8
- <body>
9
- <div id="root"></div>
10
- <script type="module" src="/src/main.tsx"></script>
11
- </body>
12
- </html>
@@ -1,34 +0,0 @@
1
- {
2
- "name": "agentscope-runtime-starter-webui",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite --host",
8
- "build": "tsc -b && vite build",
9
- "lint": "eslint .",
10
- "preview": "vite preview"
11
- },
12
- "dependencies": {
13
- "@agentscope-ai/icons": "^1.0.46",
14
- "@agentscope-ai/chat": "^1.1.44",
15
- "@agentscope-ai/design": "^1.0.19",
16
- "antd": "^5.29.1",
17
- "antd-style": "^3.7.1",
18
- "react": "^18",
19
- "react-dom": "^18"
20
- },
21
- "devDependencies": {
22
- "@eslint/js": "^9.25.0",
23
- "@types/react": "^18",
24
- "@types/react-dom": "^18",
25
- "@vitejs/plugin-react": "^4.4.1",
26
- "eslint": "^9.25.0",
27
- "eslint-plugin-react-hooks": "^5.2.0",
28
- "eslint-plugin-react-refresh": "^0.4.19",
29
- "globals": "^16.0.0",
30
- "typescript": "~5.8.3",
31
- "typescript-eslint": "^8.30.1",
32
- "vite": "^6.3.5"
33
- }
34
- }
@@ -1,20 +0,0 @@
1
- import Chat from './components/Chat';
2
-
3
- import { createGlobalStyle } from 'antd-style';
4
-
5
-
6
- const GlobalStyle = createGlobalStyle`
7
- * {
8
- margin: 0;
9
- box-sizing: border-box;
10
- }
11
- `;
12
-
13
- function App() {
14
- return <>
15
- <GlobalStyle />
16
- <Chat />
17
- </>
18
- }
19
-
20
- export default App
@@ -1,37 +0,0 @@
1
- import { Form } from 'antd';
2
- import { createStyles } from 'antd-style';
3
-
4
-
5
- interface FormItemProps {
6
- name: string | string[];
7
- label: string;
8
- isList?: boolean;
9
- children: any;
10
- normalize?: (value: any) => any;
11
- }
12
-
13
-
14
- const useStyles = createStyles(({ token }) => ({
15
- label: {
16
- marginBottom: 6,
17
- fontSize: 12,
18
- color: token.colorTextSecondary,
19
- },
20
-
21
- }));
22
-
23
- export default function FormItem(props: FormItemProps) {
24
- const { styles } = useStyles();
25
-
26
-
27
- const node = props.isList ?
28
- <Form.List name={props.name}>{props.children}</Form.List> :
29
- <Form.Item name={props.name} normalize={props.normalize}>{props.children}</Form.Item>;
30
-
31
-
32
- return <div>
33
- {props.label && <div className={styles.label}>{props.label}</div>}
34
- {node}
35
- </div>
36
-
37
- }
@@ -1,160 +0,0 @@
1
- import React from 'react';
2
- import { Form, Input, ColorPicker, Flex, Divider, InputNumber } from 'antd';
3
- import { createStyles } from 'antd-style';
4
- import { Button, IconButton, Switch } from '@agentscope-ai/design'
5
- import { SparkDeleteLine, SparkPlusLine } from '@agentscope-ai/icons';
6
- import FormItem from './FormItem';
7
- import defaultConfig from './defaultConfig';
8
-
9
- const useStyles = createStyles(({ token }) => ({
10
- container: {
11
- height: '100%',
12
- display: 'flex',
13
- flexDirection: 'column',
14
- },
15
-
16
- form: {
17
- height: 0,
18
- flex: 1,
19
- padding: '8px 16px 16px 16px',
20
- overflow: 'auto',
21
- },
22
- actions: {
23
- padding: 16,
24
- display: 'flex',
25
- borderTop: `1px solid ${token.colorBorderSecondary}`,
26
- justifyContent: 'flex-end',
27
- gap: 16,
28
- }
29
-
30
- }));
31
-
32
- interface OptionsEditorProps {
33
- value?: any;
34
- onChange?: any;
35
- }
36
-
37
- const OptionsEditor: React.FC<OptionsEditorProps> = ({
38
- value,
39
- onChange,
40
- }) => {
41
- const { styles } = useStyles();
42
- const [form] = Form.useForm();
43
-
44
-
45
- const handleSave = () => {
46
- form.validateFields().then((values) => {
47
- onChange(values);
48
- });
49
- };
50
-
51
- const handleReset = () => {
52
- form.setFieldsValue(defaultConfig);
53
- };
54
-
55
- return (
56
- <div className={styles.container}>
57
- <Form
58
- className={styles.form}
59
- form={form}
60
- layout="vertical"
61
- initialValues={value}
62
- >
63
-
64
-
65
- <Divider orientation="left">Theme</Divider>
66
-
67
- <FormItem name={['theme', 'colorPrimary']} label="colorPrimary" normalize={value => value.toHexString()}>
68
- <ColorPicker />
69
- </FormItem>
70
-
71
- <FormItem name={['theme', 'colorBgBase']} label="colorBgBase" normalize={value => value.toHexString()}>
72
- <ColorPicker />
73
- </FormItem>
74
-
75
- <FormItem name={['theme', 'colorTextBase']} label="colorTextBase" normalize={value => value.toHexString()}>
76
- <ColorPicker />
77
- </FormItem>
78
-
79
- <FormItem name={['theme', 'darkMode']} label="darkMode" >
80
- <Switch />
81
- </FormItem>
82
-
83
- <FormItem name={['theme', 'leftHeader', 'logo']} label="leftHeader.logo" >
84
- <Input />
85
- </FormItem>
86
-
87
- <FormItem name={['theme', 'leftHeader', 'title']} label="leftHeader.title" >
88
- <Input />
89
- </FormItem>
90
-
91
- <Divider orientation="left">Sender</Divider>
92
-
93
-
94
- <FormItem name={['sender', 'disclaimer']} label="disclaimer" >
95
- <Input />
96
- </FormItem>
97
-
98
-
99
-
100
-
101
- <FormItem name={['sender', 'maxLength']} label="maxLength" >
102
- <InputNumber min={1000} />
103
- </FormItem>
104
-
105
- <Divider orientation="left">Welcome</Divider>
106
-
107
-
108
- <FormItem name={['welcome', 'greeting']} label="greeting" >
109
- <Input />
110
- </FormItem>
111
-
112
- <FormItem name={['welcome', 'description']} label="description" >
113
- <Input />
114
- </FormItem>
115
-
116
- <FormItem name={['welcome', 'avatar']} label="avatar" >
117
- <Input />
118
- </FormItem>
119
-
120
-
121
- <FormItem name={['welcome', 'prompts']} isList label="prompts" >
122
- {(fields: { key: string, name: string }[], { add, remove }: { add: (item: any) => void, remove: (name: string) => void }) => {
123
- return <div>
124
- {fields.map(field => {
125
- return <Flex key={field.key} gap={6}>
126
- <Form.Item style={{ flex: 1 }} key={field.key} name={[field.name, 'value']}>
127
- <Input />
128
- </Form.Item>
129
- <IconButton icon={<SparkPlusLine />} onClick={() => add({})}></IconButton>
130
- <IconButton icon={<SparkDeleteLine />} onClick={() => remove(field.name)}></IconButton>
131
- </Flex>
132
- })}
133
- </div>
134
- }}
135
- </FormItem>
136
-
137
-
138
- <Divider orientation="left">API</Divider>
139
-
140
- <FormItem name={['api', 'baseURL']} label="baseURL" >
141
- <Input />
142
- </FormItem>
143
-
144
- <FormItem name={['api', 'token']} label="token" >
145
- <Input />
146
- </FormItem>
147
- </Form>
148
-
149
- <div className={styles.actions}>
150
- <Button onClick={handleReset}>Reset</Button>
151
- <Button type="primary" onClick={handleSave}>
152
- Save & Copy
153
- </Button>
154
- </div>
155
- </div>
156
- );
157
- };
158
-
159
- export default OptionsEditor;
160
-
@@ -1,41 +0,0 @@
1
- export default {
2
- theme: {
3
- colorPrimary: '#615CED',
4
- darkMode: true,
5
- prefix: 'agentscope-runtime-webui',
6
- leftHeader: {
7
- logo: 'https://img.alicdn.com/imgextra/i2/O1CN01lmoGYn1kjoXATy4PX_!!6000000004720-2-tps-200-200.png',
8
- title: 'Runtime WebUI',
9
- },
10
- },
11
- sender: {
12
- attachments: false,
13
- maxLength: 10000,
14
- disclaimer:
15
- 'AI can also make mistakes, so please check carefully and use it with caution',
16
- },
17
-
18
- welcome: {
19
- greeting: 'Hello, how can I help you today?',
20
- description:
21
- 'I am a helpful assistant that can help you with your questions.',
22
- avatar:
23
- 'https://img.alicdn.com/imgextra/i2/O1CN01lmoGYn1kjoXATy4PX_!!6000000004720-2-tps-200-200.png',
24
- prompts: [
25
- {
26
- value: 'Hello',
27
- },
28
- {
29
- value: 'How are you?',
30
- },
31
- {
32
- value: 'What can you do?',
33
- },
34
- ],
35
- },
36
- api: {
37
- baseURL: BASE_URL,
38
- token: TOKEN,
39
- },
40
- };
41
-
@@ -1,27 +0,0 @@
1
- import { SparkSettingLine } from "@agentscope-ai/icons";
2
- import { IconButton, Drawer } from "@agentscope-ai/design";
3
- import { useState } from "react";
4
- import OptionsEditor from "./OptionsEditor";
5
-
6
- interface OptionsPanelProps {
7
- value?: any;
8
- onChange?: any;
9
- }
10
-
11
- export default function OptionsPanel(props: OptionsPanelProps) {
12
- const [open, setOpen] = useState(false);
13
-
14
- return <>
15
- <IconButton onClick={() => setOpen(true)} icon={<SparkSettingLine />} bordered={false} />
16
- <Drawer
17
- destroyOnHidden
18
- open={open}
19
- onClose={() => setOpen(false)}
20
- styles={{ body: { padding: 0 }, header: { padding: 8 } }}>
21
- <OptionsEditor value={props.value} onChange={(v: typeof props.value) => {
22
- setOpen(false);
23
- props.onChange(v);
24
- }} />
25
- </Drawer>
26
- </>
27
- }
@@ -1,45 +0,0 @@
1
- import { AgentScopeRuntimeWebUI, IAgentScopeRuntimeWebUIOptions } from '@agentscope-ai/chat';
2
- import OptionsPanel from './OptionsPanel';
3
- import { useMemo } from 'react';
4
- import sessionApi from './sessionApi';
5
- import { useLocalStorageState } from 'ahooks';
6
- import defaultConfig from './OptionsPanel/defaultConfig';
7
-
8
- export default function () {
9
- const [optionsConfig, setOptionsConfig] = useLocalStorageState('agent-scope-runtime-webui-options', {
10
- defaultValue: defaultConfig,
11
- listenStorageChange: true,
12
- });
13
-
14
- const options = useMemo(() => {
15
- const rightHeader = <OptionsPanel value={optionsConfig} onChange={(v: typeof optionsConfig) => {
16
- setOptionsConfig(prev => ({
17
- ...prev,
18
- ...v,
19
- }));
20
- }} />;
21
-
22
-
23
-
24
- return {
25
- ...optionsConfig,
26
- session: {
27
- multiple: true,
28
- api: sessionApi,
29
- },
30
- theme: {
31
- ...optionsConfig.theme,
32
- rightHeader,
33
- },
34
- };
35
- }, [optionsConfig]);
36
-
37
-
38
-
39
-
40
- return <div style={{ height: '100vh' }}>
41
- <AgentScopeRuntimeWebUI
42
- options={options as unknown as IAgentScopeRuntimeWebUIOptions}
43
- />
44
- </div>;
45
- }
@@ -1,53 +0,0 @@
1
- import {
2
- IAgentScopeRuntimeWebUISession,
3
- IAgentScopeRuntimeWebUISessionAPI,
4
- } from '@agentscope-ai/chat';
5
-
6
- class SessionApi implements IAgentScopeRuntimeWebUISessionAPI {
7
- private lsKey: string;
8
- private sessionList: IAgentScopeRuntimeWebUISession[];
9
-
10
- constructor() {
11
- this.lsKey = 'agent-scope-runtime-webui-sessions';
12
- this.sessionList = [];
13
- }
14
-
15
- async getSessionList() {
16
- this.sessionList = JSON.parse(localStorage.getItem(this.lsKey) || '[]');
17
- return [...this.sessionList];
18
- }
19
-
20
- async getSession(sessionId: string) {
21
- return this.sessionList.find((session) => session.id === sessionId) as IAgentScopeRuntimeWebUISession;
22
- }
23
-
24
- async updateSession(session: Partial<IAgentScopeRuntimeWebUISession>) {
25
- const index = this.sessionList.findIndex((item) => item.id === session.id);
26
- if (index > -1) {
27
- this.sessionList[index] = {
28
- ...this.sessionList[index],
29
- ...session,
30
- };
31
- localStorage.setItem(this.lsKey, JSON.stringify(this.sessionList));
32
- }
33
-
34
- return [...this.sessionList];
35
- }
36
-
37
- async createSession(session: Partial<IAgentScopeRuntimeWebUISession>) {
38
- session.id = Date.now().toString();
39
- this.sessionList.unshift(session as IAgentScopeRuntimeWebUISession);
40
- localStorage.setItem(this.lsKey, JSON.stringify(this.sessionList));
41
- return [...this.sessionList];
42
- }
43
-
44
- async removeSession(session: Partial<IAgentScopeRuntimeWebUISession>) {
45
- this.sessionList = this.sessionList.filter(
46
- (item) => item.id !== session.id,
47
- );
48
- localStorage.setItem(this.lsKey, JSON.stringify(this.sessionList));
49
- return [...this.sessionList];
50
- }
51
- }
52
-
53
- export default new SessionApi();
@@ -1,9 +0,0 @@
1
- import { StrictMode } from 'react'
2
- import { createRoot } from 'react-dom/client'
3
- import App from './App.tsx'
4
-
5
- createRoot(document.getElementById('root')!).render(
6
- <StrictMode>
7
- <App />
8
- </StrictMode>,
9
- )
@@ -1,4 +0,0 @@
1
- /// <reference types="vite/client" />
2
-
3
- declare const BASE_URL: string;
4
- declare const TOKEN: string;
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
- "target": "ES2020",
5
- "useDefineForClassFields": true,
6
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
- "module": "ESNext",
8
- "skipLibCheck": true,
9
-
10
- "moduleResolution": "bundler",
11
- "allowImportingTsExtensions": true,
12
- "moduleDetection": "force",
13
- "noEmit": true,
14
- "jsx": "react-jsx",
15
-
16
- "strict": true,
17
- "noUnusedLocals": true,
18
- "noUnusedParameters": true,
19
- "erasableSyntaxOnly": true,
20
- "noFallthroughCasesInSwitch": true,
21
- "noUncheckedSideEffectImports": true
22
- },
23
- "include": ["src"]
24
- }
@@ -1,7 +0,0 @@
1
- {
2
- "files": [],
3
- "references": [
4
- { "path": "./tsconfig.app.json" },
5
- { "path": "./tsconfig.node.json" }
6
- ]
7
- }
@@ -1,22 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
- "target": "ES2022",
5
- "lib": ["ES2023"],
6
- "module": "ESNext",
7
- "skipLibCheck": true,
8
-
9
- "moduleResolution": "bundler",
10
- "allowImportingTsExtensions": true,
11
- "moduleDetection": "force",
12
- "noEmit": true,
13
-
14
- "strict": true,
15
- "noUnusedLocals": true,
16
- "noUnusedParameters": true,
17
- "erasableSyntaxOnly": true,
18
- "noFallthroughCasesInSwitch": true,
19
- "noUncheckedSideEffectImports": true
20
- },
21
- "include": ["vite.config.ts"]
22
- }
@@ -1,11 +0,0 @@
1
- import { defineConfig } from 'vite'
2
- import react from '@vitejs/plugin-react'
3
-
4
- export default defineConfig({
5
- define: {
6
- BASE_URL: JSON.stringify(process.env.BASE_URL || ''),
7
- TOKEN: JSON.stringify(process.env.TOKEN || ''),
8
- MOBILE: false,
9
- },
10
- plugins: [react()],
11
- })