@opentiny/next-sdk 0.0.1-alpha.2 → 0.0.1-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +18 -355
- package/dist/mcp-host/core/index.d.ts +2 -0
- package/dist/mcp-host/core/index.mjs +254 -0
- package/dist/mcp-host/deepseek/index.mjs +9 -0
- package/dist/mcp-host/index.mjs +8 -0
- package/dist/next-client/index.d.ts +1708 -0
- package/dist/next-client/index.mjs +61 -0
- package/dist/next-server/index.d.ts +1098 -0
- package/dist/next-server/index.mjs +45 -0
- package/dist/plugins/connectMcpServer.d.ts +1 -1
- package/dist/plugins/connectMcpServer.mjs +11 -0
- package/dist/plugins/createInMemoryTransport.d.ts +2 -2
- package/dist/plugins/createInMemoryTransport.mjs +8 -0
- package/dist/plugins/createMessageChannelTransport.d.ts +2 -2
- package/dist/plugins/createMessageChannelTransport.mjs +7 -0
- package/dist/plugins/createProxy.d.ts +2 -2
- package/dist/plugins/createProxy.mjs +27 -0
- package/dist/type.mjs +4 -0
- package/dist/utils/dom.mjs +12 -0
- package/package.json +3 -1
- package/dist/client/index.d.ts +0 -1
- package/dist/index.umd.js +0 -7
- package/dist/server/index.d.ts +0 -1
package/dist/index.d.ts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,357 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import { MessageChannelClientTransport as K, MessageChannelServerTransport as B, createTransportPair as J, createStreamProxy as V, createSseProxy as W, streamOptions as X, sseOptions as z } from "@opentiny/next";
|
|
9
|
-
import { StreamableHTTPClientTransport as R } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
10
|
-
import { SSEClientTransport as A } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
11
|
-
import { InMemoryTransport as G } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
12
|
-
const S = {
|
|
13
|
-
toolListChanged: q,
|
|
14
|
-
resourceUpdated: D,
|
|
15
|
-
promptListChanged: P,
|
|
16
|
-
loggingMessage: O
|
|
17
|
-
}, Q = {
|
|
18
|
-
createMessage: y,
|
|
19
|
-
listRoots: w,
|
|
20
|
-
elicit: U
|
|
21
|
-
};
|
|
22
|
-
class L extends v {
|
|
23
|
-
constructor(t, o) {
|
|
24
|
-
super(t, o);
|
|
25
|
-
i(this, "nextTransport", null);
|
|
26
|
-
i(this, "isMessageChannel", !1);
|
|
27
|
-
}
|
|
28
|
-
// 注册插件
|
|
29
|
-
use(t) {
|
|
30
|
-
return t(this);
|
|
31
|
-
}
|
|
32
|
-
// 注册事件,包括notification以及requestHandle
|
|
33
|
-
on(t, o) {
|
|
34
|
-
t in S ? this.setNotificationHandler(S[t], o) : this.setRequestHandler(Q[t], o);
|
|
35
|
-
}
|
|
36
|
-
async connectTransport() {
|
|
37
|
-
return await this.connect(this.nextTransport);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const de = (s, e) => new L(s, e), Z = {
|
|
41
|
-
subscribe: j,
|
|
42
|
-
unsubscribe: $,
|
|
43
|
-
listResources: _,
|
|
44
|
-
createMessage: y,
|
|
45
|
-
listRoots: w,
|
|
46
|
-
setLevel: k,
|
|
47
|
-
ping: H
|
|
48
|
-
};
|
|
49
|
-
class ee extends Y {
|
|
50
|
-
constructor(t, o) {
|
|
51
|
-
super(t, o);
|
|
52
|
-
i(this, "nextTransport");
|
|
53
|
-
i(this, "isMessageChannel", !1);
|
|
54
|
-
}
|
|
55
|
-
use(t) {
|
|
56
|
-
return t(this);
|
|
57
|
-
}
|
|
58
|
-
async connectTransport() {
|
|
59
|
-
var t, o;
|
|
60
|
-
return this.isMessageChannel && await ((o = (t = this.nextTransport) == null ? void 0 : t.listen) == null ? void 0 : o.call(t)), await this.connect(this.nextTransport);
|
|
61
|
-
}
|
|
62
|
-
on(t, o) {
|
|
63
|
-
this.server.setRequestHandler(Z[t], o);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const Se = (s, e) => new ee(s, e);
|
|
67
|
-
var c = /* @__PURE__ */ ((s) => (s.FUNCTION = "function", s.USER = "user", s.ASSISTANT = "assistant", s.DEVELOPER = "developer", s.SYSTEM = "system", s.TOOL = "tool", s))(c || {});
|
|
68
|
-
const C = 3;
|
|
69
|
-
class te {
|
|
70
|
-
// 最大迭代次数
|
|
71
|
-
constructor({ llmOption: e, mcpClients: t }) {
|
|
72
|
-
i(this, "llmOption");
|
|
73
|
-
i(this, "mcpClients");
|
|
74
|
-
i(this, "llm");
|
|
75
|
-
i(this, "mcpClientMap", /* @__PURE__ */ new Map());
|
|
76
|
-
i(this, "messages", []);
|
|
77
|
-
i(this, "toolClientMap", /* @__PURE__ */ new Map());
|
|
78
|
-
i(this, "iteration", C);
|
|
79
|
-
this.llmOption = e, this.mcpClients = t, this.messages.push({ role: c.SYSTEM, content: "You are a helpful assistant with access to tools." });
|
|
80
|
-
}
|
|
81
|
-
async generateMcpClient() {
|
|
82
|
-
for (let e = 0; e < this.mcpClients.length; e++) {
|
|
83
|
-
const t = this.mcpClients[e], { tools: o } = await t.listTools();
|
|
84
|
-
this.mcpClientMap.set(t, o), o.forEach((a) => {
|
|
85
|
-
this.toolClientMap.set(a.name, t);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
init() {
|
|
90
|
-
this.llm = new F({
|
|
91
|
-
baseURL: this.llmOption.url,
|
|
92
|
-
apiKey: this.llmOption.apiKey,
|
|
93
|
-
dangerouslyAllowBrowser: !0
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
validateRequest(e) {
|
|
97
|
-
if (!e.messages || !Array.isArray(e.messages) || e.messages.length === 0)
|
|
98
|
-
throw new Error("请求必须包含至少一条消息");
|
|
99
|
-
for (const t of e.messages)
|
|
100
|
-
if (!t.role || !t.content)
|
|
101
|
-
throw new Error("每条消息必须包含角色和内容");
|
|
102
|
-
}
|
|
103
|
-
async getMcpTools() {
|
|
104
|
-
const e = [];
|
|
105
|
-
for (const [, o] of this.mcpClientMap.entries())
|
|
106
|
-
e.push(...o);
|
|
107
|
-
return e.map((o) => ({
|
|
108
|
-
type: "function",
|
|
109
|
-
function: {
|
|
110
|
-
name: o.name,
|
|
111
|
-
description: o.description,
|
|
112
|
-
parameters: o.inputSchema
|
|
113
|
-
}
|
|
114
|
-
}));
|
|
115
|
-
}
|
|
116
|
-
async doLLMChart() {
|
|
117
|
-
const e = await this.getMcpTools();
|
|
118
|
-
return await this.llm.chat.completions.create({
|
|
119
|
-
messages: this.messages,
|
|
120
|
-
model: this.llmOption.model,
|
|
121
|
-
tools: e,
|
|
122
|
-
stream: !0
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* 解析 LLM 回复,提取工具调用和最终回复内容
|
|
127
|
-
* @param response LLM 回复对象
|
|
128
|
-
* @returns [工具调用数组, 回复内容]
|
|
129
|
-
*/
|
|
130
|
-
async parseToolCalls(e, t) {
|
|
131
|
-
var h, p, f, n;
|
|
132
|
-
const o = /* @__PURE__ */ new Map();
|
|
133
|
-
let a = "", r = "";
|
|
134
|
-
for await (const T of e) {
|
|
135
|
-
const u = T.choices[0].delta, g = u == null ? void 0 : u.tool_calls, d = u == null ? void 0 : u.content;
|
|
136
|
-
if (d && (a += d, t.onData({
|
|
137
|
-
delta: {
|
|
138
|
-
role: "assistant",
|
|
139
|
-
content: d
|
|
140
|
-
}
|
|
141
|
-
})), g && g.length > 0) {
|
|
142
|
-
const l = g[0];
|
|
143
|
-
if (l.id) {
|
|
144
|
-
r = l.id;
|
|
145
|
-
const m = {
|
|
146
|
-
id: l.id,
|
|
147
|
-
type: l.type,
|
|
148
|
-
function: {
|
|
149
|
-
name: ((h = l.function) == null ? void 0 : h.name) || "",
|
|
150
|
-
arguments: ((p = l.function) == null ? void 0 : p.arguments) || ""
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
o.set(r, m), t.onData({
|
|
154
|
-
delta: {
|
|
155
|
-
role: c.ASSISTANT,
|
|
156
|
-
content: `
|
|
157
|
-
|
|
158
|
-
正在调用工具:${m.function.name}
|
|
159
|
-
|
|
160
|
-
参数:`
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
if (o.has(r)) {
|
|
165
|
-
const m = o.get(r);
|
|
166
|
-
m && (m.function.arguments += ((f = l.function) == null ? void 0 : f.arguments) || "", t.onData({
|
|
167
|
-
delta: {
|
|
168
|
-
role: c.ASSISTANT,
|
|
169
|
-
content: `${(n = l.function) == null ? void 0 : n.arguments}`
|
|
170
|
-
}
|
|
171
|
-
}));
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return t.onData({
|
|
176
|
-
delta: {
|
|
177
|
-
role: "assistant",
|
|
178
|
-
content: `
|
|
179
|
-
|
|
180
|
-
`
|
|
181
|
-
}
|
|
182
|
-
}), [Array.from(o.values()), a];
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* 执行工具调用
|
|
186
|
-
* @param toolCalls 工具调用请求列表
|
|
187
|
-
* @returns 工具调用结果和消息
|
|
188
|
-
*/
|
|
189
|
-
async callTools(e, t) {
|
|
190
|
-
var o, a, r, h;
|
|
191
|
-
try {
|
|
192
|
-
const p = [], f = [];
|
|
193
|
-
for (const n of e) {
|
|
194
|
-
const T = n.function.name, u = this.toolClientMap.get(T);
|
|
195
|
-
if (!u)
|
|
196
|
-
continue;
|
|
197
|
-
let g = {};
|
|
198
|
-
try {
|
|
199
|
-
g = typeof n.function.arguments == "string" ? JSON.parse(n.function.arguments) : n.function.arguments;
|
|
200
|
-
} catch (N) {
|
|
201
|
-
console.error(`Failed to parse tool arguments for ${T}:`, N), g = {};
|
|
202
|
-
}
|
|
203
|
-
const d = {
|
|
204
|
-
id: n.id,
|
|
205
|
-
type: n.type,
|
|
206
|
-
function: {
|
|
207
|
-
name: ((o = n.function) == null ? void 0 : o.name) || "",
|
|
208
|
-
arguments: ((a = n.function) == null ? void 0 : a.arguments) || ""
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
t.onData({
|
|
212
|
-
delta: {
|
|
213
|
-
role: c.TOOL,
|
|
214
|
-
toolCall: d
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
const l = await u.callTool({
|
|
218
|
-
name: T,
|
|
219
|
-
arguments: g
|
|
220
|
-
}), m = this.getToolCallMessage(l), I = {
|
|
221
|
-
role: c.TOOL,
|
|
222
|
-
tool_call_id: n.id,
|
|
223
|
-
content: m
|
|
224
|
-
}, E = {
|
|
225
|
-
id: n.id,
|
|
226
|
-
type: n.type,
|
|
227
|
-
callToolContent: m,
|
|
228
|
-
function: {
|
|
229
|
-
name: ((r = n.function) == null ? void 0 : r.name) || "",
|
|
230
|
-
arguments: ((h = n.function) == null ? void 0 : h.arguments) || ""
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
t.onData({
|
|
234
|
-
delta: {
|
|
235
|
-
role: c.TOOL,
|
|
236
|
-
toolCall: E
|
|
237
|
-
}
|
|
238
|
-
}), f.push(I), p.push({
|
|
239
|
-
call: T,
|
|
240
|
-
result: l
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
return { toolResults: p, toolCallMessages: f };
|
|
244
|
-
} catch (p) {
|
|
245
|
-
return console.error("Error calling tools:", p), { toolResults: [], toolCallMessages: [{ role: c.ASSISTANT, content: "call tools failed!" }] };
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* 解析工具调用结果,拼接为字符串
|
|
250
|
-
* @param toolCallResult 工具调用结果
|
|
251
|
-
* @returns 拼接后的字符串
|
|
252
|
-
*/
|
|
253
|
-
getToolCallMessage(e) {
|
|
254
|
-
var o;
|
|
255
|
-
let t = "";
|
|
256
|
-
return (o = e.content) != null && o.length && e.content.forEach((a) => {
|
|
257
|
-
switch (a.type) {
|
|
258
|
-
case "text":
|
|
259
|
-
t += a.text;
|
|
260
|
-
break;
|
|
261
|
-
case "image":
|
|
262
|
-
case "audio":
|
|
263
|
-
case "resource":
|
|
264
|
-
t += a.data;
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
}), t;
|
|
268
|
-
}
|
|
269
|
-
async chatStream(e, t) {
|
|
270
|
-
return await this.generateMcpClient(), typeof e == "string" ? this.messages.push({ role: c.USER, content: e }) : this.messages.push(e.messages[e.messages.length - 1]), this.iteration = C, await this.processSteamToolCallsAndResponses(t).catch((o) => {
|
|
271
|
-
console.error("Chat failed:", o);
|
|
272
|
-
}), "ok";
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* 聊天迭代主流程,支持多轮工具调用和最终回复
|
|
276
|
-
*/
|
|
277
|
-
async processSteamToolCallsAndResponses(e) {
|
|
278
|
-
try {
|
|
279
|
-
const t = [];
|
|
280
|
-
for (; this.iteration > 0; ) {
|
|
281
|
-
const o = await this.doLLMChart(), [a, r] = await this.parseToolCalls(o, e);
|
|
282
|
-
if (a.length) {
|
|
283
|
-
const h = {
|
|
284
|
-
role: c.ASSISTANT,
|
|
285
|
-
content: `调用工具:${a.map((n) => n.function.name).join(",")}`,
|
|
286
|
-
tool_calls: a.map((n) => ({
|
|
287
|
-
id: n.id,
|
|
288
|
-
type: n.type,
|
|
289
|
-
function: n.function
|
|
290
|
-
}))
|
|
291
|
-
};
|
|
292
|
-
this.messages.push(h);
|
|
293
|
-
const { toolResults: p, toolCallMessages: f } = await this.callTools(a, e);
|
|
294
|
-
t.push(...p), f.forEach((n) => this.messages.push(n)), this.iteration--;
|
|
295
|
-
} else
|
|
296
|
-
this.messages.push({ role: c.ASSISTANT, content: r }), this.iteration = 0;
|
|
297
|
-
}
|
|
298
|
-
e.onDone();
|
|
299
|
-
} catch (t) {
|
|
300
|
-
throw console.error("Chat iteration failed:", t), t;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
class se extends te {
|
|
305
|
-
constructor({ llmOption: e, mcpClients: t }) {
|
|
306
|
-
super({ llmOption: e, mcpClients: t });
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
const Ce = ({ llmOption: s, mcpClients: e }) => {
|
|
310
|
-
let t;
|
|
311
|
-
return s.llm === "deepseek" && (t = new se({ llmOption: s, mcpClients: e }), t.init()), t;
|
|
312
|
-
}, Me = (s) => (e) => {
|
|
313
|
-
e.nextTransport ? e.nextTransport = new K(s.endpoint, s.globalObject) : e.nextTransport = new B(s.endpoint, s.globalObject), e.isMessageChannel = !0;
|
|
314
|
-
}, oe = () => typeof window < "u", ne = (s, e) => {
|
|
315
|
-
const t = async () => {
|
|
316
|
-
e instanceof R ? await (e == null ? void 0 : e.terminateSession()) : e instanceof A && await (e == null ? void 0 : e.close()), s.close();
|
|
317
|
-
};
|
|
318
|
-
oe() && (window.addEventListener("beforeunload", t), window.addEventListener("pagehide", t));
|
|
319
|
-
}, [ae, M] = J(), we = () => (s) => (s.nextTransport = M, M), ye = (s) => async (e) => {
|
|
320
|
-
let t = "", o = null;
|
|
321
|
-
if (s.type === "stream") {
|
|
322
|
-
const { sessionId: a, transport: r } = await V({
|
|
323
|
-
client: e,
|
|
324
|
-
...s
|
|
325
|
-
});
|
|
326
|
-
t = a, o = r;
|
|
327
|
-
} else {
|
|
328
|
-
const { sessionId: a, transport: r } = await W({
|
|
329
|
-
client: e,
|
|
330
|
-
...s
|
|
331
|
-
});
|
|
332
|
-
t = a, o = r;
|
|
333
|
-
}
|
|
334
|
-
return e.nextTransport = ae, ne(e, o), {
|
|
335
|
-
sessionId: t,
|
|
336
|
-
transport: o
|
|
337
|
-
};
|
|
338
|
-
}, Re = (s) => (e) => {
|
|
339
|
-
const { type: t, url: o, token: a } = s;
|
|
340
|
-
let r = null;
|
|
341
|
-
t === "stream" ? r = new R(new URL(o), X(a)) : t === "sse" && (r = new A(new URL(o), z(a))), e.nextTransport = r;
|
|
342
|
-
}, [re, ie] = G.createLinkedPair(), Ae = () => (s) => {
|
|
343
|
-
s instanceof L ? s.nextTransport = re : s.nextTransport = ie;
|
|
344
|
-
};
|
|
1
|
+
import { NextClient as t, createClient as o } from "./next-client/index.mjs";
|
|
2
|
+
import { NextServer as n, createServer as p } from "./next-server/index.mjs";
|
|
3
|
+
import { createMCPHost as c } from "./mcp-host/index.mjs";
|
|
4
|
+
import { createMessageChannelTransport as f } from "./plugins/createMessageChannelTransport.mjs";
|
|
5
|
+
import { createClientProxy as v, createServerProxy as C, serverTransport as l } from "./plugins/createProxy.mjs";
|
|
6
|
+
import { connectMcpServer as S } from "./plugins/connectMcpServer.mjs";
|
|
7
|
+
import { createInMemoryTransport as y } from "./plugins/createInMemoryTransport.mjs";
|
|
345
8
|
export {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
9
|
+
t as NextClient,
|
|
10
|
+
n as NextServer,
|
|
11
|
+
S as connectMcpServer,
|
|
12
|
+
o as createClient,
|
|
13
|
+
v as createClientProxy,
|
|
14
|
+
y as createInMemoryTransport,
|
|
15
|
+
c as createMCPHost,
|
|
16
|
+
f as createMessageChannelTransport,
|
|
17
|
+
p as createServer,
|
|
18
|
+
C as createServerProxy,
|
|
19
|
+
l as serverTransport
|
|
357
20
|
};
|
|
@@ -9,6 +9,7 @@ export declare class MCPHost {
|
|
|
9
9
|
messages: any[];
|
|
10
10
|
protected toolClientMap: Map<string, Client>;
|
|
11
11
|
protected iteration: number;
|
|
12
|
+
protected resourcesMap: Map<string, any>;
|
|
12
13
|
constructor({ llmOption, mcpClients }: {
|
|
13
14
|
llmOption: any;
|
|
14
15
|
mcpClients: Client[];
|
|
@@ -50,6 +51,7 @@ export declare class MCPHost {
|
|
|
50
51
|
messages: Message[];
|
|
51
52
|
options: any;
|
|
52
53
|
}, handler: StreamHandler): Promise<string>;
|
|
54
|
+
clearMessages(): void;
|
|
53
55
|
/**
|
|
54
56
|
* 聊天迭代主流程,支持多轮工具调用和最终回复
|
|
55
57
|
*/
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
var S = Object.defineProperty;
|
|
2
|
+
var A = (g, t, o) => t in g ? S(g, t, { enumerable: !0, configurable: !0, writable: !0, value: o }) : g[t] = o;
|
|
3
|
+
var u = (g, t, o) => A(g, typeof t != "symbol" ? t + "" : t, o);
|
|
4
|
+
import O from "openai";
|
|
5
|
+
import { Role as r } from "../../type.mjs";
|
|
6
|
+
const M = 3;
|
|
7
|
+
class D {
|
|
8
|
+
// 缓存资源防止资源读取频繁触发
|
|
9
|
+
constructor({ llmOption: t, mcpClients: o }) {
|
|
10
|
+
u(this, "llmOption");
|
|
11
|
+
u(this, "mcpClients");
|
|
12
|
+
u(this, "llm");
|
|
13
|
+
u(this, "mcpClientMap", /* @__PURE__ */ new Map());
|
|
14
|
+
u(this, "messages", []);
|
|
15
|
+
u(this, "toolClientMap", /* @__PURE__ */ new Map());
|
|
16
|
+
u(this, "iteration", M);
|
|
17
|
+
// 最大迭代次数
|
|
18
|
+
u(this, "resourcesMap", /* @__PURE__ */ new Map());
|
|
19
|
+
this.llmOption = t, this.mcpClients = o, this.messages.push({ role: r.SYSTEM, content: "You are a helpful assistant with access to tools." });
|
|
20
|
+
}
|
|
21
|
+
async generateMcpClient() {
|
|
22
|
+
for (let t = 0; t < this.mcpClients.length; t++) {
|
|
23
|
+
const o = this.mcpClients[t], { tools: s } = await o.listTools();
|
|
24
|
+
this.mcpClientMap.set(o, s), s.forEach((a) => {
|
|
25
|
+
this.toolClientMap.set(a.name, o);
|
|
26
|
+
});
|
|
27
|
+
const { resources: n } = await o.listResources();
|
|
28
|
+
for (const a of n)
|
|
29
|
+
if (!this.resourcesMap.has(a.uri)) {
|
|
30
|
+
const c = await o.readResource({ uri: a.uri }), l = { ...a, content: c.contents };
|
|
31
|
+
this.resourcesMap.set(a.uri, l), this.messages.push({ role: r.SYSTEM, content: JSON.stringify(l) });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
init() {
|
|
36
|
+
this.llm = new O({
|
|
37
|
+
baseURL: this.llmOption.url,
|
|
38
|
+
apiKey: this.llmOption.apiKey,
|
|
39
|
+
dangerouslyAllowBrowser: !0
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
validateRequest(t) {
|
|
43
|
+
if (!t.messages || !Array.isArray(t.messages) || t.messages.length === 0)
|
|
44
|
+
throw new Error("请求必须包含至少一条消息");
|
|
45
|
+
for (const o of t.messages)
|
|
46
|
+
if (!o.role || !o.content)
|
|
47
|
+
throw new Error("每条消息必须包含角色和内容");
|
|
48
|
+
}
|
|
49
|
+
async getMcpTools() {
|
|
50
|
+
const t = [];
|
|
51
|
+
for (const [, s] of this.mcpClientMap.entries())
|
|
52
|
+
t.push(...s);
|
|
53
|
+
return t.map((s) => ({
|
|
54
|
+
type: "function",
|
|
55
|
+
function: {
|
|
56
|
+
name: s.name,
|
|
57
|
+
description: s.description,
|
|
58
|
+
parameters: s.inputSchema
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
async doLLMChart() {
|
|
63
|
+
const t = await this.getMcpTools(), o = {
|
|
64
|
+
messages: this.messages,
|
|
65
|
+
model: this.llmOption.model,
|
|
66
|
+
stream: !0
|
|
67
|
+
};
|
|
68
|
+
return t.length > 0 && (o.tools = t), await this.llm.chat.completions.create(o);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 解析 LLM 回复,提取工具调用和最终回复内容
|
|
72
|
+
* @param response LLM 回复对象
|
|
73
|
+
* @returns [工具调用数组, 回复内容]
|
|
74
|
+
*/
|
|
75
|
+
async parseToolCalls(t, o) {
|
|
76
|
+
var c, l, h, e;
|
|
77
|
+
const s = /* @__PURE__ */ new Map();
|
|
78
|
+
let n = "", a = "";
|
|
79
|
+
for await (const C of t) {
|
|
80
|
+
const p = C.choices[0].delta, f = p == null ? void 0 : p.tool_calls, T = p == null ? void 0 : p.content;
|
|
81
|
+
if (T && (n += T, o.onData({
|
|
82
|
+
delta: {
|
|
83
|
+
role: "assistant",
|
|
84
|
+
content: T
|
|
85
|
+
}
|
|
86
|
+
})), f && f.length > 0) {
|
|
87
|
+
const i = f[0];
|
|
88
|
+
if (i.id) {
|
|
89
|
+
a = i.id;
|
|
90
|
+
const m = {
|
|
91
|
+
id: i.id,
|
|
92
|
+
type: i.type,
|
|
93
|
+
function: {
|
|
94
|
+
name: ((c = i.function) == null ? void 0 : c.name) || "",
|
|
95
|
+
arguments: ((l = i.function) == null ? void 0 : l.arguments) || ""
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
s.set(a, m), o.onData({
|
|
99
|
+
delta: {
|
|
100
|
+
role: r.ASSISTANT,
|
|
101
|
+
content: `
|
|
102
|
+
|
|
103
|
+
正在调用工具:${m.function.name}
|
|
104
|
+
|
|
105
|
+
参数:`
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (s.has(a)) {
|
|
110
|
+
const m = s.get(a);
|
|
111
|
+
m && (m.function.arguments += ((h = i.function) == null ? void 0 : h.arguments) || "", o.onData({
|
|
112
|
+
delta: {
|
|
113
|
+
role: r.ASSISTANT,
|
|
114
|
+
content: `${(e = i.function) == null ? void 0 : e.arguments}`
|
|
115
|
+
}
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return o.onData({
|
|
121
|
+
delta: {
|
|
122
|
+
role: "assistant",
|
|
123
|
+
content: `
|
|
124
|
+
|
|
125
|
+
`
|
|
126
|
+
}
|
|
127
|
+
}), [Array.from(s.values()), n];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 执行工具调用
|
|
131
|
+
* @param toolCalls 工具调用请求列表
|
|
132
|
+
* @returns 工具调用结果和消息
|
|
133
|
+
*/
|
|
134
|
+
async callTools(t, o) {
|
|
135
|
+
var s, n, a, c;
|
|
136
|
+
try {
|
|
137
|
+
const l = [], h = [];
|
|
138
|
+
for (const e of t) {
|
|
139
|
+
const C = e.function.name, p = this.toolClientMap.get(C);
|
|
140
|
+
if (!p)
|
|
141
|
+
continue;
|
|
142
|
+
let f = {};
|
|
143
|
+
try {
|
|
144
|
+
f = typeof e.function.arguments == "string" ? JSON.parse(e.function.arguments) : e.function.arguments;
|
|
145
|
+
} catch (w) {
|
|
146
|
+
console.error(`Failed to parse tool arguments for ${C}:`, w), f = {};
|
|
147
|
+
}
|
|
148
|
+
const T = {
|
|
149
|
+
id: e.id,
|
|
150
|
+
type: e.type,
|
|
151
|
+
function: {
|
|
152
|
+
name: ((s = e.function) == null ? void 0 : s.name) || "",
|
|
153
|
+
arguments: ((n = e.function) == null ? void 0 : n.arguments) || ""
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
o.onData({
|
|
157
|
+
delta: {
|
|
158
|
+
role: r.TOOL,
|
|
159
|
+
toolCall: T
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
const i = await p.callTool({
|
|
163
|
+
name: C,
|
|
164
|
+
arguments: f
|
|
165
|
+
}), m = this.getToolCallMessage(i), y = {
|
|
166
|
+
role: r.TOOL,
|
|
167
|
+
tool_call_id: e.id,
|
|
168
|
+
content: m
|
|
169
|
+
}, d = {
|
|
170
|
+
id: e.id,
|
|
171
|
+
type: e.type,
|
|
172
|
+
callToolContent: m,
|
|
173
|
+
function: {
|
|
174
|
+
name: ((a = e.function) == null ? void 0 : a.name) || "",
|
|
175
|
+
arguments: ((c = e.function) == null ? void 0 : c.arguments) || ""
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
o.onData({
|
|
179
|
+
delta: {
|
|
180
|
+
role: r.TOOL,
|
|
181
|
+
toolCall: d
|
|
182
|
+
}
|
|
183
|
+
}), h.push(y), l.push({
|
|
184
|
+
call: C,
|
|
185
|
+
result: i
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return { toolResults: l, toolCallMessages: h };
|
|
189
|
+
} catch (l) {
|
|
190
|
+
return console.error("Error calling tools:", l), { toolResults: [], toolCallMessages: [{ role: r.ASSISTANT, content: "call tools failed!" }] };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 解析工具调用结果,拼接为字符串
|
|
195
|
+
* @param toolCallResult 工具调用结果
|
|
196
|
+
* @returns 拼接后的字符串
|
|
197
|
+
*/
|
|
198
|
+
getToolCallMessage(t) {
|
|
199
|
+
var s;
|
|
200
|
+
let o = "";
|
|
201
|
+
return (s = t.content) != null && s.length && t.content.forEach((n) => {
|
|
202
|
+
switch (n.type) {
|
|
203
|
+
case "text":
|
|
204
|
+
o += n.text;
|
|
205
|
+
break;
|
|
206
|
+
case "image":
|
|
207
|
+
case "audio":
|
|
208
|
+
case "resource":
|
|
209
|
+
o += n.data;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}), o;
|
|
213
|
+
}
|
|
214
|
+
async chatStream(t, o) {
|
|
215
|
+
return await this.generateMcpClient(), typeof t == "string" ? this.messages.push({ role: r.USER, content: t }) : this.messages.push(t.messages[t.messages.length - 1]), this.iteration = M, await this.processSteamToolCallsAndResponses(o).catch((s) => {
|
|
216
|
+
console.error("Chat failed:", s);
|
|
217
|
+
}), "ok";
|
|
218
|
+
}
|
|
219
|
+
clearMessages() {
|
|
220
|
+
this.messages = [];
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 聊天迭代主流程,支持多轮工具调用和最终回复
|
|
224
|
+
*/
|
|
225
|
+
async processSteamToolCallsAndResponses(t) {
|
|
226
|
+
try {
|
|
227
|
+
const o = [];
|
|
228
|
+
for (; this.iteration > 0; ) {
|
|
229
|
+
const s = await this.doLLMChart(), [n, a] = await this.parseToolCalls(s, t);
|
|
230
|
+
if (n.length) {
|
|
231
|
+
const c = {
|
|
232
|
+
role: r.ASSISTANT,
|
|
233
|
+
content: `调用工具:${n.map((e) => e.function.name).join(",")}`,
|
|
234
|
+
tool_calls: n.map((e) => ({
|
|
235
|
+
id: e.id,
|
|
236
|
+
type: e.type,
|
|
237
|
+
function: e.function
|
|
238
|
+
}))
|
|
239
|
+
};
|
|
240
|
+
this.messages.push(c);
|
|
241
|
+
const { toolResults: l, toolCallMessages: h } = await this.callTools(n, t);
|
|
242
|
+
o.push(...l), h.forEach((e) => this.messages.push(e)), this.iteration--;
|
|
243
|
+
} else
|
|
244
|
+
this.messages.push({ role: r.ASSISTANT, content: a }), this.iteration = 0;
|
|
245
|
+
}
|
|
246
|
+
t.onDone();
|
|
247
|
+
} catch (o) {
|
|
248
|
+
throw console.error("Chat iteration failed:", o), o;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
export {
|
|
253
|
+
D as MCPHost
|
|
254
|
+
};
|