@antdv-next/x-sdk 0.0.1 → 0.0.2
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/_util/resolveMaybeRef.d.ts +2 -0
- package/dist/_util/resolveMaybeRef.js +7 -0
- package/dist/_util/types.js +0 -0
- package/dist/chat-providers/AbstractChatProvider.d.ts +2 -2
- package/dist/chat-providers/AbstractChatProvider.js +42 -0
- package/dist/chat-providers/DeepSeekChatProvider.d.ts +2 -2
- package/dist/chat-providers/DeepSeekChatProvider.js +59 -0
- package/dist/chat-providers/DefaultChatProvider.d.ts +2 -2
- package/dist/chat-providers/DefaultChatProvider.js +26 -0
- package/dist/chat-providers/OpenAIChatProvider.d.ts +2 -2
- package/dist/chat-providers/OpenAIChatProvider.js +49 -0
- package/dist/chat-providers/index.js +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -3
- package/dist/node_modules/vitest/dist/@vitest/expect/index.js +1456 -0
- package/dist/node_modules/vitest/dist/@vitest/pretty-format/index.js +884 -0
- package/dist/node_modules/vitest/dist/@vitest/runner/chunk-artifact.js +1543 -0
- package/dist/node_modules/vitest/dist/@vitest/snapshot/index.js +660 -0
- package/dist/node_modules/vitest/dist/@vitest/spy/index.js +384 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/chunk-pathe.M-eThtNZ.js +80 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/diff.js +1304 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/display.js +556 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/error.js +27 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/helpers.js +179 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/offset.js +25 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/serialize.js +75 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/source-map.js +371 -0
- package/dist/node_modules/vitest/dist/@vitest/utils/timers.js +35 -0
- package/dist/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js +4 -0
- package/dist/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js +50 -0
- package/dist/node_modules/vitest/dist/chunks/test.CBQUpOM3.js +2640 -0
- package/dist/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js +42 -0
- package/dist/node_modules/vitest/dist/vendor/chai.js +2872 -0
- package/dist/node_modules/vitest/dist/vendor/magic-string.js +1009 -0
- package/dist/node_modules/vitest/dist/vendor/tinyrainbow.js +84 -0
- package/dist/x-chat/__tests__/index.test.d.ts +1 -0
- package/dist/x-chat/__tests__/index.test.js +257 -0
- package/dist/x-chat/index.d.ts +14 -23
- package/dist/x-chat/index.js +83 -66
- package/dist/x-chat/store.d.ts +2 -0
- package/dist/{store-C1diHqNH.js → x-chat/store.js} +15 -9
- package/dist/x-conversations/__tests__/index.test.d.ts +1 -0
- package/dist/x-conversations/__tests__/index.test.js +36 -0
- package/dist/x-conversations/index.d.ts +4 -4
- package/dist/x-conversations/index.js +37 -1
- package/dist/{x-conversations-BrKWhj5r.js → x-conversations/store.js} +2 -31
- package/dist/x-request/__tests__/index.test.d.ts +1 -0
- package/dist/x-request/__tests__/index.test.js +90 -0
- package/dist/x-request/index.d.ts +36 -11
- package/dist/x-request/index.js +246 -1
- package/dist/x-request/x-fetch.js +18 -0
- package/package.json +1 -1
- package/dist/chat-providers-5x9Va7p5.js +0 -167
- package/dist/x-request-BSFGaKND.js +0 -234
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//#region ../../node_modules/vitest/dist/vendor/tinyrainbow.mjs
|
|
2
|
+
var d = {
|
|
3
|
+
reset: [0, 0],
|
|
4
|
+
bold: [
|
|
5
|
+
1,
|
|
6
|
+
22,
|
|
7
|
+
"\x1B[22m\x1B[1m"
|
|
8
|
+
],
|
|
9
|
+
dim: [
|
|
10
|
+
2,
|
|
11
|
+
22,
|
|
12
|
+
"\x1B[22m\x1B[2m"
|
|
13
|
+
],
|
|
14
|
+
italic: [3, 23],
|
|
15
|
+
underline: [4, 24],
|
|
16
|
+
inverse: [7, 27],
|
|
17
|
+
hidden: [8, 28],
|
|
18
|
+
strikethrough: [9, 29],
|
|
19
|
+
black: [30, 39],
|
|
20
|
+
red: [31, 39],
|
|
21
|
+
green: [32, 39],
|
|
22
|
+
yellow: [33, 39],
|
|
23
|
+
blue: [34, 39],
|
|
24
|
+
magenta: [35, 39],
|
|
25
|
+
cyan: [36, 39],
|
|
26
|
+
white: [37, 39],
|
|
27
|
+
gray: [90, 39],
|
|
28
|
+
bgBlack: [40, 49],
|
|
29
|
+
bgRed: [41, 49],
|
|
30
|
+
bgGreen: [42, 49],
|
|
31
|
+
bgYellow: [43, 49],
|
|
32
|
+
bgBlue: [44, 49],
|
|
33
|
+
bgMagenta: [45, 49],
|
|
34
|
+
bgCyan: [46, 49],
|
|
35
|
+
bgWhite: [47, 49],
|
|
36
|
+
blackBright: [90, 39],
|
|
37
|
+
redBright: [91, 39],
|
|
38
|
+
greenBright: [92, 39],
|
|
39
|
+
yellowBright: [93, 39],
|
|
40
|
+
blueBright: [94, 39],
|
|
41
|
+
magentaBright: [95, 39],
|
|
42
|
+
cyanBright: [96, 39],
|
|
43
|
+
whiteBright: [97, 39],
|
|
44
|
+
bgBlackBright: [100, 49],
|
|
45
|
+
bgRedBright: [101, 49],
|
|
46
|
+
bgGreenBright: [102, 49],
|
|
47
|
+
bgYellowBright: [103, 49],
|
|
48
|
+
bgBlueBright: [104, 49],
|
|
49
|
+
bgMagentaBright: [105, 49],
|
|
50
|
+
bgCyanBright: [106, 49],
|
|
51
|
+
bgWhiteBright: [107, 49]
|
|
52
|
+
};
|
|
53
|
+
function g(e) {
|
|
54
|
+
return String(e);
|
|
55
|
+
}
|
|
56
|
+
g.open = "";
|
|
57
|
+
g.close = "";
|
|
58
|
+
function h() {
|
|
59
|
+
let e = typeof process != "undefined" ? process : void 0, n = (e == null ? void 0 : e.env) || {}, a = n.FORCE_TTY !== "false", i = (e == null ? void 0 : e.argv) || [];
|
|
60
|
+
return !("NO_COLOR" in n || i.includes("--no-color")) && ("FORCE_COLOR" in n || i.includes("--color") || (e == null ? void 0 : e.platform) === "win32" || a && n.TERM !== "dumb" || "CI" in n) || typeof window != "undefined" && !!window.chrome;
|
|
61
|
+
}
|
|
62
|
+
function f() {
|
|
63
|
+
let e = h(), n = (r, t, u, o) => {
|
|
64
|
+
let l = "", s = 0;
|
|
65
|
+
do
|
|
66
|
+
l += r.substring(s, o) + u, s = o + t.length, o = r.indexOf(t, s);
|
|
67
|
+
while (~o);
|
|
68
|
+
return l + r.substring(s);
|
|
69
|
+
}, a = (r, t, u = r) => {
|
|
70
|
+
let o = (l) => {
|
|
71
|
+
let s = String(l), b = s.indexOf(t, r.length);
|
|
72
|
+
return ~b ? r + n(s, t, u, b) + t : r + s + t;
|
|
73
|
+
};
|
|
74
|
+
return o.open = r, o.close = t, o;
|
|
75
|
+
}, i = { isColorSupported: e }, c = (r) => `\x1B[${r}m`;
|
|
76
|
+
for (let r in d) {
|
|
77
|
+
let t = d[r];
|
|
78
|
+
i[r] = e ? a(c(t[0]), c(t[1]), t[2]) : g;
|
|
79
|
+
}
|
|
80
|
+
return i;
|
|
81
|
+
}
|
|
82
|
+
var C = f();
|
|
83
|
+
//#endregion
|
|
84
|
+
export { C };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import AbstractChatProvider from "../../chat-providers/AbstractChatProvider.js";
|
|
2
|
+
import { AbstractXRequestClass } from "../../x-request/index.js";
|
|
3
|
+
import { chatMessagesStoreHelper } from "../store.js";
|
|
4
|
+
import useXChat from "../index.js";
|
|
5
|
+
import { describe, it } from "../../node_modules/vitest/dist/@vitest/runner/chunk-artifact.js";
|
|
6
|
+
import { globalExpect, vi } from "../../node_modules/vitest/dist/chunks/test.CBQUpOM3.js";
|
|
7
|
+
import { computed, effectScope, nextTick, reactive, readonly, ref } from "vue";
|
|
8
|
+
//#region src/x-chat/__tests__/index.test.ts
|
|
9
|
+
var MockManualRequest = class extends AbstractXRequestClass {
|
|
10
|
+
_asyncHandler = Promise.resolve();
|
|
11
|
+
_state = reactive({
|
|
12
|
+
isTimeout: false,
|
|
13
|
+
isStreamTimeout: false,
|
|
14
|
+
isRequesting: false
|
|
15
|
+
});
|
|
16
|
+
constructor(shouldFail) {
|
|
17
|
+
super("https://mock.request", { manual: true });
|
|
18
|
+
this.shouldFail = shouldFail;
|
|
19
|
+
}
|
|
20
|
+
get asyncHandler() {
|
|
21
|
+
return this._asyncHandler;
|
|
22
|
+
}
|
|
23
|
+
get isTimeout() {
|
|
24
|
+
return this._state.isTimeout;
|
|
25
|
+
}
|
|
26
|
+
get isStreamTimeout() {
|
|
27
|
+
return this._state.isStreamTimeout;
|
|
28
|
+
}
|
|
29
|
+
get isRequesting() {
|
|
30
|
+
return this._state.isRequesting;
|
|
31
|
+
}
|
|
32
|
+
get manual() {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
get state() {
|
|
36
|
+
return readonly(this._state);
|
|
37
|
+
}
|
|
38
|
+
run(params) {
|
|
39
|
+
const callbacks = this.options.callbacks;
|
|
40
|
+
this._state.isRequesting = true;
|
|
41
|
+
this._asyncHandler = new Promise((resolve) => {
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
44
|
+
if (this.shouldFail.value) callbacks?.onError?.(/* @__PURE__ */ new Error("MockError"), { error: { message: "mock failed" } }, headers);
|
|
45
|
+
else {
|
|
46
|
+
const chunk = `echo:${params?.input || ""}`;
|
|
47
|
+
callbacks?.onUpdate?.(chunk, headers);
|
|
48
|
+
callbacks?.onSuccess?.([chunk], headers);
|
|
49
|
+
}
|
|
50
|
+
this._state.isRequesting = false;
|
|
51
|
+
resolve();
|
|
52
|
+
}, 0);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
abort() {
|
|
56
|
+
this._state.isRequesting = false;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var MockProvider = class extends AbstractChatProvider {
|
|
60
|
+
transformParams(requestParams) {
|
|
61
|
+
return { input: requestParams.input || "" };
|
|
62
|
+
}
|
|
63
|
+
transformLocalMessage(requestParams) {
|
|
64
|
+
return requestParams.input || "";
|
|
65
|
+
}
|
|
66
|
+
transformMessage(info) {
|
|
67
|
+
if (info.chunk) return info.chunk;
|
|
68
|
+
if (Array.isArray(info.chunks) && info.chunks.length > 0) return info.chunks[info.chunks.length - 1];
|
|
69
|
+
return info.originMessage || "";
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var DeferredManualRequest = class extends AbstractXRequestClass {
|
|
73
|
+
_state = reactive({
|
|
74
|
+
isTimeout: false,
|
|
75
|
+
isStreamTimeout: false,
|
|
76
|
+
isRequesting: false
|
|
77
|
+
});
|
|
78
|
+
callbacks;
|
|
79
|
+
params;
|
|
80
|
+
constructor() {
|
|
81
|
+
super("https://mock.request", { manual: true });
|
|
82
|
+
}
|
|
83
|
+
get asyncHandler() {
|
|
84
|
+
return Promise.resolve();
|
|
85
|
+
}
|
|
86
|
+
get isTimeout() {
|
|
87
|
+
return this._state.isTimeout;
|
|
88
|
+
}
|
|
89
|
+
get isStreamTimeout() {
|
|
90
|
+
return this._state.isStreamTimeout;
|
|
91
|
+
}
|
|
92
|
+
get isRequesting() {
|
|
93
|
+
return this._state.isRequesting;
|
|
94
|
+
}
|
|
95
|
+
get manual() {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
get state() {
|
|
99
|
+
return readonly(this._state);
|
|
100
|
+
}
|
|
101
|
+
run(params) {
|
|
102
|
+
this.params = params;
|
|
103
|
+
this.callbacks = this.options.callbacks;
|
|
104
|
+
this._state.isRequesting = true;
|
|
105
|
+
}
|
|
106
|
+
resolve() {
|
|
107
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
108
|
+
const chunk = `echo:${this.params?.input || ""}`;
|
|
109
|
+
this.callbacks?.onUpdate?.(chunk, headers);
|
|
110
|
+
this.callbacks?.onSuccess?.([chunk], headers);
|
|
111
|
+
this._state.isRequesting = false;
|
|
112
|
+
}
|
|
113
|
+
abort() {
|
|
114
|
+
this._state.isRequesting = false;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
describe("useXChat", () => {
|
|
118
|
+
it("uses reactive requestPlaceholder and requestFallback refs", async () => {
|
|
119
|
+
vi.useFakeTimers();
|
|
120
|
+
const shouldFail = ref(false);
|
|
121
|
+
const request = new MockManualRequest(shouldFail);
|
|
122
|
+
const provider = new MockProvider({ request });
|
|
123
|
+
const requestPlaceholder = ref("Thinking #1");
|
|
124
|
+
const requestFallback = ref("Fallback #1");
|
|
125
|
+
const scope = effectScope();
|
|
126
|
+
const chat = scope.run(() => useXChat({
|
|
127
|
+
provider,
|
|
128
|
+
requestPlaceholder,
|
|
129
|
+
requestFallback
|
|
130
|
+
}));
|
|
131
|
+
globalExpect(chat).toBeTruthy();
|
|
132
|
+
if (!chat) return;
|
|
133
|
+
chat.onRequest({ input: "hello" });
|
|
134
|
+
globalExpect(chat.messages.value.at(-1)?.status).toBe("loading");
|
|
135
|
+
globalExpect(chat.messages.value.at(-1)?.message).toBe("Thinking #1");
|
|
136
|
+
await vi.runAllTimersAsync();
|
|
137
|
+
await request.asyncHandler;
|
|
138
|
+
requestPlaceholder.value = "Thinking #2";
|
|
139
|
+
chat.onRequest({ input: "again" });
|
|
140
|
+
globalExpect(chat.messages.value.at(-1)?.status).toBe("loading");
|
|
141
|
+
globalExpect(chat.messages.value.at(-1)?.message).toBe("Thinking #2");
|
|
142
|
+
await vi.runAllTimersAsync();
|
|
143
|
+
await request.asyncHandler;
|
|
144
|
+
shouldFail.value = true;
|
|
145
|
+
chat.onRequest({ input: "error" });
|
|
146
|
+
await vi.runAllTimersAsync();
|
|
147
|
+
await request.asyncHandler;
|
|
148
|
+
globalExpect(chat.messages.value.at(-1)?.status).toBe("error");
|
|
149
|
+
globalExpect(chat.messages.value.at(-1)?.message).toBe("Fallback #1");
|
|
150
|
+
requestFallback.value = "Fallback #2";
|
|
151
|
+
chat.onRequest({ input: "error-again" });
|
|
152
|
+
await vi.runAllTimersAsync();
|
|
153
|
+
await request.asyncHandler;
|
|
154
|
+
globalExpect(chat.messages.value.at(-1)?.status).toBe("error");
|
|
155
|
+
globalExpect(chat.messages.value.at(-1)?.message).toBe("Fallback #2");
|
|
156
|
+
scope.stop();
|
|
157
|
+
vi.useRealTimers();
|
|
158
|
+
});
|
|
159
|
+
it("refreshes default messages when defaultMessages ref changes", async () => {
|
|
160
|
+
const defaultMessages = ref([{ message: "history-1" }]);
|
|
161
|
+
const scope = effectScope();
|
|
162
|
+
const chat = scope.run(() => useXChat({ defaultMessages }));
|
|
163
|
+
globalExpect(chat).toBeTruthy();
|
|
164
|
+
if (!chat) return;
|
|
165
|
+
await Promise.resolve();
|
|
166
|
+
await nextTick();
|
|
167
|
+
globalExpect(chat.messages.value.map((item) => item.message)).toEqual(["history-1"]);
|
|
168
|
+
defaultMessages.value = [{ message: "history-2" }];
|
|
169
|
+
await nextTick();
|
|
170
|
+
await Promise.resolve();
|
|
171
|
+
globalExpect(chat.messages.value.map((item) => item.message)).toEqual(["history-2"]);
|
|
172
|
+
scope.stop();
|
|
173
|
+
});
|
|
174
|
+
it("accepts computed provider refs", async () => {
|
|
175
|
+
vi.useFakeTimers();
|
|
176
|
+
const shouldFail = ref(false);
|
|
177
|
+
const provider = computed(() => new MockProvider({ request: new MockManualRequest(shouldFail) }));
|
|
178
|
+
const scope = effectScope();
|
|
179
|
+
const chat = scope.run(() => useXChat({ provider }));
|
|
180
|
+
globalExpect(chat).toBeTruthy();
|
|
181
|
+
if (!chat) return;
|
|
182
|
+
chat.onRequest({ input: "provider" });
|
|
183
|
+
await vi.runAllTimersAsync();
|
|
184
|
+
globalExpect(chat.messages.value.at(-1)?.status).toBe("success");
|
|
185
|
+
scope.stop();
|
|
186
|
+
vi.useRealTimers();
|
|
187
|
+
});
|
|
188
|
+
it("derives parsedMessages from parser output", async () => {
|
|
189
|
+
vi.useFakeTimers();
|
|
190
|
+
const scope = effectScope();
|
|
191
|
+
const chat = scope.run(() => useXChat({ parser: (message) => [message.toUpperCase(), `${message}!`] }));
|
|
192
|
+
globalExpect(chat).toBeTruthy();
|
|
193
|
+
if (!chat) return;
|
|
194
|
+
await Promise.resolve();
|
|
195
|
+
await nextTick();
|
|
196
|
+
chat.setMessages([{
|
|
197
|
+
id: "msg_1",
|
|
198
|
+
message: "hello",
|
|
199
|
+
status: "success"
|
|
200
|
+
}]);
|
|
201
|
+
await vi.advanceTimersByTimeAsync(60);
|
|
202
|
+
await nextTick();
|
|
203
|
+
globalExpect(chat.parsedMessages.value).toEqual([{
|
|
204
|
+
id: "msg_1_0",
|
|
205
|
+
message: "HELLO",
|
|
206
|
+
status: "success"
|
|
207
|
+
}, {
|
|
208
|
+
id: "msg_1_1",
|
|
209
|
+
message: "hello!",
|
|
210
|
+
status: "success"
|
|
211
|
+
}]);
|
|
212
|
+
scope.stop();
|
|
213
|
+
vi.useRealTimers();
|
|
214
|
+
});
|
|
215
|
+
it("keeps streaming updates isolated to the request conversation after switching", async () => {
|
|
216
|
+
const activeConversationKey = ref("a");
|
|
217
|
+
const requestA = new DeferredManualRequest();
|
|
218
|
+
const requestB = new DeferredManualRequest();
|
|
219
|
+
const providers = {
|
|
220
|
+
a: new MockProvider({ request: requestA }),
|
|
221
|
+
b: new MockProvider({ request: requestB })
|
|
222
|
+
};
|
|
223
|
+
const scope = effectScope();
|
|
224
|
+
const chat = scope.run(() => useXChat({
|
|
225
|
+
provider: computed(() => providers[activeConversationKey.value]),
|
|
226
|
+
conversationKey: activeConversationKey
|
|
227
|
+
}));
|
|
228
|
+
globalExpect(chat).toBeTruthy();
|
|
229
|
+
if (!chat) return;
|
|
230
|
+
await Promise.resolve();
|
|
231
|
+
await nextTick();
|
|
232
|
+
chat.onRequest({ input: "from-a" });
|
|
233
|
+
globalExpect(chat.messages.value.map((item) => item.message)).toEqual(["from-a"]);
|
|
234
|
+
activeConversationKey.value = "b";
|
|
235
|
+
await nextTick();
|
|
236
|
+
globalExpect(chat.messages.value).toEqual([]);
|
|
237
|
+
requestA.resolve();
|
|
238
|
+
await nextTick();
|
|
239
|
+
globalExpect(chat.messages.value).toEqual([]);
|
|
240
|
+
globalExpect(chatMessagesStoreHelper.getMessages("a")?.map((item) => ({
|
|
241
|
+
message: item.message,
|
|
242
|
+
status: item.status
|
|
243
|
+
}))).toEqual([{
|
|
244
|
+
message: "from-a",
|
|
245
|
+
status: "local"
|
|
246
|
+
}, {
|
|
247
|
+
message: "echo:from-a",
|
|
248
|
+
status: "success"
|
|
249
|
+
}]);
|
|
250
|
+
globalExpect(chatMessagesStoreHelper.getMessages("b")).toEqual([]);
|
|
251
|
+
activeConversationKey.value = "a";
|
|
252
|
+
await nextTick();
|
|
253
|
+
globalExpect(chat.messages.value.map((item) => item.message)).toEqual(["from-a", "echo:from-a"]);
|
|
254
|
+
scope.stop();
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
//#endregion
|
package/dist/x-chat/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MaybeRef } from 'vue';
|
|
2
2
|
import { AnyObject } from '../_util/types';
|
|
3
3
|
import { ConversationData } from '../x-conversations';
|
|
4
4
|
import { SSEOutput } from '../x-stream';
|
|
@@ -26,18 +26,19 @@ type RequestFallbackFn<Input, MessageInfoType, Message> = (requestParams: Partia
|
|
|
26
26
|
export type RequestParams<Message> = {
|
|
27
27
|
[Key: PropertyKey]: Message;
|
|
28
28
|
} & AnyObject;
|
|
29
|
-
type
|
|
29
|
+
type ParserFn<ChatMessage, BubbleMessage> = (message: ChatMessage) => BubbleMessage | BubbleMessage[];
|
|
30
|
+
type DefaultMessagesFn<ChatMessage extends SimpleType> = ((info: {
|
|
31
|
+
conversationKey?: ConversationData["key"];
|
|
32
|
+
}) => Promise<DefaultMessageInfo<ChatMessage>[]>) | ((info?: {
|
|
33
|
+
conversationKey?: ConversationData["key"];
|
|
34
|
+
}) => DefaultMessageInfo<ChatMessage>[]);
|
|
30
35
|
export interface XChatConfig<ChatMessage extends SimpleType = string, BubbleMessage extends SimpleType = ChatMessage, Input = ChatMessage, Output = ChatMessage> {
|
|
31
|
-
provider?: AbstractChatProvider<ChatMessage, Input, Output
|
|
36
|
+
provider?: MaybeRef<AbstractChatProvider<ChatMessage, Input, Output>>;
|
|
32
37
|
conversationKey?: MaybeRef<ConversationData["key"] | undefined>;
|
|
33
|
-
defaultMessages?: DefaultMessageInfo<ChatMessage>[] |
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}) => DefaultMessageInfo<ChatMessage>[]);
|
|
38
|
-
parser?: (message: ChatMessage) => BubbleMessage | BubbleMessage[];
|
|
39
|
-
requestPlaceholder?: ChatMessage | RequestPlaceholderFn<Input, ChatMessage>;
|
|
40
|
-
requestFallback?: ChatMessage | RequestFallbackFn<Input, MessageInfo<ChatMessage>, ChatMessage>;
|
|
38
|
+
defaultMessages?: MaybeRef<DefaultMessageInfo<ChatMessage>[]> | DefaultMessagesFn<ChatMessage>;
|
|
39
|
+
parser?: ParserFn<ChatMessage, BubbleMessage> | undefined;
|
|
40
|
+
requestPlaceholder?: MaybeRef<ChatMessage> | RequestPlaceholderFn<Input, ChatMessage>;
|
|
41
|
+
requestFallback?: MaybeRef<ChatMessage> | RequestFallbackFn<Input, MessageInfo<ChatMessage>, ChatMessage>;
|
|
41
42
|
}
|
|
42
43
|
export interface MessageInfo<Message extends SimpleType> {
|
|
43
44
|
id: number | string;
|
|
@@ -58,18 +59,8 @@ export default function useXChat<ChatMessage extends SimpleType = string, Parsed
|
|
|
58
59
|
readonly onRequest: (requestParams: Partial<Input>, opts?: {
|
|
59
60
|
extraInfo: AnyObject;
|
|
60
61
|
}) => void;
|
|
61
|
-
readonly isDefaultMessagesRequesting: Ref<boolean, boolean
|
|
62
|
-
readonly messages:
|
|
63
|
-
id: number | string;
|
|
64
|
-
message: import('vue').UnwrapRef<ChatMessage>;
|
|
65
|
-
status: MessageStatus;
|
|
66
|
-
extraInfo?: AnyObject | undefined;
|
|
67
|
-
}[], MessageInfo<ChatMessage>[] | {
|
|
68
|
-
id: number | string;
|
|
69
|
-
message: import('vue').UnwrapRef<ChatMessage>;
|
|
70
|
-
status: MessageStatus;
|
|
71
|
-
extraInfo?: AnyObject | undefined;
|
|
72
|
-
}[]>;
|
|
62
|
+
readonly isDefaultMessagesRequesting: Readonly<import('vue').Ref<boolean, boolean>>;
|
|
63
|
+
readonly messages: import('vue').ShallowRef<MessageInfo<ChatMessage>[], MessageInfo<ChatMessage>[]>;
|
|
73
64
|
readonly parsedMessages: import('vue').ComputedRef<MessageInfo<ParsedMessage>[]>;
|
|
74
65
|
readonly setMessages: (updater: MessageInfo<ChatMessage>[] | ((ori: MessageInfo<ChatMessage>[]) => MessageInfo<ChatMessage>[])) => boolean;
|
|
75
66
|
readonly removeMessage: (id: string | number) => boolean;
|
package/dist/x-chat/index.js
CHANGED
|
@@ -1,28 +1,49 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import resolveMaybeRef from "../_util/resolveMaybeRef.js";
|
|
2
|
+
import "../chat-providers/index.js";
|
|
3
|
+
import "../x-request/index.js";
|
|
4
|
+
import { ChatMessagesStore, chatMessagesStoreHelper } from "./store.js";
|
|
5
|
+
import { computed, isRef, onScopeDispose, reactive, readonly, ref, shallowRef, watch } from "vue";
|
|
3
6
|
//#region src/x-chat/index.ts
|
|
4
7
|
function toArray(item) {
|
|
5
8
|
return Array.isArray(item) ? item : [item];
|
|
6
9
|
}
|
|
7
|
-
function resolveMaybeRef(value) {
|
|
8
|
-
return isRef(value) ? value.value : value;
|
|
9
|
-
}
|
|
10
10
|
var IsRequestingMap = reactive(/* @__PURE__ */ new Map());
|
|
11
11
|
var generateConversationKey = () => Symbol("ConversationKey");
|
|
12
12
|
function useXChat(config) {
|
|
13
|
-
const { defaultMessages, requestFallback, requestPlaceholder, parser, provider } = config;
|
|
14
13
|
const idRef = ref(0);
|
|
15
14
|
const requestHandlerRef = shallowRef();
|
|
15
|
+
const providerRef = isRef(config.provider) ? config.provider : shallowRef(config.provider);
|
|
16
|
+
let provider = providerRef.value;
|
|
16
17
|
const conversationKey = ref((config.conversationKey ? resolveMaybeRef(config.conversationKey) : void 0) ?? generateConversationKey());
|
|
17
18
|
if (config.conversationKey && isRef(config.conversationKey)) watch(config.conversationKey, (value) => {
|
|
18
19
|
if (value !== void 0 && value !== null) conversationKey.value = value;
|
|
19
20
|
});
|
|
20
21
|
const messageQueueMap = /* @__PURE__ */ new Map();
|
|
21
22
|
const activeStore = shallowRef();
|
|
22
|
-
const messages =
|
|
23
|
-
const
|
|
23
|
+
const messages = shallowRef([]);
|
|
24
|
+
const isDefaultMessagesRequestingRef = shallowRef(false);
|
|
25
|
+
const parsedMessages = computed(() => {
|
|
26
|
+
const parser = config.parser;
|
|
27
|
+
const list = [];
|
|
28
|
+
messages.value.forEach((agentMsg) => {
|
|
29
|
+
const sourceMessage = agentMsg.message;
|
|
30
|
+
const bubbleMsgs = toArray(parser ? parser(sourceMessage) : sourceMessage);
|
|
31
|
+
bubbleMsgs.forEach((bubbleMsg, bubbleMsgIndex) => {
|
|
32
|
+
let key = agentMsg.id;
|
|
33
|
+
if (bubbleMsgs.length > 1) key = `${key}_${bubbleMsgIndex}`;
|
|
34
|
+
list.push({
|
|
35
|
+
id: key,
|
|
36
|
+
message: bubbleMsg,
|
|
37
|
+
status: agentMsg.status
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
return list;
|
|
42
|
+
});
|
|
24
43
|
let unsubscribeStore = null;
|
|
25
44
|
const resolveDefaultMessages = async (currentConversationKey) => {
|
|
45
|
+
const sourceDefaultMessages = config.defaultMessages;
|
|
46
|
+
const defaultMessages = typeof sourceDefaultMessages === "function" ? sourceDefaultMessages : resolveMaybeRef(sourceDefaultMessages);
|
|
26
47
|
return ((typeof defaultMessages === "function" ? await defaultMessages({ conversationKey: currentConversationKey }) : defaultMessages) || []).map((info, index) => ({
|
|
27
48
|
id: `default_${index}`,
|
|
28
49
|
status: "local",
|
|
@@ -37,12 +58,12 @@ function useXChat(config) {
|
|
|
37
58
|
const syncStoreSnapshot = () => {
|
|
38
59
|
if (!activeStore.value) {
|
|
39
60
|
messages.value = [];
|
|
40
|
-
|
|
61
|
+
isDefaultMessagesRequestingRef.value = false;
|
|
41
62
|
return;
|
|
42
63
|
}
|
|
43
64
|
const snapshot = activeStore.value.getSnapshot();
|
|
44
65
|
messages.value = snapshot.messages;
|
|
45
|
-
|
|
66
|
+
isDefaultMessagesRequestingRef.value = snapshot.isDefaultMessagesRequesting;
|
|
46
67
|
};
|
|
47
68
|
const bindStore = (currentConversationKey) => {
|
|
48
69
|
if (unsubscribeStore) {
|
|
@@ -85,38 +106,32 @@ function useXChat(config) {
|
|
|
85
106
|
idRef.value += 1;
|
|
86
107
|
return msg;
|
|
87
108
|
};
|
|
88
|
-
const parsedMessages = computed(() => {
|
|
89
|
-
const list = [];
|
|
90
|
-
messages.value.forEach((agentMsg) => {
|
|
91
|
-
const sourceMessage = agentMsg.message;
|
|
92
|
-
const bubbleMsgs = toArray(parser ? parser(sourceMessage) : sourceMessage);
|
|
93
|
-
bubbleMsgs.forEach((bubbleMsg, bubbleMsgIndex) => {
|
|
94
|
-
let key = agentMsg.id;
|
|
95
|
-
if (bubbleMsgs.length > 1) key = `${key}_${bubbleMsgIndex}`;
|
|
96
|
-
list.push({
|
|
97
|
-
id: key,
|
|
98
|
-
message: bubbleMsg,
|
|
99
|
-
status: agentMsg.status
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
return list;
|
|
104
|
-
});
|
|
105
109
|
const getFilteredMessages = (msgs) => msgs.filter((info) => info.status !== "loading").map((info) => info.message);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
const setupProvider = (nextProvider) => {
|
|
111
|
+
provider = nextProvider;
|
|
112
|
+
if (nextProvider) {
|
|
113
|
+
nextProvider.injectGetMessages(() => {
|
|
114
|
+
return getFilteredMessages(getMessages());
|
|
115
|
+
});
|
|
116
|
+
requestHandlerRef.value = nextProvider.request;
|
|
117
|
+
} else requestHandlerRef.value = void 0;
|
|
118
|
+
};
|
|
119
|
+
setupProvider(provider);
|
|
120
|
+
watch(providerRef, setupProvider);
|
|
111
121
|
const innerOnRequest = (requestParams, opts) => {
|
|
112
122
|
if (!provider) return;
|
|
123
|
+
const activeProvider = provider;
|
|
124
|
+
const requestKey = conversationKey.value;
|
|
125
|
+
const requestStore = createStore(requestKey);
|
|
113
126
|
const { updatingId, reload } = opts || {};
|
|
114
127
|
let loadingMsgId = null;
|
|
115
|
-
const
|
|
128
|
+
const sourceRequestPlaceholder = config.requestPlaceholder;
|
|
129
|
+
const requestPlaceholder = typeof sourceRequestPlaceholder === "function" ? sourceRequestPlaceholder : resolveMaybeRef(sourceRequestPlaceholder);
|
|
130
|
+
const localMessage = activeProvider.transformLocalMessage(requestParams);
|
|
116
131
|
const localMessages = (Array.isArray(localMessage) ? localMessage : [localMessage]).map((message) => createMessage(message, "local", opts?.extraInfo));
|
|
117
132
|
if (reload) {
|
|
118
133
|
loadingMsgId = updatingId;
|
|
119
|
-
setMessages((ori) => {
|
|
134
|
+
requestStore.setMessages((ori) => {
|
|
120
135
|
const nextMessages = [...ori];
|
|
121
136
|
if (requestPlaceholder) {
|
|
122
137
|
let placeholderMsg;
|
|
@@ -132,7 +147,7 @@ function useXChat(config) {
|
|
|
132
147
|
}
|
|
133
148
|
return nextMessages;
|
|
134
149
|
});
|
|
135
|
-
} else setMessages((ori) => {
|
|
150
|
+
} else requestStore.setMessages((ori) => {
|
|
136
151
|
let nextMessages = [...ori, ...localMessages];
|
|
137
152
|
if (requestPlaceholder) {
|
|
138
153
|
let placeholderMsg;
|
|
@@ -146,38 +161,38 @@ function useXChat(config) {
|
|
|
146
161
|
});
|
|
147
162
|
let updatingMsgId = null;
|
|
148
163
|
const updateMessage = (status, chunk, chunks, responseHeaders) => {
|
|
149
|
-
let msg = getMessages().find((info) => info.id === updatingMsgId);
|
|
164
|
+
let msg = requestStore.getMessages().find((info) => info.id === updatingMsgId);
|
|
150
165
|
if (!msg) if (reload && updatingId) {
|
|
151
|
-
msg = getMessages().find((info) => info.id === updatingId);
|
|
166
|
+
msg = requestStore.getMessages().find((info) => info.id === updatingId);
|
|
152
167
|
if (msg) {
|
|
153
168
|
msg.status = status;
|
|
154
|
-
msg.message =
|
|
169
|
+
msg.message = activeProvider.transformMessage({
|
|
155
170
|
chunk,
|
|
156
171
|
status,
|
|
157
172
|
chunks,
|
|
158
173
|
responseHeaders
|
|
159
174
|
});
|
|
160
|
-
setMessages((ori) => {
|
|
175
|
+
requestStore.setMessages((ori) => {
|
|
161
176
|
return [...ori];
|
|
162
177
|
});
|
|
163
178
|
updatingMsgId = msg.id;
|
|
164
179
|
}
|
|
165
180
|
} else {
|
|
166
|
-
msg = createMessage(
|
|
181
|
+
msg = createMessage(activeProvider.transformMessage({
|
|
167
182
|
chunk,
|
|
168
183
|
status,
|
|
169
184
|
chunks,
|
|
170
185
|
responseHeaders
|
|
171
186
|
}), status);
|
|
172
|
-
setMessages((ori) => {
|
|
187
|
+
requestStore.setMessages((ori) => {
|
|
173
188
|
return [...ori.filter((info) => info.id !== loadingMsgId), msg];
|
|
174
189
|
});
|
|
175
190
|
updatingMsgId = msg.id;
|
|
176
191
|
}
|
|
177
|
-
else setMessages((ori) => {
|
|
192
|
+
else requestStore.setMessages((ori) => {
|
|
178
193
|
return ori.map((info) => {
|
|
179
194
|
if (info.id === updatingMsgId) {
|
|
180
|
-
const transformData =
|
|
195
|
+
const transformData = activeProvider.transformMessage({
|
|
181
196
|
originMessage: info.message,
|
|
182
197
|
chunk,
|
|
183
198
|
chunks,
|
|
@@ -193,34 +208,36 @@ function useXChat(config) {
|
|
|
193
208
|
return info;
|
|
194
209
|
});
|
|
195
210
|
});
|
|
196
|
-
msg = getMessages().find((info) => info.id === updatingMsgId) || msg;
|
|
211
|
+
msg = requestStore.getMessages().find((info) => info.id === updatingMsgId) || msg;
|
|
197
212
|
return msg;
|
|
198
213
|
};
|
|
199
|
-
|
|
214
|
+
activeProvider.injectRequest({
|
|
200
215
|
onUpdate: (chunk, headers) => {
|
|
201
216
|
return updateMessage("updating", chunk, [], headers);
|
|
202
217
|
},
|
|
203
218
|
onSuccess: (chunks, headers) => {
|
|
204
|
-
if (
|
|
219
|
+
if (requestKey) IsRequestingMap.delete(requestKey);
|
|
205
220
|
return updateMessage("success", void 0, chunks, headers);
|
|
206
221
|
},
|
|
207
222
|
onError: async (error, errorInfo) => {
|
|
208
|
-
if (
|
|
223
|
+
if (requestKey) IsRequestingMap.delete(requestKey);
|
|
209
224
|
let fallbackMsg;
|
|
225
|
+
const sourceRequestFallback = config.requestFallback;
|
|
226
|
+
const requestFallback = typeof sourceRequestFallback === "function" ? sourceRequestFallback : resolveMaybeRef(sourceRequestFallback);
|
|
210
227
|
if (requestFallback) {
|
|
211
228
|
if (typeof requestFallback === "function") {
|
|
212
|
-
const currentMessages =
|
|
229
|
+
const currentMessages = getFilteredMessages(requestStore.getMessages());
|
|
213
230
|
fallbackMsg = await requestFallback(requestParams, {
|
|
214
231
|
error,
|
|
215
232
|
errorInfo,
|
|
216
|
-
messageInfo: getMessages().find((info) => info.id === loadingMsgId || info.id === updatingMsgId),
|
|
233
|
+
messageInfo: requestStore.getMessages().find((info) => info.id === loadingMsgId || info.id === updatingMsgId),
|
|
217
234
|
messages: currentMessages
|
|
218
235
|
});
|
|
219
236
|
} else fallbackMsg = requestFallback;
|
|
220
|
-
setMessages((ori) => [...ori.filter((info) => info.id !== loadingMsgId && info.id !== updatingMsgId), createMessage(fallbackMsg, error.name === "AbortError" ? "abort" : "error")]);
|
|
237
|
+
requestStore.setMessages((ori) => [...ori.filter((info) => info.id !== loadingMsgId && info.id !== updatingMsgId), createMessage(fallbackMsg, error.name === "AbortError" ? "abort" : "error")]);
|
|
221
238
|
} else {
|
|
222
|
-
fallbackMsg = getMessages().find((info) => info.id !== loadingMsgId && info.id !== updatingMsgId);
|
|
223
|
-
setMessages((ori) => {
|
|
239
|
+
fallbackMsg = requestStore.getMessages().find((info) => info.id !== loadingMsgId && info.id !== updatingMsgId)?.message;
|
|
240
|
+
requestStore.setMessages((ori) => {
|
|
224
241
|
return ori.map((info) => {
|
|
225
242
|
if (info.id === loadingMsgId || info.id === updatingMsgId) return {
|
|
226
243
|
...info,
|
|
@@ -233,15 +250,15 @@ function useXChat(config) {
|
|
|
233
250
|
return fallbackMsg;
|
|
234
251
|
}
|
|
235
252
|
});
|
|
236
|
-
if (
|
|
237
|
-
|
|
253
|
+
if (requestKey) IsRequestingMap.set(requestKey, true);
|
|
254
|
+
activeProvider.request.run(activeProvider.transformParams(requestParams, activeProvider.request.options));
|
|
238
255
|
};
|
|
239
256
|
const onRequest = (requestParams, opts) => {
|
|
240
|
-
if (!
|
|
257
|
+
if (!providerRef.value) throw new Error("provider is required");
|
|
241
258
|
innerOnRequest(requestParams, opts);
|
|
242
259
|
};
|
|
243
260
|
const onReload = (id, requestParams, opts) => {
|
|
244
|
-
if (!
|
|
261
|
+
if (!providerRef.value) throw new Error("provider is required");
|
|
245
262
|
if (!id || !getMessages().find((info) => info.id === id)) throw new Error(`message [${id}] is not found`);
|
|
246
263
|
innerOnRequest(requestParams, {
|
|
247
264
|
updatingId: id,
|
|
@@ -251,19 +268,19 @@ function useXChat(config) {
|
|
|
251
268
|
};
|
|
252
269
|
const processMessageQueue = () => {
|
|
253
270
|
const requestParamsList = messageQueueMap.get(conversationKey.value);
|
|
254
|
-
if (requestParamsList && requestParamsList.length > 0) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
requestParamsList.forEach(({ requestParams, opts }) => {
|
|
258
|
-
onRequest(requestParams, opts);
|
|
259
|
-
});
|
|
260
|
-
messageQueueMap.delete(conversationKey.value);
|
|
271
|
+
if (requestParamsList && requestParamsList.length > 0) setTimeout(() => {
|
|
272
|
+
requestParamsList.forEach(({ requestParams, opts }) => {
|
|
273
|
+
onRequest(requestParams, opts);
|
|
261
274
|
});
|
|
262
|
-
|
|
275
|
+
messageQueueMap.delete(conversationKey.value);
|
|
276
|
+
});
|
|
263
277
|
};
|
|
264
|
-
watch(
|
|
278
|
+
watch(isDefaultMessagesRequestingRef, (requesting) => {
|
|
265
279
|
if (!requesting) processMessageQueue();
|
|
266
280
|
});
|
|
281
|
+
if (config.defaultMessages && isRef(config.defaultMessages)) watch(config.defaultMessages, () => {
|
|
282
|
+
activeStore.value?.refreshDefaultMessages();
|
|
283
|
+
}, { deep: true });
|
|
267
284
|
const queueRequest = (currentConversationKey, requestParams, opts) => {
|
|
268
285
|
if (!messageQueueMap.has(currentConversationKey)) messageQueueMap.set(currentConversationKey, []);
|
|
269
286
|
messageQueueMap.get(currentConversationKey).push({
|
|
@@ -273,7 +290,7 @@ function useXChat(config) {
|
|
|
273
290
|
};
|
|
274
291
|
return {
|
|
275
292
|
onRequest,
|
|
276
|
-
isDefaultMessagesRequesting,
|
|
293
|
+
isDefaultMessagesRequesting: readonly(isDefaultMessagesRequestingRef),
|
|
277
294
|
messages,
|
|
278
295
|
parsedMessages,
|
|
279
296
|
setMessages,
|