@eko-ai/eko 4.0.0 → 4.0.1
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/agent/plan.d.ts.map +1 -1
- package/dist/chat/index.d.ts +5 -1
- package/dist/chat/index.d.ts.map +1 -1
- package/dist/chat/tools/variable-storage.d.ts +1 -0
- package/dist/chat/tools/variable-storage.d.ts.map +1 -1
- package/dist/chat/tools/web-search.d.ts +1 -0
- package/dist/chat/tools/web-search.d.ts.map +1 -1
- package/dist/chat/tools/webpage-qa.d.ts +1 -0
- package/dist/chat/tools/webpage-qa.d.ts.map +1 -1
- package/dist/index.cjs.js +1379 -1374
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +1375 -1375
- package/dist/index.esm.js.map +1 -1
- package/dist/prompt/prompt-template.d.ts +30 -0
- package/dist/prompt/prompt-template.d.ts.map +1 -0
- package/dist/service/browser-service.d.ts +2 -1
- package/dist/service/browser-service.d.ts.map +1 -1
- package/dist/service/chat-service.d.ts +2 -1
- package/dist/service/chat-service.d.ts.map +1 -1
- package/dist/service/index.d.ts +4 -0
- package/dist/service/index.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -31493,7 +31493,7 @@ class Planner {
|
|
|
31493
31493
|
}
|
|
31494
31494
|
}
|
|
31495
31495
|
if (this.callback) {
|
|
31496
|
-
|
|
31496
|
+
const workflow = parseWorkflow(this.taskId, streamText, false, thinkingText);
|
|
31497
31497
|
if (workflow) {
|
|
31498
31498
|
await this.callback.onMessage({
|
|
31499
31499
|
streamType: "agent",
|
|
@@ -31526,7 +31526,14 @@ class Planner {
|
|
|
31526
31526
|
chain.planRequest = request;
|
|
31527
31527
|
chain.planResult = streamText;
|
|
31528
31528
|
}
|
|
31529
|
-
|
|
31529
|
+
const workflow = parseWorkflow(this.taskId, streamText, true, thinkingText);
|
|
31530
|
+
if (workflow.taskPrompt) {
|
|
31531
|
+
workflow.taskPrompt += "\n" + taskPrompt;
|
|
31532
|
+
}
|
|
31533
|
+
else {
|
|
31534
|
+
workflow.taskPrompt = taskPrompt;
|
|
31535
|
+
}
|
|
31536
|
+
workflow.taskPrompt = workflow.taskPrompt.trim();
|
|
31530
31537
|
if (this.callback) {
|
|
31531
31538
|
await this.callback.onMessage({
|
|
31532
31539
|
streamType: "agent",
|
|
@@ -31538,13 +31545,6 @@ class Planner {
|
|
|
31538
31545
|
workflow: workflow,
|
|
31539
31546
|
});
|
|
31540
31547
|
}
|
|
31541
|
-
if (workflow.taskPrompt) {
|
|
31542
|
-
workflow.taskPrompt += "\n" + taskPrompt;
|
|
31543
|
-
}
|
|
31544
|
-
else {
|
|
31545
|
-
workflow.taskPrompt = taskPrompt;
|
|
31546
|
-
}
|
|
31547
|
-
workflow.taskPrompt = workflow.taskPrompt.trim();
|
|
31548
31548
|
return workflow;
|
|
31549
31549
|
}
|
|
31550
31550
|
}
|
|
@@ -35871,1485 +35871,1485 @@ class EkoMemory {
|
|
|
35871
35871
|
}
|
|
35872
35872
|
}
|
|
35873
35873
|
|
|
35874
|
-
|
|
35875
|
-
|
|
35876
|
-
|
|
35877
|
-
|
|
35878
|
-
|
|
35879
|
-
|
|
35880
|
-
|
|
35881
|
-
|
|
35882
|
-
|
|
35883
|
-
|
|
35884
|
-
|
|
35885
|
-
|
|
35886
|
-
|
|
35887
|
-
|
|
35888
|
-
|
|
35889
|
-
|
|
35890
|
-
|
|
35891
|
-
|
|
35892
|
-
|
|
35893
|
-
|
|
35894
|
-
|
|
35895
|
-
|
|
35896
|
-
|
|
35897
|
-
|
|
35898
|
-
|
|
35899
|
-
|
|
35900
|
-
|
|
35901
|
-
|
|
35902
|
-
|
|
35903
|
-
|
|
35904
|
-
|
|
35905
|
-
|
|
35906
|
-
|
|
35907
|
-
case "text-delta": {
|
|
35908
|
-
if (toolPart && !chunk.delta) {
|
|
35909
|
-
continue;
|
|
35874
|
+
class SimpleSseMcpClient {
|
|
35875
|
+
constructor(sseServerUrl, clientName = "EkoMcpClient", headers = {}) {
|
|
35876
|
+
this.protocolVersion = "2024-11-05";
|
|
35877
|
+
this.sseUrl = sseServerUrl;
|
|
35878
|
+
this.clientName = clientName;
|
|
35879
|
+
this.headers = headers;
|
|
35880
|
+
this.requestMap = new Map();
|
|
35881
|
+
}
|
|
35882
|
+
async connect(signal) {
|
|
35883
|
+
Log.info("MCP Client, connecting...", this.sseUrl);
|
|
35884
|
+
if (this.sseHandler && this.sseHandler.readyState == 1) {
|
|
35885
|
+
this.sseHandler.close && this.sseHandler.close();
|
|
35886
|
+
this.sseHandler = undefined;
|
|
35887
|
+
}
|
|
35888
|
+
this.pingTimer && clearInterval(this.pingTimer);
|
|
35889
|
+
this.reconnectTimer && clearTimeout(this.reconnectTimer);
|
|
35890
|
+
await new Promise((resolve) => {
|
|
35891
|
+
const timer = setTimeout(resolve, 15000);
|
|
35892
|
+
this.sseHandler = {
|
|
35893
|
+
onopen: () => {
|
|
35894
|
+
Log.info("MCP Client, connection successful", this.sseUrl);
|
|
35895
|
+
clearTimeout(timer);
|
|
35896
|
+
setTimeout(resolve, 200);
|
|
35897
|
+
},
|
|
35898
|
+
onmessage: (data) => this.onmessage(data),
|
|
35899
|
+
onerror: (e) => {
|
|
35900
|
+
Log.error("MCP Client, error: ", e);
|
|
35901
|
+
clearTimeout(timer);
|
|
35902
|
+
if (this.sseHandler?.readyState === 2) {
|
|
35903
|
+
this.pingTimer && clearInterval(this.pingTimer);
|
|
35904
|
+
this.reconnectTimer = setTimeout(() => {
|
|
35905
|
+
this.connect();
|
|
35906
|
+
}, 500);
|
|
35910
35907
|
}
|
|
35911
|
-
|
|
35912
|
-
|
|
35913
|
-
|
|
35914
|
-
|
|
35915
|
-
|
|
35916
|
-
|
|
35917
|
-
|
|
35918
|
-
|
|
35919
|
-
|
|
35908
|
+
resolve();
|
|
35909
|
+
},
|
|
35910
|
+
};
|
|
35911
|
+
connectSse(this.sseUrl, this.sseHandler, this.headers, signal);
|
|
35912
|
+
});
|
|
35913
|
+
this.pingTimer = setInterval(() => this.ping(), 10000);
|
|
35914
|
+
}
|
|
35915
|
+
onmessage(data) {
|
|
35916
|
+
Log.debug("MCP Client, onmessage", this.sseUrl, data);
|
|
35917
|
+
if (data.event == "endpoint") {
|
|
35918
|
+
let uri = data.data;
|
|
35919
|
+
let msgUrl;
|
|
35920
|
+
let idx = this.sseUrl.indexOf("/", 10);
|
|
35921
|
+
if (idx > -1) {
|
|
35922
|
+
msgUrl = this.sseUrl.substring(0, idx) + uri;
|
|
35923
|
+
}
|
|
35924
|
+
else {
|
|
35925
|
+
msgUrl = this.sseUrl + uri;
|
|
35926
|
+
}
|
|
35927
|
+
this.msgUrl = msgUrl;
|
|
35928
|
+
this.initialize();
|
|
35929
|
+
}
|
|
35930
|
+
else if (data.event == "message") {
|
|
35931
|
+
let message = JSON.parse(data.data);
|
|
35932
|
+
let _resolve = this.requestMap.get(message.id);
|
|
35933
|
+
_resolve && _resolve(message);
|
|
35934
|
+
}
|
|
35935
|
+
}
|
|
35936
|
+
async initialize() {
|
|
35937
|
+
await this.request("initialize", {
|
|
35938
|
+
protocolVersion: this.protocolVersion,
|
|
35939
|
+
capabilities: {
|
|
35940
|
+
tools: {
|
|
35941
|
+
listChanged: true,
|
|
35942
|
+
},
|
|
35943
|
+
sampling: {},
|
|
35944
|
+
},
|
|
35945
|
+
clientInfo: {
|
|
35946
|
+
name: this.clientName,
|
|
35947
|
+
version: "1.0.0",
|
|
35948
|
+
},
|
|
35949
|
+
});
|
|
35950
|
+
try {
|
|
35951
|
+
await this.request("notifications/initialized", {});
|
|
35952
|
+
}
|
|
35953
|
+
catch (ignored) { }
|
|
35954
|
+
}
|
|
35955
|
+
ping() {
|
|
35956
|
+
this.request("ping", {});
|
|
35957
|
+
}
|
|
35958
|
+
async listTools(param, signal) {
|
|
35959
|
+
const message = await this.request("tools/list", {
|
|
35960
|
+
...param,
|
|
35961
|
+
}, signal);
|
|
35962
|
+
return message.result.tools || [];
|
|
35963
|
+
}
|
|
35964
|
+
async callTool(param, signal) {
|
|
35965
|
+
const message = await this.request("tools/call", {
|
|
35966
|
+
...param,
|
|
35967
|
+
}, signal);
|
|
35968
|
+
return message.result;
|
|
35969
|
+
}
|
|
35970
|
+
async request(method, params, signal) {
|
|
35971
|
+
const id = method.startsWith("notifications/") ? undefined : uuidv4();
|
|
35972
|
+
try {
|
|
35973
|
+
const callback = new Promise((resolve, reject) => {
|
|
35974
|
+
if (signal) {
|
|
35975
|
+
signal.addEventListener("abort", () => {
|
|
35976
|
+
const error = new Error("Operation was interrupted");
|
|
35977
|
+
error.name = "AbortError";
|
|
35978
|
+
reject(error);
|
|
35920
35979
|
});
|
|
35921
|
-
if (toolPart) {
|
|
35922
|
-
await streamCallback.onMessage({
|
|
35923
|
-
streamType: "chat",
|
|
35924
|
-
chatId: chatContext.getChatId(),
|
|
35925
|
-
messageId,
|
|
35926
|
-
type: "tool_use",
|
|
35927
|
-
toolCallId: toolPart.toolCallId,
|
|
35928
|
-
toolName: toolPart.toolName,
|
|
35929
|
-
params: toolPart.input || {},
|
|
35930
|
-
});
|
|
35931
|
-
toolPart = null;
|
|
35932
|
-
}
|
|
35933
|
-
break;
|
|
35934
|
-
}
|
|
35935
|
-
case "text-end": {
|
|
35936
|
-
textStreamDone = true;
|
|
35937
|
-
if (streamText) {
|
|
35938
|
-
await streamCallback.onMessage({
|
|
35939
|
-
streamType: "chat",
|
|
35940
|
-
chatId: chatContext.getChatId(),
|
|
35941
|
-
messageId,
|
|
35942
|
-
type: "text",
|
|
35943
|
-
streamId: textStreamId,
|
|
35944
|
-
streamDone: true,
|
|
35945
|
-
text: streamText,
|
|
35946
|
-
});
|
|
35947
|
-
}
|
|
35948
|
-
break;
|
|
35949
|
-
}
|
|
35950
|
-
case "reasoning-start": {
|
|
35951
|
-
thinkStreamId = uuidv4();
|
|
35952
|
-
break;
|
|
35953
35980
|
}
|
|
35954
|
-
|
|
35955
|
-
|
|
35956
|
-
|
|
35957
|
-
|
|
35958
|
-
|
|
35959
|
-
|
|
35960
|
-
|
|
35961
|
-
|
|
35962
|
-
|
|
35963
|
-
|
|
35964
|
-
|
|
35965
|
-
|
|
35981
|
+
id && this.requestMap.set(id, resolve);
|
|
35982
|
+
});
|
|
35983
|
+
Log.debug(`MCP Client, ${method}`, id, params);
|
|
35984
|
+
const response = await fetch(this.msgUrl, {
|
|
35985
|
+
method: "POST",
|
|
35986
|
+
headers: {
|
|
35987
|
+
"Content-Type": "application/json",
|
|
35988
|
+
...this.headers,
|
|
35989
|
+
},
|
|
35990
|
+
body: JSON.stringify({
|
|
35991
|
+
jsonrpc: "2.0",
|
|
35992
|
+
id: id,
|
|
35993
|
+
method: method,
|
|
35994
|
+
params: {
|
|
35995
|
+
...params,
|
|
35996
|
+
},
|
|
35997
|
+
}),
|
|
35998
|
+
signal: signal,
|
|
35999
|
+
});
|
|
36000
|
+
const body = await response.text();
|
|
36001
|
+
if (body == "Accepted") {
|
|
36002
|
+
const message = await callback;
|
|
36003
|
+
if (message.error) {
|
|
36004
|
+
Log.error(`MCP ${method} error: ` + message.error);
|
|
36005
|
+
throw new Error(`MCP ${method} error: ` +
|
|
36006
|
+
(typeof message.error === "string"
|
|
36007
|
+
? message.error
|
|
36008
|
+
: message.error.message));
|
|
35966
36009
|
}
|
|
35967
|
-
|
|
35968
|
-
if (
|
|
35969
|
-
|
|
35970
|
-
|
|
35971
|
-
|
|
35972
|
-
|
|
35973
|
-
|
|
35974
|
-
|
|
35975
|
-
|
|
35976
|
-
text: thinkText,
|
|
35977
|
-
});
|
|
36010
|
+
if (message.result?.isError == true) {
|
|
36011
|
+
if (message.result.content) {
|
|
36012
|
+
throw new Error(`MCP ${method} error: ` +
|
|
36013
|
+
(typeof message.result.content === "string"
|
|
36014
|
+
? message.result.content
|
|
36015
|
+
: message.result.content[0].text));
|
|
36016
|
+
}
|
|
36017
|
+
else {
|
|
36018
|
+
throw new Error(`MCP ${method} error: ` + JSON.stringify(message.result));
|
|
35978
36019
|
}
|
|
35979
|
-
break;
|
|
35980
36020
|
}
|
|
35981
|
-
|
|
35982
|
-
|
|
35983
|
-
|
|
35984
|
-
|
|
35985
|
-
|
|
35986
|
-
|
|
35987
|
-
|
|
35988
|
-
|
|
35989
|
-
|
|
35990
|
-
|
|
35991
|
-
|
|
35992
|
-
|
|
35993
|
-
|
|
35994
|
-
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
|
|
35998
|
-
|
|
35999
|
-
|
|
36000
|
-
|
|
36001
|
-
|
|
36002
|
-
|
|
36003
|
-
|
|
36004
|
-
|
|
36005
|
-
|
|
36006
|
-
|
|
36007
|
-
|
|
36008
|
-
|
|
36009
|
-
|
|
36010
|
-
|
|
36011
|
-
|
|
36012
|
-
|
|
36013
|
-
|
|
36014
|
-
|
|
36015
|
-
|
|
36016
|
-
|
|
36017
|
-
|
|
36018
|
-
|
|
36019
|
-
|
|
36020
|
-
|
|
36021
|
-
|
|
36022
|
-
|
|
36023
|
-
|
|
36024
|
-
|
|
36025
|
-
|
|
36026
|
-
|
|
36027
|
-
|
|
36028
|
-
|
|
36029
|
-
|
|
36030
|
-
|
|
36031
|
-
|
|
36032
|
-
|
|
36033
|
-
|
|
36034
|
-
|
|
36035
|
-
|
|
36036
|
-
|
|
36037
|
-
|
|
36038
|
-
|
|
36039
|
-
|
|
36040
|
-
|
|
36041
|
-
|
|
36042
|
-
|
|
36043
|
-
|
|
36044
|
-
|
|
36045
|
-
|
|
36046
|
-
|
|
36047
|
-
|
|
36048
|
-
|
|
36049
|
-
|
|
36050
|
-
|
|
36051
|
-
|
|
36052
|
-
|
|
36053
|
-
|
|
36054
|
-
type: "error",
|
|
36055
|
-
error: chunk.error,
|
|
36056
|
-
});
|
|
36057
|
-
throw new Error("LLM Error: " + chunk.error);
|
|
36058
|
-
}
|
|
36059
|
-
case "finish": {
|
|
36060
|
-
if (!textStreamDone) {
|
|
36061
|
-
textStreamDone = true;
|
|
36062
|
-
await streamCallback.onMessage({
|
|
36063
|
-
streamType: "chat",
|
|
36064
|
-
chatId: chatContext.getChatId(),
|
|
36065
|
-
messageId,
|
|
36066
|
-
type: "text",
|
|
36067
|
-
streamId: textStreamId,
|
|
36068
|
-
streamDone: true,
|
|
36069
|
-
text: streamText,
|
|
36070
|
-
});
|
|
36071
|
-
}
|
|
36072
|
-
if (toolPart) {
|
|
36073
|
-
await streamCallback.onMessage({
|
|
36074
|
-
streamType: "chat",
|
|
36075
|
-
chatId: chatContext.getChatId(),
|
|
36076
|
-
messageId,
|
|
36077
|
-
type: "tool_use",
|
|
36078
|
-
toolCallId: toolPart.toolCallId,
|
|
36079
|
-
toolName: toolPart.toolName,
|
|
36080
|
-
params: toolPart.input || {},
|
|
36081
|
-
});
|
|
36082
|
-
toolPart = null;
|
|
36083
|
-
}
|
|
36084
|
-
await streamCallback.onMessage({
|
|
36085
|
-
streamType: "chat",
|
|
36086
|
-
chatId: chatContext.getChatId(),
|
|
36087
|
-
messageId,
|
|
36088
|
-
type: "finish",
|
|
36089
|
-
finishReason: chunk.finishReason,
|
|
36090
|
-
usage: {
|
|
36091
|
-
promptTokens: chunk.usage.inputTokens || 0,
|
|
36092
|
-
completionTokens: chunk.usage.outputTokens || 0,
|
|
36093
|
-
totalTokens: chunk.usage.totalTokens ||
|
|
36094
|
-
(chunk.usage.inputTokens || 0) +
|
|
36095
|
-
(chunk.usage.outputTokens || 0),
|
|
36096
|
-
},
|
|
36097
|
-
});
|
|
36098
|
-
break;
|
|
36021
|
+
return message;
|
|
36022
|
+
}
|
|
36023
|
+
else {
|
|
36024
|
+
throw new Error(`MCP ${method} error:` + body);
|
|
36025
|
+
}
|
|
36026
|
+
}
|
|
36027
|
+
finally {
|
|
36028
|
+
id && this.requestMap.delete(id);
|
|
36029
|
+
}
|
|
36030
|
+
}
|
|
36031
|
+
isConnected() {
|
|
36032
|
+
if (this.sseHandler && this.sseHandler.readyState == 1) {
|
|
36033
|
+
return true;
|
|
36034
|
+
}
|
|
36035
|
+
return false;
|
|
36036
|
+
}
|
|
36037
|
+
async close() {
|
|
36038
|
+
try {
|
|
36039
|
+
await this.request("notifications/cancelled", {
|
|
36040
|
+
requestId: uuidv4(),
|
|
36041
|
+
reason: "User requested cancellation",
|
|
36042
|
+
});
|
|
36043
|
+
}
|
|
36044
|
+
catch (ignored) { }
|
|
36045
|
+
this.pingTimer && clearInterval(this.pingTimer);
|
|
36046
|
+
this.reconnectTimer && clearTimeout(this.reconnectTimer);
|
|
36047
|
+
this.sseHandler && this.sseHandler.close && this.sseHandler.close();
|
|
36048
|
+
this.pingTimer = undefined;
|
|
36049
|
+
this.sseHandler = undefined;
|
|
36050
|
+
this.reconnectTimer = undefined;
|
|
36051
|
+
}
|
|
36052
|
+
}
|
|
36053
|
+
async function connectSse(sseUrl, hander, headers = {}, _signal) {
|
|
36054
|
+
try {
|
|
36055
|
+
hander.readyState = 0;
|
|
36056
|
+
const controller = new AbortController();
|
|
36057
|
+
const signal = _signal
|
|
36058
|
+
? AbortSignal.any([controller.signal, _signal])
|
|
36059
|
+
: controller.signal;
|
|
36060
|
+
const response = await fetch(sseUrl, {
|
|
36061
|
+
method: "GET",
|
|
36062
|
+
headers: {
|
|
36063
|
+
"Content-Type": "text/event-stream",
|
|
36064
|
+
"Cache-Control": "no-cache",
|
|
36065
|
+
...headers,
|
|
36066
|
+
},
|
|
36067
|
+
body: null,
|
|
36068
|
+
keepalive: true,
|
|
36069
|
+
signal: signal,
|
|
36070
|
+
});
|
|
36071
|
+
const reader = response.body?.getReader();
|
|
36072
|
+
hander.close = () => {
|
|
36073
|
+
controller.abort();
|
|
36074
|
+
hander.readyState = 2;
|
|
36075
|
+
Log.debug("McpClient close abort.", sseUrl);
|
|
36076
|
+
};
|
|
36077
|
+
let str = "";
|
|
36078
|
+
const decoder = new TextDecoder();
|
|
36079
|
+
hander.readyState = 1;
|
|
36080
|
+
hander.onopen();
|
|
36081
|
+
while (hander.readyState == 1) {
|
|
36082
|
+
const { value, done } = await reader?.read();
|
|
36083
|
+
if (done) {
|
|
36084
|
+
break;
|
|
36085
|
+
}
|
|
36086
|
+
const text = decoder.decode(value);
|
|
36087
|
+
str += text;
|
|
36088
|
+
if (str.indexOf("\n\n") > -1) {
|
|
36089
|
+
const chunks = str.split("\n\n");
|
|
36090
|
+
for (let i = 0; i < chunks.length - 1; i++) {
|
|
36091
|
+
const chunk = chunks[i];
|
|
36092
|
+
const chunkData = parseChunk(chunk);
|
|
36093
|
+
hander.onmessage(chunkData);
|
|
36099
36094
|
}
|
|
36095
|
+
str = chunks[chunks.length - 1];
|
|
36100
36096
|
}
|
|
36101
36097
|
}
|
|
36102
36098
|
}
|
|
36103
36099
|
catch (e) {
|
|
36104
|
-
if (
|
|
36105
|
-
|
|
36106
|
-
|
|
36100
|
+
if (e?.name !== "AbortError") {
|
|
36101
|
+
Log.error("MCP Client, connectSse error:", e);
|
|
36102
|
+
hander.onerror(e);
|
|
36107
36103
|
}
|
|
36108
|
-
throw e;
|
|
36109
36104
|
}
|
|
36110
36105
|
finally {
|
|
36111
|
-
|
|
36106
|
+
hander.readyState = 2;
|
|
36112
36107
|
}
|
|
36113
|
-
return streamText
|
|
36114
|
-
? [
|
|
36115
|
-
{ type: "text", text: streamText },
|
|
36116
|
-
...toolParts,
|
|
36117
|
-
]
|
|
36118
|
-
: toolParts;
|
|
36119
36108
|
}
|
|
36120
|
-
function
|
|
36121
|
-
|
|
36122
|
-
|
|
36123
|
-
|
|
36124
|
-
|
|
36125
|
-
|
|
36126
|
-
|
|
36109
|
+
function parseChunk(chunk) {
|
|
36110
|
+
const lines = chunk.split("\n");
|
|
36111
|
+
const chunk_obj = {};
|
|
36112
|
+
for (let j = 0; j < lines.length; j++) {
|
|
36113
|
+
const line = lines[j];
|
|
36114
|
+
if (line.startsWith("id:")) {
|
|
36115
|
+
chunk_obj["id"] = line.substring(3).trim();
|
|
36127
36116
|
}
|
|
36128
|
-
else if (
|
|
36129
|
-
|
|
36130
|
-
type: "tool-call",
|
|
36131
|
-
toolCallId: part.toolCallId,
|
|
36132
|
-
toolName: part.toolName,
|
|
36133
|
-
args: (part.input || {}),
|
|
36134
|
-
};
|
|
36117
|
+
else if (line.startsWith("event:")) {
|
|
36118
|
+
chunk_obj["event"] = line.substring(6).trim();
|
|
36135
36119
|
}
|
|
36136
|
-
|
|
36137
|
-
|
|
36138
|
-
}
|
|
36139
|
-
|
|
36140
|
-
|
|
36141
|
-
|
|
36142
|
-
|
|
36143
|
-
|
|
36144
|
-
|
|
36145
|
-
|
|
36146
|
-
|
|
36147
|
-
? output.value
|
|
36148
|
-
: output.type == "json" || output.type == "error-json"
|
|
36149
|
-
? output.value
|
|
36150
|
-
: output.value
|
|
36151
|
-
.map((s) => {
|
|
36152
|
-
if (s.type == "text") {
|
|
36153
|
-
return s.text;
|
|
36154
|
-
}
|
|
36155
|
-
else if (s.type == "media") {
|
|
36156
|
-
return JSON.stringify({
|
|
36157
|
-
data: s.data,
|
|
36158
|
-
mimeType: s.mediaType,
|
|
36159
|
-
});
|
|
36160
|
-
}
|
|
36161
|
-
})
|
|
36162
|
-
.join("\n"),
|
|
36163
|
-
};
|
|
36164
|
-
});
|
|
36120
|
+
else if (line.startsWith("data:")) {
|
|
36121
|
+
chunk_obj["data"] = line.substring(5).trim();
|
|
36122
|
+
}
|
|
36123
|
+
else {
|
|
36124
|
+
const idx = line.indexOf(":");
|
|
36125
|
+
if (idx > -1) {
|
|
36126
|
+
chunk_obj[line.substring(0, idx)] = line.substring(idx + 1).trim();
|
|
36127
|
+
}
|
|
36128
|
+
}
|
|
36129
|
+
}
|
|
36130
|
+
return chunk_obj;
|
|
36165
36131
|
}
|
|
36166
36132
|
|
|
36167
|
-
class
|
|
36168
|
-
constructor(
|
|
36169
|
-
this.
|
|
36170
|
-
this.
|
|
36171
|
-
this.
|
|
36172
|
-
this.
|
|
36133
|
+
class SimpleHttpMcpClient {
|
|
36134
|
+
constructor(httpUrl, clientName = "EkoMcpClient", headers = {}) {
|
|
36135
|
+
this.protocolVersion = "2025-06-18";
|
|
36136
|
+
this.connected = false;
|
|
36137
|
+
this.httpUrl = httpUrl;
|
|
36138
|
+
this.clientName = clientName;
|
|
36139
|
+
this.headers = headers;
|
|
36173
36140
|
}
|
|
36174
|
-
|
|
36175
|
-
|
|
36141
|
+
async connect(signal) {
|
|
36142
|
+
Log.info("MCP Client, connecting...", this.httpUrl);
|
|
36143
|
+
this.mcpSessionId = null;
|
|
36144
|
+
await this.request("initialize", {
|
|
36145
|
+
protocolVersion: this.protocolVersion,
|
|
36146
|
+
capabilities: {
|
|
36147
|
+
tools: {
|
|
36148
|
+
listChanged: true,
|
|
36149
|
+
},
|
|
36150
|
+
sampling: {},
|
|
36151
|
+
},
|
|
36152
|
+
clientInfo: {
|
|
36153
|
+
name: this.clientName,
|
|
36154
|
+
version: "1.0.0",
|
|
36155
|
+
},
|
|
36156
|
+
}, signal);
|
|
36157
|
+
if (this.mcpSessionId) {
|
|
36158
|
+
try {
|
|
36159
|
+
await this.request("notifications/initialized", {});
|
|
36160
|
+
}
|
|
36161
|
+
catch (ignored) { }
|
|
36162
|
+
}
|
|
36163
|
+
this.connected = true;
|
|
36176
36164
|
}
|
|
36177
|
-
|
|
36178
|
-
|
|
36165
|
+
async listTools(param, signal) {
|
|
36166
|
+
const message = await this.request("tools/list", {
|
|
36167
|
+
...param,
|
|
36168
|
+
}, signal);
|
|
36169
|
+
return message.result.tools || [];
|
|
36179
36170
|
}
|
|
36180
|
-
|
|
36181
|
-
this.
|
|
36171
|
+
async callTool(param, signal) {
|
|
36172
|
+
const message = await this.request("tools/call", {
|
|
36173
|
+
...param,
|
|
36174
|
+
}, signal);
|
|
36175
|
+
return message.result;
|
|
36182
36176
|
}
|
|
36183
|
-
|
|
36184
|
-
return this.
|
|
36177
|
+
isConnected() {
|
|
36178
|
+
return this.connected;
|
|
36185
36179
|
}
|
|
36186
|
-
|
|
36187
|
-
|
|
36180
|
+
async close() {
|
|
36181
|
+
this.connected = false;
|
|
36182
|
+
if (this.mcpSessionId) {
|
|
36183
|
+
try {
|
|
36184
|
+
await this.request("notifications/cancelled", {
|
|
36185
|
+
requestId: uuidv4(),
|
|
36186
|
+
reason: "User requested cancellation",
|
|
36187
|
+
});
|
|
36188
|
+
}
|
|
36189
|
+
catch (ignored) { }
|
|
36190
|
+
this.mcpSessionId = null;
|
|
36191
|
+
}
|
|
36188
36192
|
}
|
|
36189
|
-
|
|
36190
|
-
|
|
36191
|
-
const
|
|
36192
|
-
const
|
|
36193
|
-
|
|
36194
|
-
|
|
36195
|
-
|
|
36196
|
-
|
|
36197
|
-
|
|
36198
|
-
|
|
36199
|
-
|
|
36200
|
-
|
|
36201
|
-
|
|
36202
|
-
|
|
36203
|
-
|
|
36204
|
-
|
|
36205
|
-
<language>{{language}}</language>
|
|
36206
|
-
</if>
|
|
36207
|
-
|
|
36208
|
-
Answer user's question based on the webpage context, the answer should be in the same language as the user's question.
|
|
36209
|
-
`;
|
|
36210
|
-
class WebpageQaTool {
|
|
36211
|
-
constructor(chatContext, params) {
|
|
36212
|
-
this.name = TOOL_NAME$3;
|
|
36213
|
-
this.params = params;
|
|
36214
|
-
this.chatContext = chatContext;
|
|
36215
|
-
this.description = `This tool is designed only for handling simple web-related tasks, including summarizing webpage content, extracting data from web pages, translating webpage content, and converting webpage information into more easily understandable forms. It does not interact with or operate web pages. For more complex browser tasks, please use deepAction.It does not perform operations on the webpage itself, but only involves reading the page content. Users do not need to provide the web page content, as the tool can automatically extract the content of the web page based on the tabId to respond.`;
|
|
36216
|
-
this.parameters = {
|
|
36217
|
-
type: "object",
|
|
36218
|
-
properties: {
|
|
36219
|
-
language: {
|
|
36220
|
-
type: "string",
|
|
36221
|
-
description: "User language used, eg: English",
|
|
36222
|
-
},
|
|
36223
|
-
tabIds: {
|
|
36224
|
-
type: "array",
|
|
36225
|
-
description: "The browser tab ids to be used for the QA. When the user says 'left side' or 'current', it means current active tab.",
|
|
36226
|
-
items: { type: "integer" },
|
|
36193
|
+
async request(method, params, signal) {
|
|
36194
|
+
try {
|
|
36195
|
+
const id = method.startsWith("notifications/") ? undefined : uuidv4();
|
|
36196
|
+
const extHeaders = {};
|
|
36197
|
+
if (this.mcpSessionId && method !== "initialize") {
|
|
36198
|
+
extHeaders["Mcp-Session-Id"] = this.mcpSessionId;
|
|
36199
|
+
}
|
|
36200
|
+
const response = await fetch(this.httpUrl, {
|
|
36201
|
+
method: "POST",
|
|
36202
|
+
headers: {
|
|
36203
|
+
"Cache-Control": "no-cache",
|
|
36204
|
+
"Content-Type": "application/json",
|
|
36205
|
+
Accept: "application/json, text/event-stream",
|
|
36206
|
+
"MCP-Protocol-Version": this.protocolVersion,
|
|
36207
|
+
...extHeaders,
|
|
36208
|
+
...this.headers,
|
|
36227
36209
|
},
|
|
36228
|
-
|
|
36229
|
-
|
|
36230
|
-
|
|
36231
|
-
|
|
36232
|
-
|
|
36233
|
-
|
|
36234
|
-
return {
|
|
36235
|
-
content: [
|
|
36236
|
-
{
|
|
36237
|
-
type: "text",
|
|
36238
|
-
text: "Error: not implemented",
|
|
36210
|
+
body: JSON.stringify({
|
|
36211
|
+
jsonrpc: "2.0",
|
|
36212
|
+
id: id,
|
|
36213
|
+
method: method,
|
|
36214
|
+
params: {
|
|
36215
|
+
...params,
|
|
36239
36216
|
},
|
|
36240
|
-
|
|
36241
|
-
|
|
36242
|
-
|
|
36243
|
-
|
|
36244
|
-
|
|
36245
|
-
|
|
36246
|
-
|
|
36247
|
-
|
|
36248
|
-
|
|
36249
|
-
|
|
36250
|
-
|
|
36251
|
-
|
|
36252
|
-
|
|
36253
|
-
.
|
|
36254
|
-
|
|
36255
|
-
|
|
36256
|
-
|
|
36257
|
-
|
|
36258
|
-
|
|
36259
|
-
|
|
36260
|
-
|
|
36261
|
-
|
|
36262
|
-
|
|
36263
|
-
|
|
36264
|
-
|
|
36265
|
-
|
|
36266
|
-
|
|
36267
|
-
|
|
36268
|
-
|
|
36269
|
-
|
|
36270
|
-
|
|
36271
|
-
|
|
36272
|
-
|
|
36273
|
-
|
|
36274
|
-
|
|
36275
|
-
|
|
36276
|
-
|
|
36277
|
-
|
|
36278
|
-
|
|
36279
|
-
|
|
36280
|
-
|
|
36281
|
-
|
|
36282
|
-
toolCallId: toolCall.toolCallId,
|
|
36283
|
-
text: text,
|
|
36284
|
-
streamId: streamId,
|
|
36285
|
-
streamDone: false,
|
|
36286
|
-
});
|
|
36287
|
-
}
|
|
36288
|
-
else if (chunk.type == "error") {
|
|
36289
|
-
throw new Error(chunk.error);
|
|
36290
|
-
}
|
|
36291
|
-
else if (chunk.type == "finish") {
|
|
36292
|
-
break;
|
|
36217
|
+
}),
|
|
36218
|
+
keepalive: true,
|
|
36219
|
+
signal: signal,
|
|
36220
|
+
});
|
|
36221
|
+
if (method.startsWith("notifications/")) {
|
|
36222
|
+
return;
|
|
36223
|
+
}
|
|
36224
|
+
if (method == "initialize") {
|
|
36225
|
+
this.mcpSessionId =
|
|
36226
|
+
response.headers.get("Mcp-Session-Id") ||
|
|
36227
|
+
response.headers.get("mcp-session-id");
|
|
36228
|
+
}
|
|
36229
|
+
const contentType = response.headers.get("Content-Type") ||
|
|
36230
|
+
response.headers.get("content-type") ||
|
|
36231
|
+
"application/json";
|
|
36232
|
+
if (contentType?.includes("text/event-stream")) {
|
|
36233
|
+
// SSE
|
|
36234
|
+
const reader = response.body?.getReader();
|
|
36235
|
+
let str = "";
|
|
36236
|
+
let message;
|
|
36237
|
+
const decoder = new TextDecoder();
|
|
36238
|
+
while (true) {
|
|
36239
|
+
const { value, done } = await reader?.read();
|
|
36240
|
+
if (done) {
|
|
36241
|
+
break;
|
|
36242
|
+
}
|
|
36243
|
+
const text = decoder.decode(value);
|
|
36244
|
+
str += text;
|
|
36245
|
+
if (str.indexOf("\n\n") > -1) {
|
|
36246
|
+
const chunks = str.split("\n\n");
|
|
36247
|
+
for (let i = 0; i < chunks.length - 1; i++) {
|
|
36248
|
+
const chunk = chunks[i];
|
|
36249
|
+
const chunkData = this.parseChunk(chunk);
|
|
36250
|
+
if (chunkData.event == "message") {
|
|
36251
|
+
message = JSON.parse(chunkData.data);
|
|
36252
|
+
if (message.id == id) {
|
|
36253
|
+
return message;
|
|
36254
|
+
}
|
|
36255
|
+
}
|
|
36256
|
+
}
|
|
36257
|
+
str = chunks[chunks.length - 1];
|
|
36258
|
+
}
|
|
36293
36259
|
}
|
|
36260
|
+
this.handleError(method, message);
|
|
36261
|
+
return message;
|
|
36294
36262
|
}
|
|
36295
|
-
|
|
36296
|
-
|
|
36297
|
-
|
|
36298
|
-
|
|
36299
|
-
|
|
36300
|
-
chatId: this.chatContext.getChatId(),
|
|
36301
|
-
messageId: messageId,
|
|
36302
|
-
type: "tool_running",
|
|
36303
|
-
toolName: this.name,
|
|
36304
|
-
toolCallId: toolCall.toolCallId,
|
|
36305
|
-
text: text,
|
|
36306
|
-
streamId: streamId,
|
|
36307
|
-
streamDone: true,
|
|
36308
|
-
});
|
|
36309
|
-
}
|
|
36310
|
-
return {
|
|
36311
|
-
content: [
|
|
36312
|
-
{
|
|
36313
|
-
type: "text",
|
|
36314
|
-
text: text,
|
|
36315
|
-
},
|
|
36316
|
-
],
|
|
36317
|
-
};
|
|
36318
|
-
}
|
|
36319
|
-
buildTabContents(tabs) {
|
|
36320
|
-
return tabs
|
|
36321
|
-
.map((tab) => {
|
|
36322
|
-
return `<webpage>\nTabId: ${tab.tabId}\nTitle: ${tab.title}\nURL: ${tab.url}\nContent: ${sub(tab.content, 8000)}\n</webpage>`;
|
|
36323
|
-
})
|
|
36324
|
-
.join("\n");
|
|
36325
|
-
}
|
|
36326
|
-
}
|
|
36327
|
-
|
|
36328
|
-
const TOOL_NAME$2 = "webSearch";
|
|
36329
|
-
class WebSearchTool {
|
|
36330
|
-
constructor(chatContext, params) {
|
|
36331
|
-
this.name = TOOL_NAME$2;
|
|
36332
|
-
this.params = params;
|
|
36333
|
-
this.chatContext = chatContext;
|
|
36334
|
-
this.description = `Search the web for information using search engine API. This tool can perform web searches to find current information, news, articles, and other web content related to the query. It returns search results with titles, descriptions, URLs, and other relevant metadata, use this tool when users need the latest data/information and have NOT specified a particular platform or website, use the search tool.`;
|
|
36335
|
-
this.parameters = {
|
|
36336
|
-
type: "object",
|
|
36337
|
-
properties: {
|
|
36338
|
-
query: {
|
|
36339
|
-
type: "string",
|
|
36340
|
-
description: "The search query to execute. Use specific keywords and phrases for better results.",
|
|
36341
|
-
},
|
|
36342
|
-
language: {
|
|
36343
|
-
type: "string",
|
|
36344
|
-
description: "Language code for search results (e.g., 'en', 'zh', 'ja'). If not specified, will be auto-detected from query.",
|
|
36345
|
-
},
|
|
36346
|
-
count: {
|
|
36347
|
-
type: "integer",
|
|
36348
|
-
description: "Number of search results to return (default: 10, max: 50)",
|
|
36349
|
-
default: 10,
|
|
36350
|
-
minimum: 1,
|
|
36351
|
-
maximum: 50,
|
|
36352
|
-
},
|
|
36353
|
-
},
|
|
36354
|
-
required: ["query", "keywords"],
|
|
36355
|
-
};
|
|
36356
|
-
}
|
|
36357
|
-
async execute(args) {
|
|
36358
|
-
if (!global.chatService) {
|
|
36359
|
-
return {
|
|
36360
|
-
content: [
|
|
36361
|
-
{
|
|
36362
|
-
type: "text",
|
|
36363
|
-
text: "Error: not implemented",
|
|
36364
|
-
},
|
|
36365
|
-
],
|
|
36366
|
-
};
|
|
36367
|
-
}
|
|
36368
|
-
const query = args.query;
|
|
36369
|
-
const language = args.language;
|
|
36370
|
-
const count = args.count || 10;
|
|
36371
|
-
const results = await global.chatService.websearch(this.chatContext.getChatId(), query, undefined, language, count);
|
|
36372
|
-
return Promise.resolve({
|
|
36373
|
-
content: [
|
|
36374
|
-
{
|
|
36375
|
-
type: "text",
|
|
36376
|
-
text: JSON.stringify(results.map((result) => {
|
|
36377
|
-
return {
|
|
36378
|
-
title: result.title,
|
|
36379
|
-
url: result.url,
|
|
36380
|
-
content: sub(result.content || result.snippet || "", 6000),
|
|
36381
|
-
};
|
|
36382
|
-
})),
|
|
36383
|
-
},
|
|
36384
|
-
],
|
|
36385
|
-
});
|
|
36386
|
-
}
|
|
36387
|
-
}
|
|
36388
|
-
|
|
36389
|
-
async function recursiveTextNode(node, callback) {
|
|
36390
|
-
if (node.type === "normal") {
|
|
36391
|
-
callback(node, node);
|
|
36392
|
-
}
|
|
36393
|
-
if (node.type === "forEach") {
|
|
36394
|
-
node.nodes.map((item) => recursiveTextNode(item, callback));
|
|
36395
|
-
}
|
|
36396
|
-
if (node.type === "watch") {
|
|
36397
|
-
node.triggerNodes.map((triggerNode) => recursiveTextNode(triggerNode, callback));
|
|
36398
|
-
}
|
|
36399
|
-
}
|
|
36400
|
-
|
|
36401
|
-
const TOOL_NAME$1 = "deepAction";
|
|
36402
|
-
const deep_action_description = "Delegate tasks to a Javis AI assistant for completion. This assistant can understand natural language instructions and has full control over both networked computers, browser agent, and multiple specialized agents ({agentNames}). The assistant can autonomously decide to use various software tools, browse the internet to query information, write code, and perform direct operations to complete tasks. He can deliver various digitized outputs (text reports, tables, images, music, videos, websites, deepSearch, programs, etc.) and handle design/analysis tasks. and execute operational tasks (such as batch following bloggers of specific topics on certain websites). For operational tasks, the focus is on completing the process actions rather than delivering final outputs, and the assistant can complete these types of tasks well. It should also be noted that users may actively mention deepsearch, which is also one of the capabilities of this tool. If users mention it, please explicitly tell the assistant to use deepsearch. Supports parallel execution of multiple tasks.";
|
|
36403
|
-
const deep_action_param_task_description = "Task description, please output the user's original instructions without omitting any information from the user's instructions, and use the same language as the user's question.";
|
|
36404
|
-
class DeepActionTool {
|
|
36405
|
-
constructor(chatContext, params) {
|
|
36406
|
-
this.name = TOOL_NAME$1;
|
|
36407
|
-
this.chatContext = chatContext;
|
|
36408
|
-
const agents = this.chatContext.getConfig().agents || [];
|
|
36409
|
-
const agentNames = agents.map((agent) => agent.Name).join(", ");
|
|
36410
|
-
const description = global.prompts.get(GlobalPromptKey.deep_action_description) ||
|
|
36411
|
-
deep_action_description;
|
|
36412
|
-
const paramTaskDescription = global.prompts.get(GlobalPromptKey.deep_action_param_task_description) ||
|
|
36413
|
-
deep_action_param_task_description;
|
|
36414
|
-
this.description = description.replace("{agentNames}", agentNames).trim();
|
|
36415
|
-
this.parameters = {
|
|
36416
|
-
type: "object",
|
|
36417
|
-
properties: {
|
|
36418
|
-
language: {
|
|
36419
|
-
type: "string",
|
|
36420
|
-
description: "User language used, eg: English",
|
|
36421
|
-
},
|
|
36422
|
-
taskDescription: {
|
|
36423
|
-
type: "string",
|
|
36424
|
-
description: paramTaskDescription.trim(),
|
|
36425
|
-
},
|
|
36426
|
-
tabIds: {
|
|
36427
|
-
type: "array",
|
|
36428
|
-
description: "Browser Tab IDs associated with this task, When user says 'left side' or 'current', it means current active tab",
|
|
36429
|
-
items: { type: "integer" },
|
|
36430
|
-
},
|
|
36431
|
-
dependentVariables: {
|
|
36432
|
-
type: "array",
|
|
36433
|
-
description: "The current task relies on variable data from prerequisite execution outputs. Provide the name of the dependent variable.",
|
|
36434
|
-
items: {
|
|
36435
|
-
type: "string",
|
|
36436
|
-
},
|
|
36437
|
-
},
|
|
36438
|
-
},
|
|
36439
|
-
required: ["language", "taskDescription"],
|
|
36440
|
-
};
|
|
36441
|
-
this.params = params;
|
|
36442
|
-
}
|
|
36443
|
-
async execute(args, toolCall, messageId) {
|
|
36444
|
-
const chatId = this.chatContext.getChatId();
|
|
36445
|
-
const language = args.language;
|
|
36446
|
-
const taskDescription = args.taskDescription;
|
|
36447
|
-
const tabIds = args.tabIds;
|
|
36448
|
-
const dependentVariables = args.dependentVariables;
|
|
36449
|
-
const config = this.chatContext.getConfig();
|
|
36450
|
-
const globalVariables = this.chatContext.getGlobalVariables();
|
|
36451
|
-
const eko = new Eko({
|
|
36452
|
-
...config,
|
|
36453
|
-
callback: this.params.callback?.taskCallback,
|
|
36454
|
-
}, chatId);
|
|
36455
|
-
this.chatContext.addEko(messageId, eko);
|
|
36456
|
-
if (this.params.signal) {
|
|
36457
|
-
if (this.params.signal.aborted) {
|
|
36458
|
-
const error = new Error("Operation was interrupted");
|
|
36459
|
-
error.name = "AbortError";
|
|
36460
|
-
throw error;
|
|
36263
|
+
else {
|
|
36264
|
+
// JSON
|
|
36265
|
+
const message = await response.json();
|
|
36266
|
+
this.handleError(method, message);
|
|
36267
|
+
return message;
|
|
36461
36268
|
}
|
|
36462
|
-
this.params.signal.addEventListener("abort", () => {
|
|
36463
|
-
eko.abortTask(messageId, "User aborted");
|
|
36464
|
-
});
|
|
36465
|
-
}
|
|
36466
|
-
const attachments = this.params.user
|
|
36467
|
-
.filter((part) => part.type === "file")
|
|
36468
|
-
.filter((part) => part.data && part.data.length < 500)
|
|
36469
|
-
.map((part) => {
|
|
36470
|
-
return {
|
|
36471
|
-
file_name: part.filename,
|
|
36472
|
-
file_path: part.filePath,
|
|
36473
|
-
file_url: part.data,
|
|
36474
|
-
};
|
|
36475
|
-
});
|
|
36476
|
-
const taskWebsite = await this.gettaskWebsite(tabIds);
|
|
36477
|
-
const workflow = await eko.generate(taskDescription, messageId, {
|
|
36478
|
-
...globalVariables,
|
|
36479
|
-
tabIds: tabIds,
|
|
36480
|
-
language: language,
|
|
36481
|
-
attachments: attachments,
|
|
36482
|
-
taskWebsite: taskWebsite,
|
|
36483
|
-
dependentVariables: dependentVariables,
|
|
36484
|
-
datetime: this.params.datetime || new Date().toLocaleString(),
|
|
36485
|
-
});
|
|
36486
|
-
const context = eko.getTask(messageId);
|
|
36487
|
-
console.log("==> workflow", workflow);
|
|
36488
|
-
const result = await eko.execute(messageId);
|
|
36489
|
-
const variableNames = [];
|
|
36490
|
-
if (context.variables && context.variables.size > 0) {
|
|
36491
|
-
workflow.agents
|
|
36492
|
-
.map((agent) => agent.nodes)
|
|
36493
|
-
.flat()
|
|
36494
|
-
.forEach((node) => {
|
|
36495
|
-
recursiveTextNode(node, async (textNode) => {
|
|
36496
|
-
if (textNode.output) {
|
|
36497
|
-
variableNames.push(textNode.output);
|
|
36498
|
-
globalVariables.set(textNode.output, context.variables.get(textNode.output));
|
|
36499
|
-
}
|
|
36500
|
-
});
|
|
36501
|
-
});
|
|
36502
|
-
}
|
|
36503
|
-
return {
|
|
36504
|
-
content: [
|
|
36505
|
-
{
|
|
36506
|
-
type: "text",
|
|
36507
|
-
text: JSON.stringify({
|
|
36508
|
-
taskPlan: workflow.xml,
|
|
36509
|
-
subAgents: context.chain.agents.map((agent) => {
|
|
36510
|
-
return {
|
|
36511
|
-
agent: agent.agent.name,
|
|
36512
|
-
subTask: agent.agent.task,
|
|
36513
|
-
agentResult: sub(agent.agentResult || "", 800, true),
|
|
36514
|
-
};
|
|
36515
|
-
}),
|
|
36516
|
-
variables: variableNames,
|
|
36517
|
-
taskResult: result.result,
|
|
36518
|
-
success: result.success,
|
|
36519
|
-
}),
|
|
36520
|
-
},
|
|
36521
|
-
],
|
|
36522
|
-
};
|
|
36523
|
-
}
|
|
36524
|
-
async gettaskWebsite(tabIds) {
|
|
36525
|
-
if (!global.browserService) {
|
|
36526
|
-
return [];
|
|
36527
36269
|
}
|
|
36528
|
-
|
|
36529
|
-
|
|
36530
|
-
|
|
36531
|
-
tabId: tab.tabId,
|
|
36532
|
-
title: tab.title,
|
|
36533
|
-
url: sub(tab.url, 300),
|
|
36534
|
-
};
|
|
36535
|
-
});
|
|
36536
|
-
}
|
|
36537
|
-
}
|
|
36538
|
-
|
|
36539
|
-
const TOOL_NAME = "taskVariableStorage";
|
|
36540
|
-
class TaskVariableStorageTool {
|
|
36541
|
-
constructor(chatContext, params) {
|
|
36542
|
-
this.name = TOOL_NAME;
|
|
36543
|
-
this.params = params;
|
|
36544
|
-
this.chatContext = chatContext;
|
|
36545
|
-
this.description = `Used for storing, reading, and retrieving variable data, and maintaining input/output variables in task nodes.`;
|
|
36546
|
-
this.parameters = {
|
|
36547
|
-
type: "object",
|
|
36548
|
-
properties: {
|
|
36549
|
-
operation: {
|
|
36550
|
-
type: "string",
|
|
36551
|
-
description: "variable storage operation type.",
|
|
36552
|
-
enum: ["read_variable", "write_variable", "list_all_variable"],
|
|
36553
|
-
},
|
|
36554
|
-
name: {
|
|
36555
|
-
type: "string",
|
|
36556
|
-
description: "variable name, required when reading and writing variables, If reading variables, it supports reading multiple variables separated by commas.",
|
|
36557
|
-
},
|
|
36558
|
-
value: {
|
|
36559
|
-
type: "string",
|
|
36560
|
-
description: "variable value, required when writing variables",
|
|
36561
|
-
},
|
|
36562
|
-
},
|
|
36563
|
-
required: ["operation"],
|
|
36564
|
-
};
|
|
36565
|
-
}
|
|
36566
|
-
async execute(args) {
|
|
36567
|
-
let operation = args.operation;
|
|
36568
|
-
let resultText = "";
|
|
36569
|
-
switch (operation) {
|
|
36570
|
-
case "read_variable": {
|
|
36571
|
-
if (!args.name) {
|
|
36572
|
-
resultText = "Error: name is required";
|
|
36573
|
-
}
|
|
36574
|
-
else {
|
|
36575
|
-
let result = {};
|
|
36576
|
-
let name = args.name;
|
|
36577
|
-
let keys = name.split(",");
|
|
36578
|
-
for (let i = 0; i < keys.length; i++) {
|
|
36579
|
-
let key = keys[i].trim();
|
|
36580
|
-
let value = this.chatContext.getGlobalVariables().get(key);
|
|
36581
|
-
result[key] = value;
|
|
36582
|
-
}
|
|
36583
|
-
resultText = JSON.stringify(result);
|
|
36584
|
-
}
|
|
36585
|
-
break;
|
|
36586
|
-
}
|
|
36587
|
-
case "write_variable": {
|
|
36588
|
-
if (!args.name) {
|
|
36589
|
-
resultText = "Error: name is required";
|
|
36590
|
-
break;
|
|
36591
|
-
}
|
|
36592
|
-
if (args.value == undefined) {
|
|
36593
|
-
resultText = "Error: value is required";
|
|
36594
|
-
break;
|
|
36595
|
-
}
|
|
36596
|
-
let key = args.name;
|
|
36597
|
-
this.chatContext.getGlobalVariables().set(key.trim(), args.value);
|
|
36598
|
-
resultText = "success";
|
|
36599
|
-
break;
|
|
36600
|
-
}
|
|
36601
|
-
case "list_all_variable": {
|
|
36602
|
-
resultText = JSON.stringify([
|
|
36603
|
-
...this.chatContext.getGlobalVariables().keys(),
|
|
36604
|
-
]);
|
|
36605
|
-
break;
|
|
36270
|
+
catch (e) {
|
|
36271
|
+
if (e?.name !== "AbortError") {
|
|
36272
|
+
Log.error("MCP Client, connectSse error:", e);
|
|
36606
36273
|
}
|
|
36274
|
+
throw e;
|
|
36607
36275
|
}
|
|
36608
|
-
return {
|
|
36609
|
-
content: [
|
|
36610
|
-
{
|
|
36611
|
-
type: "text",
|
|
36612
|
-
text: resultText || "",
|
|
36613
|
-
},
|
|
36614
|
-
],
|
|
36615
|
-
};
|
|
36616
|
-
}
|
|
36617
|
-
}
|
|
36618
|
-
|
|
36619
|
-
const CHAT_SYSTEM_TEMPLATE = `
|
|
36620
|
-
You are {{name}}, it is an action-oriented assistant in the browser, a general-purpose intelligent agent running in the browser environment.
|
|
36621
|
-
|
|
36622
|
-
<tool_instructions>
|
|
36623
|
-
General Principles:
|
|
36624
|
-
- Only one tool can be called at a time.
|
|
36625
|
-
- Users may not be able to clearly describe their needs in a single conversation. When needs are ambiguous or lack details, assistant can appropriately initiate follow-up questions before making tool calls. Follow-up rounds should not exceed two rounds.
|
|
36626
|
-
- Users may switch topics multiple times during ongoing conversations. When calling tools, assistant must focus ONLY on the current user question and ignore previous conversation topics unless they are directly related to the current request. Each question should be treated as independent unless explicitly building on previous context.
|
|
36627
|
-
|
|
36628
|
-
For non-chat related tasks issued by users, the following tools need to be called to complete them:
|
|
36629
|
-
<if ${TOOL_NAME$1}Tool>
|
|
36630
|
-
- ${TOOL_NAME$1}: This tool is used to execute tasks, delegate to Javis AI assistant with full computer control.
|
|
36631
|
-
</if>
|
|
36632
|
-
<if ${TOOL_NAME$3}Tool>
|
|
36633
|
-
- ${TOOL_NAME$3}: When a user's query involves finding content in a webpage within a browser tab, extracting webpage content, summarizing webpage content, translating webpage content, read PDF page content, or converting webpage content into a more understandable format, this tool should be used. If the task requires performing actions based on webpage content, deepAction should be used. only needs to provide the required invocation parameters according to the tool's needs; users do not need to manually provide the content of the browser tab.
|
|
36634
|
-
</if>
|
|
36635
|
-
<if ${TOOL_NAME$2}Tool>
|
|
36636
|
-
- ${TOOL_NAME$2}: Search the web for information using search engine API. This tool can perform web searches to find current information, news, articles, and other web content related to the query. It returns search results with titles, descriptions, URLs, and other relevant metadata. Use this tool when you need to find current information from the internet that may not be available in your training data.
|
|
36637
|
-
</if>
|
|
36638
|
-
<if ${TOOL_NAME}Tool>
|
|
36639
|
-
- ${TOOL_NAME}: This tool is used to read output variables from task nodes and write input variables to task nodes, mainly used to retrieve variable results after task execution is completed.
|
|
36640
|
-
</if>
|
|
36641
|
-
</tool_instructions>
|
|
36642
|
-
|
|
36643
|
-
<if memory>
|
|
36644
|
-
The assistant always focuses on the user's current question and will not allow previous conversation turns or irrelevant memory content to interfere with the response to the user's current question. Each question should be handled independently unless it explicitly builds upon prior context.
|
|
36645
|
-
Before responding to user questions, the assistant intelligently analyzes the relevance of memories. When responding, the assistant first determines whether the user's current question is related to information in the retrieved memories, and only incorporates memory data when there is clear contextual relevance. If the user's question is unrelated to the retrieved memories, the assistant will directly respond to the current question without referencing memory content, ensuring the conversation flows naturally.
|
|
36646
|
-
Avoid forcing the use of memories when they are irrelevant to the current context, prioritizing the accuracy and relevance of responses over the inclusion of memories.
|
|
36647
|
-
<retrieved_memories>
|
|
36648
|
-
{{memory}}
|
|
36649
|
-
</retrieved_memories>
|
|
36650
|
-
</if>
|
|
36651
|
-
|
|
36652
|
-
<if tabs>
|
|
36653
|
-
The information about the browser tabs currently open by the user is as follows:
|
|
36654
|
-
<browser_tabs>
|
|
36655
|
-
{{tabs}}
|
|
36656
|
-
</browser_tabs>
|
|
36657
|
-
</if>
|
|
36658
|
-
|
|
36659
|
-
Current datetime: {{datetime}}
|
|
36660
|
-
The output language should match the user's conversation language.
|
|
36661
|
-
`;
|
|
36662
|
-
function getChatSystemPrompt(tools, datetime, memory, tabs) {
|
|
36663
|
-
const systemPrompt = global.prompts.get(GlobalPromptKey.chat_system) || CHAT_SYSTEM_TEMPLATE;
|
|
36664
|
-
const toolVars = {};
|
|
36665
|
-
for (let i = 0; i < tools.length; i++) {
|
|
36666
|
-
toolVars[tools[i].name + "Tool"] = true;
|
|
36667
|
-
}
|
|
36668
|
-
return PromptTemplate.render(systemPrompt, {
|
|
36669
|
-
name: config$1.name,
|
|
36670
|
-
datetime: datetime,
|
|
36671
|
-
memory: memory || "",
|
|
36672
|
-
tabs: getTabsInfo(tabs),
|
|
36673
|
-
...toolVars,
|
|
36674
|
-
}).trim();
|
|
36675
|
-
}
|
|
36676
|
-
function getTabsInfo(tabs) {
|
|
36677
|
-
if (!tabs || tabs.length == 0) {
|
|
36678
|
-
return "Empty";
|
|
36679
36276
|
}
|
|
36680
|
-
|
|
36681
|
-
|
|
36682
|
-
|
|
36683
|
-
title: sub(tab.title, 50),
|
|
36684
|
-
url: sub(tab.url, 300),
|
|
36685
|
-
active: tab.active,
|
|
36686
|
-
lastAccessed: tab.lastAccessed,
|
|
36687
|
-
};
|
|
36688
|
-
}), null, 2);
|
|
36689
|
-
}
|
|
36690
|
-
|
|
36691
|
-
class ChatAgent {
|
|
36692
|
-
constructor(config, chatId = uuidv4(), memory, tools) {
|
|
36693
|
-
this.tools = tools ?? [];
|
|
36694
|
-
this.memory = memory ?? new EkoMemory();
|
|
36695
|
-
this.chatContext = new ChatContext(chatId, config);
|
|
36696
|
-
global.chatMap.set(chatId, this.chatContext);
|
|
36697
|
-
}
|
|
36698
|
-
async chat(params) {
|
|
36699
|
-
return this.doChat(params, false);
|
|
36700
|
-
}
|
|
36701
|
-
async doChat(params, segmentedExecution) {
|
|
36702
|
-
const runStartTime = Date.now();
|
|
36703
|
-
let reactLoopNum = 0;
|
|
36704
|
-
let errorInfo = null;
|
|
36705
|
-
try {
|
|
36706
|
-
if (params.callback?.chatCallback) {
|
|
36707
|
-
await params.callback.chatCallback.onMessage({
|
|
36708
|
-
streamType: "chat",
|
|
36709
|
-
chatId: this.chatContext.getChatId(),
|
|
36710
|
-
messageId: params.messageId,
|
|
36711
|
-
type: "chat_start",
|
|
36712
|
-
});
|
|
36713
|
-
}
|
|
36714
|
-
const chatTools = mergeTools(this.buildInnerTools(params), this.tools);
|
|
36715
|
-
await this.buildSystemPrompt(params, chatTools);
|
|
36716
|
-
await this.addUserMessage(params.messageId, params.user);
|
|
36717
|
-
const config = this.chatContext.getConfig();
|
|
36718
|
-
const rlm = new RetryLanguageModel(config.llms, config.chatLlms);
|
|
36719
|
-
for (; reactLoopNum < 15; reactLoopNum++) {
|
|
36720
|
-
const messages = this.memory.buildMessages();
|
|
36721
|
-
const results = await callChatLLM(params.messageId, this.chatContext, rlm, messages, convertTools(chatTools), undefined, 0, params.callback, params.signal);
|
|
36722
|
-
const finalResult = await this.handleCallResult(params.messageId, chatTools, results, params.callback);
|
|
36723
|
-
if (finalResult) {
|
|
36724
|
-
return finalResult;
|
|
36725
|
-
}
|
|
36726
|
-
if (params.signal?.aborted) {
|
|
36727
|
-
const error = new Error("Operation was interrupted");
|
|
36728
|
-
error.name = "AbortError";
|
|
36729
|
-
throw error;
|
|
36730
|
-
}
|
|
36731
|
-
}
|
|
36732
|
-
reactLoopNum--;
|
|
36733
|
-
return "Unfinished";
|
|
36277
|
+
handleError(method, message) {
|
|
36278
|
+
if (!message) {
|
|
36279
|
+
throw new Error(`MCP ${method} error: no response`);
|
|
36734
36280
|
}
|
|
36735
|
-
|
|
36736
|
-
Log.error(
|
|
36737
|
-
|
|
36738
|
-
|
|
36281
|
+
if (message?.error) {
|
|
36282
|
+
Log.error(`MCP ${method} error: ` + message.error);
|
|
36283
|
+
throw new Error(`MCP ${method} error: ` +
|
|
36284
|
+
(typeof message.error === "string"
|
|
36285
|
+
? message.error
|
|
36286
|
+
: message.error.message));
|
|
36287
|
+
}
|
|
36288
|
+
if (message.result?.isError == true) {
|
|
36289
|
+
if (message.result.content) {
|
|
36290
|
+
throw new Error(`MCP ${method} error: ` +
|
|
36291
|
+
(typeof message.result.content === "string"
|
|
36292
|
+
? message.result.content
|
|
36293
|
+
: message.result.content[0].text));
|
|
36739
36294
|
}
|
|
36740
36295
|
else {
|
|
36741
|
-
|
|
36742
|
-
}
|
|
36743
|
-
return errorInfo;
|
|
36744
|
-
}
|
|
36745
|
-
finally {
|
|
36746
|
-
if (params.callback?.chatCallback) {
|
|
36747
|
-
await params.callback.chatCallback.onMessage({
|
|
36748
|
-
streamType: "chat",
|
|
36749
|
-
chatId: this.chatContext.getChatId(),
|
|
36750
|
-
messageId: params.messageId,
|
|
36751
|
-
type: "chat_end",
|
|
36752
|
-
error: errorInfo,
|
|
36753
|
-
duration: Date.now() - runStartTime,
|
|
36754
|
-
reactLoopNum: reactLoopNum + 1,
|
|
36755
|
-
});
|
|
36296
|
+
throw new Error(`MCP ${method} error: ` + JSON.stringify(message.result));
|
|
36756
36297
|
}
|
|
36757
36298
|
}
|
|
36758
36299
|
}
|
|
36759
|
-
|
|
36760
|
-
|
|
36761
|
-
|
|
36762
|
-
|
|
36763
|
-
|
|
36764
|
-
|
|
36765
|
-
|
|
36766
|
-
|
|
36767
|
-
|
|
36300
|
+
parseChunk(chunk) {
|
|
36301
|
+
const lines = chunk.split("\n");
|
|
36302
|
+
const chunk_obj = {};
|
|
36303
|
+
for (let j = 0; j < lines.length; j++) {
|
|
36304
|
+
const line = lines[j];
|
|
36305
|
+
if (line.startsWith("id:")) {
|
|
36306
|
+
chunk_obj["id"] = line.substring(3).trim();
|
|
36307
|
+
}
|
|
36308
|
+
else if (line.startsWith("event:")) {
|
|
36309
|
+
chunk_obj["event"] = line.substring(6).trim();
|
|
36310
|
+
}
|
|
36311
|
+
else if (line.startsWith("data:")) {
|
|
36312
|
+
chunk_obj["data"] = line.substring(5).trim();
|
|
36313
|
+
}
|
|
36314
|
+
else {
|
|
36315
|
+
const idx = line.indexOf(":");
|
|
36316
|
+
if (idx > -1) {
|
|
36317
|
+
chunk_obj[line.substring(0, idx)] = line.substring(idx + 1).trim();
|
|
36318
|
+
}
|
|
36768
36319
|
}
|
|
36769
36320
|
}
|
|
36321
|
+
return chunk_obj;
|
|
36770
36322
|
}
|
|
36771
|
-
|
|
36772
|
-
|
|
36773
|
-
|
|
36774
|
-
|
|
36775
|
-
|
|
36776
|
-
|
|
36777
|
-
|
|
36778
|
-
|
|
36779
|
-
|
|
36780
|
-
|
|
36323
|
+
}
|
|
36324
|
+
|
|
36325
|
+
async function callChatLLM(messageId, chatContext, rlm, messages, tools, toolChoice, retryNum = 0, callback, signal) {
|
|
36326
|
+
const streamCallback = callback?.chatCallback || {
|
|
36327
|
+
onMessage: async () => { },
|
|
36328
|
+
};
|
|
36329
|
+
const request = {
|
|
36330
|
+
tools: tools,
|
|
36331
|
+
toolChoice,
|
|
36332
|
+
messages: messages,
|
|
36333
|
+
abortSignal: signal,
|
|
36334
|
+
};
|
|
36335
|
+
let streamText = "";
|
|
36336
|
+
let thinkText = "";
|
|
36337
|
+
let toolArgsText = "";
|
|
36338
|
+
let textStreamId = uuidv4();
|
|
36339
|
+
let thinkStreamId = uuidv4();
|
|
36340
|
+
let textStreamDone = false;
|
|
36341
|
+
const toolParts = [];
|
|
36342
|
+
let reader = null;
|
|
36343
|
+
try {
|
|
36344
|
+
const result = await rlm.callStream(request);
|
|
36345
|
+
reader = result.stream.getReader();
|
|
36346
|
+
let toolPart = null;
|
|
36347
|
+
while (true) {
|
|
36348
|
+
const { done, value } = await reader.read();
|
|
36349
|
+
if (done) {
|
|
36350
|
+
break;
|
|
36351
|
+
}
|
|
36352
|
+
const chunk = value;
|
|
36353
|
+
switch (chunk.type) {
|
|
36354
|
+
case "text-start": {
|
|
36355
|
+
textStreamId = uuidv4();
|
|
36356
|
+
break;
|
|
36357
|
+
}
|
|
36358
|
+
case "text-delta": {
|
|
36359
|
+
if (toolPart && !chunk.delta) {
|
|
36360
|
+
continue;
|
|
36361
|
+
}
|
|
36362
|
+
streamText += chunk.delta || "";
|
|
36363
|
+
await streamCallback.onMessage({
|
|
36364
|
+
streamType: "chat",
|
|
36365
|
+
chatId: chatContext.getChatId(),
|
|
36366
|
+
messageId,
|
|
36367
|
+
type: "text",
|
|
36368
|
+
streamId: textStreamId,
|
|
36369
|
+
streamDone: false,
|
|
36370
|
+
text: streamText,
|
|
36371
|
+
});
|
|
36372
|
+
if (toolPart) {
|
|
36373
|
+
await streamCallback.onMessage({
|
|
36374
|
+
streamType: "chat",
|
|
36375
|
+
chatId: chatContext.getChatId(),
|
|
36376
|
+
messageId,
|
|
36377
|
+
type: "tool_use",
|
|
36378
|
+
toolCallId: toolPart.toolCallId,
|
|
36379
|
+
toolName: toolPart.toolName,
|
|
36380
|
+
params: toolPart.input || {},
|
|
36381
|
+
});
|
|
36382
|
+
toolPart = null;
|
|
36383
|
+
}
|
|
36384
|
+
break;
|
|
36385
|
+
}
|
|
36386
|
+
case "text-end": {
|
|
36387
|
+
textStreamDone = true;
|
|
36388
|
+
if (streamText) {
|
|
36389
|
+
await streamCallback.onMessage({
|
|
36390
|
+
streamType: "chat",
|
|
36391
|
+
chatId: chatContext.getChatId(),
|
|
36392
|
+
messageId,
|
|
36393
|
+
type: "text",
|
|
36394
|
+
streamId: textStreamId,
|
|
36395
|
+
streamDone: true,
|
|
36396
|
+
text: streamText,
|
|
36397
|
+
});
|
|
36398
|
+
}
|
|
36399
|
+
break;
|
|
36400
|
+
}
|
|
36401
|
+
case "reasoning-start": {
|
|
36402
|
+
thinkStreamId = uuidv4();
|
|
36403
|
+
break;
|
|
36404
|
+
}
|
|
36405
|
+
case "reasoning-delta": {
|
|
36406
|
+
thinkText += chunk.delta || "";
|
|
36407
|
+
await streamCallback.onMessage({
|
|
36408
|
+
streamType: "chat",
|
|
36409
|
+
chatId: chatContext.getChatId(),
|
|
36410
|
+
messageId,
|
|
36411
|
+
type: "thinking",
|
|
36412
|
+
streamId: thinkStreamId,
|
|
36413
|
+
streamDone: false,
|
|
36414
|
+
text: thinkText,
|
|
36415
|
+
});
|
|
36416
|
+
break;
|
|
36417
|
+
}
|
|
36418
|
+
case "reasoning-end": {
|
|
36419
|
+
if (thinkText) {
|
|
36420
|
+
await streamCallback.onMessage({
|
|
36421
|
+
streamType: "chat",
|
|
36422
|
+
chatId: chatContext.getChatId(),
|
|
36423
|
+
messageId,
|
|
36424
|
+
type: "thinking",
|
|
36425
|
+
streamId: thinkStreamId,
|
|
36426
|
+
streamDone: true,
|
|
36427
|
+
text: thinkText,
|
|
36428
|
+
});
|
|
36429
|
+
}
|
|
36430
|
+
break;
|
|
36431
|
+
}
|
|
36432
|
+
case "tool-input-start": {
|
|
36433
|
+
if (toolPart && toolPart.toolCallId == chunk.id) {
|
|
36434
|
+
toolPart.toolName = chunk.toolName;
|
|
36435
|
+
}
|
|
36436
|
+
else {
|
|
36437
|
+
toolPart = {
|
|
36438
|
+
type: "tool-call",
|
|
36439
|
+
toolCallId: chunk.id,
|
|
36440
|
+
toolName: chunk.toolName,
|
|
36441
|
+
input: {},
|
|
36442
|
+
};
|
|
36443
|
+
toolParts.push(toolPart);
|
|
36444
|
+
}
|
|
36445
|
+
break;
|
|
36446
|
+
}
|
|
36447
|
+
case "tool-input-delta": {
|
|
36448
|
+
if (!textStreamDone) {
|
|
36449
|
+
textStreamDone = true;
|
|
36450
|
+
await streamCallback.onMessage({
|
|
36451
|
+
streamType: "chat",
|
|
36452
|
+
chatId: chatContext.getChatId(),
|
|
36453
|
+
messageId,
|
|
36454
|
+
type: "text",
|
|
36455
|
+
streamId: textStreamId,
|
|
36456
|
+
streamDone: true,
|
|
36457
|
+
text: streamText,
|
|
36458
|
+
});
|
|
36459
|
+
}
|
|
36460
|
+
toolArgsText += chunk.delta || "";
|
|
36461
|
+
await streamCallback.onMessage({
|
|
36462
|
+
streamType: "chat",
|
|
36463
|
+
chatId: chatContext.getChatId(),
|
|
36464
|
+
messageId,
|
|
36465
|
+
type: "tool_streaming",
|
|
36466
|
+
toolCallId: chunk.id,
|
|
36467
|
+
toolName: toolPart?.toolName || "",
|
|
36468
|
+
paramsText: toolArgsText,
|
|
36469
|
+
});
|
|
36470
|
+
break;
|
|
36471
|
+
}
|
|
36472
|
+
case "tool-call": {
|
|
36473
|
+
toolArgsText = "";
|
|
36474
|
+
const args = chunk.input ? JSON.parse(chunk.input) : {};
|
|
36475
|
+
const message = {
|
|
36476
|
+
streamType: "chat",
|
|
36477
|
+
chatId: chatContext.getChatId(),
|
|
36478
|
+
messageId,
|
|
36479
|
+
type: "tool_use",
|
|
36480
|
+
toolCallId: chunk.toolCallId,
|
|
36481
|
+
toolName: chunk.toolName,
|
|
36482
|
+
params: args,
|
|
36483
|
+
};
|
|
36484
|
+
await streamCallback.onMessage(message);
|
|
36485
|
+
if (toolPart == null) {
|
|
36486
|
+
toolParts.push({
|
|
36487
|
+
type: "tool-call",
|
|
36488
|
+
toolCallId: chunk.toolCallId,
|
|
36489
|
+
toolName: chunk.toolName,
|
|
36490
|
+
input: message.params || args,
|
|
36491
|
+
});
|
|
36492
|
+
}
|
|
36493
|
+
else {
|
|
36494
|
+
toolPart.input = message.params || args;
|
|
36495
|
+
toolPart = null;
|
|
36496
|
+
}
|
|
36497
|
+
break;
|
|
36498
|
+
}
|
|
36499
|
+
case "error": {
|
|
36500
|
+
Log.error(`chatLLM error: `, chunk);
|
|
36501
|
+
await streamCallback.onMessage({
|
|
36502
|
+
streamType: "chat",
|
|
36503
|
+
chatId: chatContext.getChatId(),
|
|
36504
|
+
messageId,
|
|
36505
|
+
type: "error",
|
|
36506
|
+
error: chunk.error,
|
|
36507
|
+
});
|
|
36508
|
+
throw new Error("LLM Error: " + chunk.error);
|
|
36509
|
+
}
|
|
36510
|
+
case "finish": {
|
|
36511
|
+
if (!textStreamDone) {
|
|
36512
|
+
textStreamDone = true;
|
|
36513
|
+
await streamCallback.onMessage({
|
|
36514
|
+
streamType: "chat",
|
|
36515
|
+
chatId: chatContext.getChatId(),
|
|
36516
|
+
messageId,
|
|
36517
|
+
type: "text",
|
|
36518
|
+
streamId: textStreamId,
|
|
36519
|
+
streamDone: true,
|
|
36520
|
+
text: streamText,
|
|
36521
|
+
});
|
|
36522
|
+
}
|
|
36523
|
+
if (toolPart) {
|
|
36524
|
+
await streamCallback.onMessage({
|
|
36525
|
+
streamType: "chat",
|
|
36526
|
+
chatId: chatContext.getChatId(),
|
|
36527
|
+
messageId,
|
|
36528
|
+
type: "tool_use",
|
|
36529
|
+
toolCallId: toolPart.toolCallId,
|
|
36530
|
+
toolName: toolPart.toolName,
|
|
36531
|
+
params: toolPart.input || {},
|
|
36532
|
+
});
|
|
36533
|
+
toolPart = null;
|
|
36534
|
+
}
|
|
36535
|
+
await streamCallback.onMessage({
|
|
36536
|
+
streamType: "chat",
|
|
36537
|
+
chatId: chatContext.getChatId(),
|
|
36538
|
+
messageId,
|
|
36539
|
+
type: "finish",
|
|
36540
|
+
finishReason: chunk.finishReason,
|
|
36541
|
+
usage: {
|
|
36542
|
+
promptTokens: chunk.usage.inputTokens || 0,
|
|
36543
|
+
completionTokens: chunk.usage.outputTokens || 0,
|
|
36544
|
+
totalTokens: chunk.usage.totalTokens ||
|
|
36545
|
+
(chunk.usage.inputTokens || 0) +
|
|
36546
|
+
(chunk.usage.outputTokens || 0),
|
|
36547
|
+
},
|
|
36548
|
+
});
|
|
36549
|
+
break;
|
|
36781
36550
|
}
|
|
36782
36551
|
}
|
|
36783
|
-
catch (e) {
|
|
36784
|
-
Log.error("chat service memory recall error: ", e);
|
|
36785
|
-
}
|
|
36786
|
-
}
|
|
36787
|
-
let _tabs = undefined;
|
|
36788
|
-
if (global.browserService) {
|
|
36789
|
-
try {
|
|
36790
|
-
_tabs = await global.browserService.loadTabs(this.chatContext.getChatId());
|
|
36791
|
-
}
|
|
36792
|
-
catch (e) {
|
|
36793
|
-
Log.error("browser service load tabs error: ", e);
|
|
36794
|
-
}
|
|
36795
|
-
}
|
|
36796
|
-
const datetime = params.datetime || new Date().toLocaleString();
|
|
36797
|
-
const systemPrompt = getChatSystemPrompt(chatTools, datetime, _memory, _tabs);
|
|
36798
|
-
this.memory.setSystemPrompt(systemPrompt);
|
|
36799
|
-
}
|
|
36800
|
-
async addUserMessage(messageId, user) {
|
|
36801
|
-
const message = {
|
|
36802
|
-
id: messageId,
|
|
36803
|
-
role: "user",
|
|
36804
|
-
timestamp: Date.now(),
|
|
36805
|
-
content: user,
|
|
36806
|
-
};
|
|
36807
|
-
await this.addMessages([message]);
|
|
36808
|
-
return message;
|
|
36809
|
-
}
|
|
36810
|
-
async addMessages(messages, storage = true) {
|
|
36811
|
-
await this.memory.addMessages(messages);
|
|
36812
|
-
if (storage && global.chatService) {
|
|
36813
|
-
await global.chatService.addMessage(this.chatContext.getChatId(), messages);
|
|
36814
36552
|
}
|
|
36815
36553
|
}
|
|
36816
|
-
|
|
36817
|
-
|
|
36818
|
-
|
|
36819
|
-
|
|
36820
|
-
tools.push(new WebpageQaTool(this.chatContext, params));
|
|
36554
|
+
catch (e) {
|
|
36555
|
+
if (retryNum < config$1.maxRetryNum) {
|
|
36556
|
+
await sleep(200 * (retryNum + 1) * (retryNum + 1));
|
|
36557
|
+
return callChatLLM(messageId, chatContext, rlm, messages, tools, toolChoice, ++retryNum, callback, signal);
|
|
36821
36558
|
}
|
|
36822
|
-
|
|
36823
|
-
tools.push(new TaskVariableStorageTool(this.chatContext, params));
|
|
36824
|
-
return tools;
|
|
36559
|
+
throw e;
|
|
36825
36560
|
}
|
|
36826
|
-
|
|
36827
|
-
|
|
36561
|
+
finally {
|
|
36562
|
+
reader && reader.releaseLock();
|
|
36828
36563
|
}
|
|
36829
|
-
|
|
36830
|
-
|
|
36831
|
-
|
|
36832
|
-
|
|
36833
|
-
|
|
36834
|
-
|
|
36835
|
-
|
|
36836
|
-
|
|
36837
|
-
|
|
36838
|
-
|
|
36839
|
-
|
|
36840
|
-
|
|
36841
|
-
|
|
36842
|
-
|
|
36843
|
-
const args = typeof result.input == "string"
|
|
36844
|
-
? JSON.parse(result.input || "{}")
|
|
36845
|
-
: result.input || {};
|
|
36846
|
-
const tool = getTool(chatTools, result.toolName);
|
|
36847
|
-
if (!tool) {
|
|
36848
|
-
throw new Error(result.toolName + " tool does not exist");
|
|
36849
|
-
}
|
|
36850
|
-
toolResult = await tool.execute(args, result, messageId);
|
|
36851
|
-
}
|
|
36852
|
-
catch (e) {
|
|
36853
|
-
Log.error("tool call error: ", result.toolName, result.input, e);
|
|
36854
|
-
toolResult = {
|
|
36855
|
-
content: [
|
|
36856
|
-
{
|
|
36857
|
-
type: "text",
|
|
36858
|
-
text: e + "",
|
|
36859
|
-
},
|
|
36860
|
-
],
|
|
36861
|
-
isError: true,
|
|
36862
|
-
};
|
|
36863
|
-
}
|
|
36864
|
-
const callback = chatStreamCallback?.chatCallback;
|
|
36865
|
-
if (callback) {
|
|
36866
|
-
await callback.onMessage({
|
|
36867
|
-
streamType: "chat",
|
|
36868
|
-
chatId: this.chatContext.getChatId(),
|
|
36869
|
-
messageId: messageId,
|
|
36870
|
-
type: "tool_result",
|
|
36871
|
-
toolCallId: result.toolCallId,
|
|
36872
|
-
toolName: result.toolName,
|
|
36873
|
-
params: result.input || {},
|
|
36874
|
-
toolResult: toolResult,
|
|
36875
|
-
});
|
|
36876
|
-
}
|
|
36877
|
-
const llmToolResult = convertToolResult(result, toolResult);
|
|
36878
|
-
toolResults.push(llmToolResult);
|
|
36879
|
-
}
|
|
36880
|
-
await this.addMessages([
|
|
36881
|
-
{
|
|
36882
|
-
id: this.memory.genMessageId(),
|
|
36883
|
-
role: "assistant",
|
|
36884
|
-
timestamp: Date.now(),
|
|
36885
|
-
content: convertAssistantToolResults(results),
|
|
36886
|
-
},
|
|
36887
|
-
]);
|
|
36888
|
-
if (toolResults.length > 0) {
|
|
36889
|
-
await this.addMessages([
|
|
36890
|
-
{
|
|
36891
|
-
id: this.memory.genMessageId(),
|
|
36892
|
-
role: "tool",
|
|
36893
|
-
timestamp: Date.now(),
|
|
36894
|
-
content: convertToolResults(toolResults),
|
|
36895
|
-
},
|
|
36896
|
-
]);
|
|
36897
|
-
return null;
|
|
36564
|
+
return streamText
|
|
36565
|
+
? [
|
|
36566
|
+
{ type: "text", text: streamText },
|
|
36567
|
+
...toolParts,
|
|
36568
|
+
]
|
|
36569
|
+
: toolParts;
|
|
36570
|
+
}
|
|
36571
|
+
function convertAssistantToolResults(results) {
|
|
36572
|
+
return results.map((part) => {
|
|
36573
|
+
if (part.type == "text") {
|
|
36574
|
+
return {
|
|
36575
|
+
type: "text",
|
|
36576
|
+
text: part.text,
|
|
36577
|
+
};
|
|
36898
36578
|
}
|
|
36899
|
-
else {
|
|
36900
|
-
return
|
|
36579
|
+
else if (part.type == "tool-call") {
|
|
36580
|
+
return {
|
|
36581
|
+
type: "tool-call",
|
|
36582
|
+
toolCallId: part.toolCallId,
|
|
36583
|
+
toolName: part.toolName,
|
|
36584
|
+
args: (part.input || {}),
|
|
36585
|
+
};
|
|
36901
36586
|
}
|
|
36902
|
-
|
|
36587
|
+
return part;
|
|
36588
|
+
});
|
|
36589
|
+
}
|
|
36590
|
+
function convertToolResults(toolResults) {
|
|
36591
|
+
return toolResults.map((part) => {
|
|
36592
|
+
const output = part.output;
|
|
36593
|
+
return {
|
|
36594
|
+
type: "tool-result",
|
|
36595
|
+
toolCallId: part.toolCallId,
|
|
36596
|
+
toolName: part.toolName,
|
|
36597
|
+
result: output.type == "text" || output.type == "error-text"
|
|
36598
|
+
? output.value
|
|
36599
|
+
: output.type == "json" || output.type == "error-json"
|
|
36600
|
+
? output.value
|
|
36601
|
+
: output.value
|
|
36602
|
+
.map((s) => {
|
|
36603
|
+
if (s.type == "text") {
|
|
36604
|
+
return s.text;
|
|
36605
|
+
}
|
|
36606
|
+
else if (s.type == "media") {
|
|
36607
|
+
return JSON.stringify({
|
|
36608
|
+
data: s.data,
|
|
36609
|
+
mimeType: s.mediaType,
|
|
36610
|
+
});
|
|
36611
|
+
}
|
|
36612
|
+
})
|
|
36613
|
+
.join("\n"),
|
|
36614
|
+
};
|
|
36615
|
+
});
|
|
36903
36616
|
}
|
|
36904
36617
|
|
|
36905
|
-
class
|
|
36906
|
-
constructor(
|
|
36907
|
-
this.
|
|
36908
|
-
this.
|
|
36909
|
-
this.
|
|
36910
|
-
this.
|
|
36911
|
-
this.requestMap = new Map();
|
|
36912
|
-
}
|
|
36913
|
-
async connect(signal) {
|
|
36914
|
-
Log.info("MCP Client, connecting...", this.sseUrl);
|
|
36915
|
-
if (this.sseHandler && this.sseHandler.readyState == 1) {
|
|
36916
|
-
this.sseHandler.close && this.sseHandler.close();
|
|
36917
|
-
this.sseHandler = undefined;
|
|
36918
|
-
}
|
|
36919
|
-
this.pingTimer && clearInterval(this.pingTimer);
|
|
36920
|
-
this.reconnectTimer && clearTimeout(this.reconnectTimer);
|
|
36921
|
-
await new Promise((resolve) => {
|
|
36922
|
-
const timer = setTimeout(resolve, 15000);
|
|
36923
|
-
this.sseHandler = {
|
|
36924
|
-
onopen: () => {
|
|
36925
|
-
Log.info("MCP Client, connection successful", this.sseUrl);
|
|
36926
|
-
clearTimeout(timer);
|
|
36927
|
-
setTimeout(resolve, 200);
|
|
36928
|
-
},
|
|
36929
|
-
onmessage: (data) => this.onmessage(data),
|
|
36930
|
-
onerror: (e) => {
|
|
36931
|
-
Log.error("MCP Client, error: ", e);
|
|
36932
|
-
clearTimeout(timer);
|
|
36933
|
-
if (this.sseHandler?.readyState === 2) {
|
|
36934
|
-
this.pingTimer && clearInterval(this.pingTimer);
|
|
36935
|
-
this.reconnectTimer = setTimeout(() => {
|
|
36936
|
-
this.connect();
|
|
36937
|
-
}, 500);
|
|
36938
|
-
}
|
|
36939
|
-
resolve();
|
|
36940
|
-
},
|
|
36941
|
-
};
|
|
36942
|
-
connectSse(this.sseUrl, this.sseHandler, this.headers, signal);
|
|
36943
|
-
});
|
|
36944
|
-
this.pingTimer = setInterval(() => this.ping(), 10000);
|
|
36618
|
+
class ChatContext {
|
|
36619
|
+
constructor(chatId, config) {
|
|
36620
|
+
this.chatId = chatId;
|
|
36621
|
+
this.config = config;
|
|
36622
|
+
this.ekoMap = new Map();
|
|
36623
|
+
this.globalVariables = new Map();
|
|
36945
36624
|
}
|
|
36946
|
-
|
|
36947
|
-
|
|
36948
|
-
if (data.event == "endpoint") {
|
|
36949
|
-
let uri = data.data;
|
|
36950
|
-
let msgUrl;
|
|
36951
|
-
let idx = this.sseUrl.indexOf("/", 10);
|
|
36952
|
-
if (idx > -1) {
|
|
36953
|
-
msgUrl = this.sseUrl.substring(0, idx) + uri;
|
|
36954
|
-
}
|
|
36955
|
-
else {
|
|
36956
|
-
msgUrl = this.sseUrl + uri;
|
|
36957
|
-
}
|
|
36958
|
-
this.msgUrl = msgUrl;
|
|
36959
|
-
this.initialize();
|
|
36960
|
-
}
|
|
36961
|
-
else if (data.event == "message") {
|
|
36962
|
-
let message = JSON.parse(data.data);
|
|
36963
|
-
let _resolve = this.requestMap.get(message.id);
|
|
36964
|
-
_resolve && _resolve(message);
|
|
36965
|
-
}
|
|
36625
|
+
getChatId() {
|
|
36626
|
+
return this.chatId;
|
|
36966
36627
|
}
|
|
36967
|
-
|
|
36968
|
-
|
|
36969
|
-
protocolVersion: this.protocolVersion,
|
|
36970
|
-
capabilities: {
|
|
36971
|
-
tools: {
|
|
36972
|
-
listChanged: true,
|
|
36973
|
-
},
|
|
36974
|
-
sampling: {},
|
|
36975
|
-
},
|
|
36976
|
-
clientInfo: {
|
|
36977
|
-
name: this.clientName,
|
|
36978
|
-
version: "1.0.0",
|
|
36979
|
-
},
|
|
36980
|
-
});
|
|
36981
|
-
try {
|
|
36982
|
-
await this.request("notifications/initialized", {});
|
|
36983
|
-
}
|
|
36984
|
-
catch (ignored) { }
|
|
36628
|
+
getConfig() {
|
|
36629
|
+
return this.config;
|
|
36985
36630
|
}
|
|
36986
|
-
|
|
36987
|
-
this.
|
|
36631
|
+
addEko(taskId, eko) {
|
|
36632
|
+
this.ekoMap.set(taskId, eko);
|
|
36988
36633
|
}
|
|
36989
|
-
|
|
36990
|
-
|
|
36991
|
-
...param,
|
|
36992
|
-
}, signal);
|
|
36993
|
-
return message.result.tools || [];
|
|
36634
|
+
getEko(taskId) {
|
|
36635
|
+
return this.ekoMap.get(taskId);
|
|
36994
36636
|
}
|
|
36995
|
-
|
|
36996
|
-
|
|
36997
|
-
...param,
|
|
36998
|
-
}, signal);
|
|
36999
|
-
return message.result;
|
|
36637
|
+
getGlobalVariables() {
|
|
36638
|
+
return this.globalVariables;
|
|
37000
36639
|
}
|
|
37001
|
-
|
|
37002
|
-
|
|
36640
|
+
}
|
|
36641
|
+
|
|
36642
|
+
const TOOL_NAME$3 = "webpageQa";
|
|
36643
|
+
const WEBPAGE_QA_PROMPT = `
|
|
36644
|
+
You are a helpful assistant that can answer questions based on the provided webpage context.
|
|
36645
|
+
|
|
36646
|
+
# Webpage Context
|
|
36647
|
+
<webpage_contexts>
|
|
36648
|
+
{{contexts}}
|
|
36649
|
+
</webpage_contexts>
|
|
36650
|
+
|
|
36651
|
+
# User Question
|
|
36652
|
+
<user_question>
|
|
36653
|
+
{{userPrompt}}
|
|
36654
|
+
</user_question>
|
|
36655
|
+
<if language>
|
|
36656
|
+
<language>{{language}}</language>
|
|
36657
|
+
</if>
|
|
36658
|
+
|
|
36659
|
+
Answer user's question based on the webpage context, the answer should be in the same language as the user's question.
|
|
36660
|
+
`;
|
|
36661
|
+
class WebpageQaTool {
|
|
36662
|
+
constructor(chatContext, params) {
|
|
36663
|
+
this.name = TOOL_NAME$3;
|
|
36664
|
+
this.params = params;
|
|
36665
|
+
this.chatContext = chatContext;
|
|
36666
|
+
this.description = `This tool is designed only for handling simple web-related tasks, including summarizing webpage content, extracting data from web pages, translating webpage content, and converting webpage information into more easily understandable forms. It does not interact with or operate web pages. For more complex browser tasks, please use deepAction.It does not perform operations on the webpage itself, but only involves reading the page content. Users do not need to provide the web page content, as the tool can automatically extract the content of the web page based on the tabId to respond.`;
|
|
36667
|
+
this.parameters = {
|
|
36668
|
+
type: "object",
|
|
36669
|
+
properties: {
|
|
36670
|
+
language: {
|
|
36671
|
+
type: "string",
|
|
36672
|
+
description: "User language used, eg: English",
|
|
36673
|
+
},
|
|
36674
|
+
tabIds: {
|
|
36675
|
+
type: "array",
|
|
36676
|
+
description: "The browser tab ids to be used for the QA. When the user says 'left side' or 'current', it means current active tab.",
|
|
36677
|
+
items: { type: "integer" },
|
|
36678
|
+
},
|
|
36679
|
+
},
|
|
36680
|
+
required: ["tabIds", "language"],
|
|
36681
|
+
};
|
|
36682
|
+
}
|
|
36683
|
+
async execute(args, toolCall, messageId) {
|
|
36684
|
+
if (!global.browserService) {
|
|
36685
|
+
return {
|
|
36686
|
+
content: [
|
|
36687
|
+
{
|
|
36688
|
+
type: "text",
|
|
36689
|
+
text: "Error: not implemented",
|
|
36690
|
+
},
|
|
36691
|
+
],
|
|
36692
|
+
};
|
|
36693
|
+
}
|
|
36694
|
+
const tabIds = args.tabIds;
|
|
36695
|
+
const language = args.language;
|
|
36696
|
+
const tabs = await global.browserService.extractPageContents(this.chatContext.getChatId(), tabIds);
|
|
36697
|
+
const chatConfig = this.chatContext.getConfig();
|
|
36698
|
+
const rlm = new RetryLanguageModel(chatConfig.llms, chatConfig.chatLlms);
|
|
36699
|
+
const prompt = PromptTemplate.render(global.prompts.get(GlobalPromptKey.webpage_qa_prompt) ||
|
|
36700
|
+
WEBPAGE_QA_PROMPT, {
|
|
36701
|
+
language: language,
|
|
36702
|
+
userPrompt: this.params.user
|
|
36703
|
+
.map((part) => (part.type == "text" ? part.text : ""))
|
|
36704
|
+
.join("\n")
|
|
36705
|
+
.trim(),
|
|
36706
|
+
contexts: this.buildTabContents(tabs),
|
|
36707
|
+
}).trim();
|
|
36708
|
+
const result = await rlm.callStream({
|
|
36709
|
+
temperature: 0.7,
|
|
36710
|
+
maxOutputTokens: config$1.maxOutputTokens,
|
|
36711
|
+
messages: [{ role: "user", content: [{ type: "text", text: prompt }] }],
|
|
36712
|
+
});
|
|
36713
|
+
const stream = result.stream;
|
|
36714
|
+
const reader = stream.getReader();
|
|
36715
|
+
const streamId = uuidv4();
|
|
36716
|
+
const callback = this.params.callback.chatCallback;
|
|
36717
|
+
let text = "";
|
|
37003
36718
|
try {
|
|
37004
|
-
|
|
37005
|
-
|
|
37006
|
-
|
|
37007
|
-
|
|
37008
|
-
|
|
37009
|
-
|
|
36719
|
+
while (true) {
|
|
36720
|
+
const { done, value } = await reader.read();
|
|
36721
|
+
if (done) {
|
|
36722
|
+
break;
|
|
36723
|
+
}
|
|
36724
|
+
const chunk = value;
|
|
36725
|
+
if (chunk.type == "text-delta") {
|
|
36726
|
+
text += chunk.delta;
|
|
36727
|
+
await callback.onMessage({
|
|
36728
|
+
streamType: "chat",
|
|
36729
|
+
chatId: this.chatContext.getChatId(),
|
|
36730
|
+
messageId: messageId,
|
|
36731
|
+
type: "tool_running",
|
|
36732
|
+
toolName: this.name,
|
|
36733
|
+
toolCallId: toolCall.toolCallId,
|
|
36734
|
+
text: text,
|
|
36735
|
+
streamId: streamId,
|
|
36736
|
+
streamDone: false,
|
|
37010
36737
|
});
|
|
37011
36738
|
}
|
|
37012
|
-
|
|
37013
|
-
|
|
37014
|
-
Log.debug(`MCP Client, ${method}`, id, params);
|
|
37015
|
-
const response = await fetch(this.msgUrl, {
|
|
37016
|
-
method: "POST",
|
|
37017
|
-
headers: {
|
|
37018
|
-
"Content-Type": "application/json",
|
|
37019
|
-
...this.headers,
|
|
37020
|
-
},
|
|
37021
|
-
body: JSON.stringify({
|
|
37022
|
-
jsonrpc: "2.0",
|
|
37023
|
-
id: id,
|
|
37024
|
-
method: method,
|
|
37025
|
-
params: {
|
|
37026
|
-
...params,
|
|
37027
|
-
},
|
|
37028
|
-
}),
|
|
37029
|
-
signal: signal,
|
|
37030
|
-
});
|
|
37031
|
-
const body = await response.text();
|
|
37032
|
-
if (body == "Accepted") {
|
|
37033
|
-
const message = await callback;
|
|
37034
|
-
if (message.error) {
|
|
37035
|
-
Log.error(`MCP ${method} error: ` + message.error);
|
|
37036
|
-
throw new Error(`MCP ${method} error: ` +
|
|
37037
|
-
(typeof message.error === "string"
|
|
37038
|
-
? message.error
|
|
37039
|
-
: message.error.message));
|
|
36739
|
+
else if (chunk.type == "error") {
|
|
36740
|
+
throw new Error(chunk.error);
|
|
37040
36741
|
}
|
|
37041
|
-
if (
|
|
37042
|
-
|
|
37043
|
-
throw new Error(`MCP ${method} error: ` +
|
|
37044
|
-
(typeof message.result.content === "string"
|
|
37045
|
-
? message.result.content
|
|
37046
|
-
: message.result.content[0].text));
|
|
37047
|
-
}
|
|
37048
|
-
else {
|
|
37049
|
-
throw new Error(`MCP ${method} error: ` + JSON.stringify(message.result));
|
|
37050
|
-
}
|
|
36742
|
+
else if (chunk.type == "finish") {
|
|
36743
|
+
break;
|
|
37051
36744
|
}
|
|
37052
|
-
return message;
|
|
37053
|
-
}
|
|
37054
|
-
else {
|
|
37055
|
-
throw new Error(`MCP ${method} error:` + body);
|
|
37056
36745
|
}
|
|
37057
36746
|
}
|
|
37058
36747
|
finally {
|
|
37059
|
-
|
|
37060
|
-
|
|
37061
|
-
|
|
37062
|
-
|
|
37063
|
-
|
|
37064
|
-
|
|
37065
|
-
|
|
37066
|
-
|
|
37067
|
-
|
|
37068
|
-
|
|
37069
|
-
|
|
37070
|
-
await this.request("notifications/cancelled", {
|
|
37071
|
-
requestId: uuidv4(),
|
|
37072
|
-
reason: "User requested cancellation",
|
|
36748
|
+
reader.releaseLock();
|
|
36749
|
+
await callback.onMessage({
|
|
36750
|
+
streamType: "chat",
|
|
36751
|
+
chatId: this.chatContext.getChatId(),
|
|
36752
|
+
messageId: messageId,
|
|
36753
|
+
type: "tool_running",
|
|
36754
|
+
toolName: this.name,
|
|
36755
|
+
toolCallId: toolCall.toolCallId,
|
|
36756
|
+
text: text,
|
|
36757
|
+
streamId: streamId,
|
|
36758
|
+
streamDone: true,
|
|
37073
36759
|
});
|
|
37074
36760
|
}
|
|
37075
|
-
|
|
37076
|
-
|
|
37077
|
-
|
|
37078
|
-
|
|
37079
|
-
|
|
37080
|
-
|
|
37081
|
-
|
|
36761
|
+
return {
|
|
36762
|
+
content: [
|
|
36763
|
+
{
|
|
36764
|
+
type: "text",
|
|
36765
|
+
text: text,
|
|
36766
|
+
},
|
|
36767
|
+
],
|
|
36768
|
+
};
|
|
36769
|
+
}
|
|
36770
|
+
buildTabContents(tabs) {
|
|
36771
|
+
return tabs
|
|
36772
|
+
.map((tab) => {
|
|
36773
|
+
return `<webpage>\nTabId: ${tab.tabId}\nTitle: ${tab.title}\nURL: ${tab.url}\nContent: ${sub(tab.content, 8000)}\n</webpage>`;
|
|
36774
|
+
})
|
|
36775
|
+
.join("\n");
|
|
37082
36776
|
}
|
|
37083
36777
|
}
|
|
37084
|
-
|
|
37085
|
-
|
|
37086
|
-
|
|
37087
|
-
|
|
37088
|
-
|
|
37089
|
-
|
|
37090
|
-
|
|
37091
|
-
|
|
37092
|
-
|
|
37093
|
-
|
|
37094
|
-
|
|
37095
|
-
|
|
37096
|
-
|
|
36778
|
+
|
|
36779
|
+
const TOOL_NAME$2 = "webSearch";
|
|
36780
|
+
class WebSearchTool {
|
|
36781
|
+
constructor(chatContext, params) {
|
|
36782
|
+
this.name = TOOL_NAME$2;
|
|
36783
|
+
this.params = params;
|
|
36784
|
+
this.chatContext = chatContext;
|
|
36785
|
+
this.description = `Search the web for information using search engine API. This tool can perform web searches to find current information, news, articles, and other web content related to the query. It returns search results with titles, descriptions, URLs, and other relevant metadata, use this tool when users need the latest data/information and have NOT specified a particular platform or website, use the search tool.`;
|
|
36786
|
+
this.parameters = {
|
|
36787
|
+
type: "object",
|
|
36788
|
+
properties: {
|
|
36789
|
+
query: {
|
|
36790
|
+
type: "string",
|
|
36791
|
+
description: "The search query to execute. Use specific keywords and phrases for better results.",
|
|
36792
|
+
},
|
|
36793
|
+
language: {
|
|
36794
|
+
type: "string",
|
|
36795
|
+
description: "Language code for search results (e.g., 'en', 'zh', 'ja'). If not specified, will be auto-detected from query.",
|
|
36796
|
+
},
|
|
36797
|
+
count: {
|
|
36798
|
+
type: "integer",
|
|
36799
|
+
description: "Number of search results to return (default: 10, max: 50)",
|
|
36800
|
+
default: 10,
|
|
36801
|
+
minimum: 1,
|
|
36802
|
+
maximum: 50,
|
|
36803
|
+
},
|
|
37097
36804
|
},
|
|
37098
|
-
|
|
37099
|
-
keepalive: true,
|
|
37100
|
-
signal: signal,
|
|
37101
|
-
});
|
|
37102
|
-
const reader = response.body?.getReader();
|
|
37103
|
-
hander.close = () => {
|
|
37104
|
-
controller.abort();
|
|
37105
|
-
hander.readyState = 2;
|
|
37106
|
-
Log.debug("McpClient close abort.", sseUrl);
|
|
36805
|
+
required: ["query", "keywords"],
|
|
37107
36806
|
};
|
|
37108
|
-
let str = "";
|
|
37109
|
-
const decoder = new TextDecoder();
|
|
37110
|
-
hander.readyState = 1;
|
|
37111
|
-
hander.onopen();
|
|
37112
|
-
while (hander.readyState == 1) {
|
|
37113
|
-
const { value, done } = await reader?.read();
|
|
37114
|
-
if (done) {
|
|
37115
|
-
break;
|
|
37116
|
-
}
|
|
37117
|
-
const text = decoder.decode(value);
|
|
37118
|
-
str += text;
|
|
37119
|
-
if (str.indexOf("\n\n") > -1) {
|
|
37120
|
-
const chunks = str.split("\n\n");
|
|
37121
|
-
for (let i = 0; i < chunks.length - 1; i++) {
|
|
37122
|
-
const chunk = chunks[i];
|
|
37123
|
-
const chunkData = parseChunk(chunk);
|
|
37124
|
-
hander.onmessage(chunkData);
|
|
37125
|
-
}
|
|
37126
|
-
str = chunks[chunks.length - 1];
|
|
37127
|
-
}
|
|
37128
|
-
}
|
|
37129
36807
|
}
|
|
37130
|
-
|
|
37131
|
-
if (
|
|
37132
|
-
|
|
37133
|
-
|
|
36808
|
+
async execute(args) {
|
|
36809
|
+
if (!global.chatService) {
|
|
36810
|
+
return {
|
|
36811
|
+
content: [
|
|
36812
|
+
{
|
|
36813
|
+
type: "text",
|
|
36814
|
+
text: "Error: not implemented",
|
|
36815
|
+
},
|
|
36816
|
+
],
|
|
36817
|
+
};
|
|
37134
36818
|
}
|
|
36819
|
+
const query = args.query;
|
|
36820
|
+
const language = args.language;
|
|
36821
|
+
const count = args.count || 10;
|
|
36822
|
+
const results = await global.chatService.websearch(this.chatContext.getChatId(), query, undefined, language, count);
|
|
36823
|
+
return Promise.resolve({
|
|
36824
|
+
content: [
|
|
36825
|
+
{
|
|
36826
|
+
type: "text",
|
|
36827
|
+
text: JSON.stringify(results.map((result) => {
|
|
36828
|
+
return {
|
|
36829
|
+
title: result.title,
|
|
36830
|
+
url: result.url,
|
|
36831
|
+
content: sub(result.content || result.snippet || "", 6000),
|
|
36832
|
+
};
|
|
36833
|
+
})),
|
|
36834
|
+
},
|
|
36835
|
+
],
|
|
36836
|
+
});
|
|
37135
36837
|
}
|
|
37136
|
-
|
|
37137
|
-
|
|
36838
|
+
}
|
|
36839
|
+
|
|
36840
|
+
async function recursiveTextNode(node, callback) {
|
|
36841
|
+
if (node.type === "normal") {
|
|
36842
|
+
callback(node, node);
|
|
36843
|
+
}
|
|
36844
|
+
if (node.type === "forEach") {
|
|
36845
|
+
node.nodes.map((item) => recursiveTextNode(item, callback));
|
|
36846
|
+
}
|
|
36847
|
+
if (node.type === "watch") {
|
|
36848
|
+
node.triggerNodes.map((triggerNode) => recursiveTextNode(triggerNode, callback));
|
|
37138
36849
|
}
|
|
37139
36850
|
}
|
|
37140
|
-
|
|
37141
|
-
|
|
37142
|
-
|
|
37143
|
-
|
|
37144
|
-
|
|
37145
|
-
|
|
37146
|
-
|
|
37147
|
-
|
|
37148
|
-
|
|
37149
|
-
|
|
36851
|
+
|
|
36852
|
+
const TOOL_NAME$1 = "deepAction";
|
|
36853
|
+
const deep_action_description = "Delegate tasks to a Javis AI assistant for completion. This assistant can understand natural language instructions and has full control over both networked computers, browser agent, and multiple specialized agents ({agentNames}). The assistant can autonomously decide to use various software tools, browse the internet to query information, write code, and perform direct operations to complete tasks. He can deliver various digitized outputs (text reports, tables, images, music, videos, websites, deepSearch, programs, etc.) and handle design/analysis tasks. and execute operational tasks (such as batch following bloggers of specific topics on certain websites). For operational tasks, the focus is on completing the process actions rather than delivering final outputs, and the assistant can complete these types of tasks well. It should also be noted that users may actively mention deepsearch, which is also one of the capabilities of this tool. If users mention it, please explicitly tell the assistant to use deepsearch. Supports parallel execution of multiple tasks.";
|
|
36854
|
+
const deep_action_param_task_description = "Task description, please output the user's original instructions without omitting any information from the user's instructions, and use the same language as the user's question.";
|
|
36855
|
+
class DeepActionTool {
|
|
36856
|
+
constructor(chatContext, params) {
|
|
36857
|
+
this.name = TOOL_NAME$1;
|
|
36858
|
+
this.chatContext = chatContext;
|
|
36859
|
+
const agents = this.chatContext.getConfig().agents || [];
|
|
36860
|
+
const agentNames = agents.map((agent) => agent.Name).join(", ");
|
|
36861
|
+
const description = global.prompts.get(GlobalPromptKey.deep_action_description) ||
|
|
36862
|
+
deep_action_description;
|
|
36863
|
+
const paramTaskDescription = global.prompts.get(GlobalPromptKey.deep_action_param_task_description) ||
|
|
36864
|
+
deep_action_param_task_description;
|
|
36865
|
+
this.description = description.replace("{agentNames}", agentNames).trim();
|
|
36866
|
+
this.parameters = {
|
|
36867
|
+
type: "object",
|
|
36868
|
+
properties: {
|
|
36869
|
+
language: {
|
|
36870
|
+
type: "string",
|
|
36871
|
+
description: "User language used, eg: English",
|
|
36872
|
+
},
|
|
36873
|
+
taskDescription: {
|
|
36874
|
+
type: "string",
|
|
36875
|
+
description: paramTaskDescription.trim(),
|
|
36876
|
+
},
|
|
36877
|
+
tabIds: {
|
|
36878
|
+
type: "array",
|
|
36879
|
+
description: "Browser Tab IDs associated with this task, When user says 'left side' or 'current', it means current active tab",
|
|
36880
|
+
items: { type: "integer" },
|
|
36881
|
+
},
|
|
36882
|
+
dependentVariables: {
|
|
36883
|
+
type: "array",
|
|
36884
|
+
description: "The current task relies on variable data from prerequisite execution outputs. Provide the name of the dependent variable.",
|
|
36885
|
+
items: {
|
|
36886
|
+
type: "string",
|
|
36887
|
+
},
|
|
36888
|
+
},
|
|
36889
|
+
},
|
|
36890
|
+
required: ["language", "taskDescription"],
|
|
36891
|
+
};
|
|
36892
|
+
this.params = params;
|
|
36893
|
+
}
|
|
36894
|
+
async execute(args, toolCall, messageId) {
|
|
36895
|
+
const chatId = this.chatContext.getChatId();
|
|
36896
|
+
const language = args.language;
|
|
36897
|
+
const taskDescription = args.taskDescription;
|
|
36898
|
+
const tabIds = args.tabIds;
|
|
36899
|
+
const dependentVariables = args.dependentVariables;
|
|
36900
|
+
const config = this.chatContext.getConfig();
|
|
36901
|
+
const globalVariables = this.chatContext.getGlobalVariables();
|
|
36902
|
+
const eko = new Eko({
|
|
36903
|
+
...config,
|
|
36904
|
+
callback: this.params.callback?.taskCallback,
|
|
36905
|
+
}, chatId);
|
|
36906
|
+
this.chatContext.addEko(messageId, eko);
|
|
36907
|
+
if (this.params.signal) {
|
|
36908
|
+
if (this.params.signal.aborted) {
|
|
36909
|
+
const error = new Error("Operation was interrupted");
|
|
36910
|
+
error.name = "AbortError";
|
|
36911
|
+
throw error;
|
|
36912
|
+
}
|
|
36913
|
+
this.params.signal.addEventListener("abort", () => {
|
|
36914
|
+
eko.abortTask(messageId, "User aborted");
|
|
36915
|
+
});
|
|
37150
36916
|
}
|
|
37151
|
-
|
|
37152
|
-
|
|
36917
|
+
const attachments = this.params.user
|
|
36918
|
+
.filter((part) => part.type === "file")
|
|
36919
|
+
.filter((part) => part.data && part.data.length < 500)
|
|
36920
|
+
.map((part) => {
|
|
36921
|
+
return {
|
|
36922
|
+
file_name: part.filename,
|
|
36923
|
+
file_path: part.filePath,
|
|
36924
|
+
file_url: part.data,
|
|
36925
|
+
};
|
|
36926
|
+
});
|
|
36927
|
+
const taskWebsite = await this.gettaskWebsite(tabIds);
|
|
36928
|
+
const workflow = await eko.generate(taskDescription, messageId, {
|
|
36929
|
+
...globalVariables,
|
|
36930
|
+
tabIds: tabIds,
|
|
36931
|
+
language: language,
|
|
36932
|
+
attachments: attachments,
|
|
36933
|
+
taskWebsite: taskWebsite,
|
|
36934
|
+
dependentVariables: dependentVariables,
|
|
36935
|
+
datetime: this.params.datetime || new Date().toLocaleString(),
|
|
36936
|
+
});
|
|
36937
|
+
const context = eko.getTask(messageId);
|
|
36938
|
+
console.log("==> workflow", workflow);
|
|
36939
|
+
const result = await eko.execute(messageId);
|
|
36940
|
+
const variableNames = [];
|
|
36941
|
+
if (context.variables && context.variables.size > 0) {
|
|
36942
|
+
workflow.agents
|
|
36943
|
+
.map((agent) => agent.nodes)
|
|
36944
|
+
.flat()
|
|
36945
|
+
.forEach((node) => {
|
|
36946
|
+
recursiveTextNode(node, async (textNode) => {
|
|
36947
|
+
if (textNode.output) {
|
|
36948
|
+
variableNames.push(textNode.output);
|
|
36949
|
+
globalVariables.set(textNode.output, context.variables.get(textNode.output));
|
|
36950
|
+
}
|
|
36951
|
+
});
|
|
36952
|
+
});
|
|
37153
36953
|
}
|
|
37154
|
-
|
|
37155
|
-
|
|
37156
|
-
|
|
37157
|
-
|
|
37158
|
-
|
|
36954
|
+
return {
|
|
36955
|
+
content: [
|
|
36956
|
+
{
|
|
36957
|
+
type: "text",
|
|
36958
|
+
text: JSON.stringify({
|
|
36959
|
+
taskPlan: workflow.xml,
|
|
36960
|
+
subAgents: context.chain.agents.map((agent) => {
|
|
36961
|
+
return {
|
|
36962
|
+
agent: agent.agent.name,
|
|
36963
|
+
subTask: agent.agent.task,
|
|
36964
|
+
agentResult: sub(agent.agentResult || "", 800, true),
|
|
36965
|
+
};
|
|
36966
|
+
}),
|
|
36967
|
+
variables: variableNames,
|
|
36968
|
+
taskResult: result.result,
|
|
36969
|
+
success: result.success,
|
|
36970
|
+
}),
|
|
36971
|
+
},
|
|
36972
|
+
],
|
|
36973
|
+
};
|
|
36974
|
+
}
|
|
36975
|
+
async gettaskWebsite(tabIds) {
|
|
36976
|
+
if (!global.browserService) {
|
|
36977
|
+
return [];
|
|
37159
36978
|
}
|
|
36979
|
+
const tabs = await global.browserService.loadTabs(this.chatContext.getChatId(), tabIds);
|
|
36980
|
+
return tabs.map((tab) => {
|
|
36981
|
+
return {
|
|
36982
|
+
tabId: tab.tabId,
|
|
36983
|
+
title: tab.title,
|
|
36984
|
+
url: sub(tab.url, 300),
|
|
36985
|
+
};
|
|
36986
|
+
});
|
|
37160
36987
|
}
|
|
37161
|
-
return chunk_obj;
|
|
37162
36988
|
}
|
|
37163
36989
|
|
|
37164
|
-
|
|
37165
|
-
|
|
37166
|
-
|
|
37167
|
-
this.
|
|
37168
|
-
this.
|
|
37169
|
-
this.
|
|
37170
|
-
this.
|
|
37171
|
-
|
|
37172
|
-
|
|
37173
|
-
|
|
37174
|
-
|
|
37175
|
-
|
|
37176
|
-
|
|
37177
|
-
|
|
37178
|
-
|
|
37179
|
-
|
|
36990
|
+
const TOOL_NAME = "taskVariableStorage";
|
|
36991
|
+
class TaskVariableStorageTool {
|
|
36992
|
+
constructor(chatContext, params) {
|
|
36993
|
+
this.name = TOOL_NAME;
|
|
36994
|
+
this.params = params;
|
|
36995
|
+
this.chatContext = chatContext;
|
|
36996
|
+
this.description = `Used for storing, reading, and retrieving variable data, and maintaining input/output variables in task nodes.`;
|
|
36997
|
+
this.parameters = {
|
|
36998
|
+
type: "object",
|
|
36999
|
+
properties: {
|
|
37000
|
+
operation: {
|
|
37001
|
+
type: "string",
|
|
37002
|
+
description: "variable storage operation type.",
|
|
37003
|
+
enum: ["read_variable", "write_variable", "list_all_variable"],
|
|
37004
|
+
},
|
|
37005
|
+
name: {
|
|
37006
|
+
type: "string",
|
|
37007
|
+
description: "variable name, required when reading and writing variables, If reading variables, it supports reading multiple variables separated by commas.",
|
|
37008
|
+
},
|
|
37009
|
+
value: {
|
|
37010
|
+
type: "string",
|
|
37011
|
+
description: "variable value, required when writing variables",
|
|
37180
37012
|
},
|
|
37181
|
-
sampling: {},
|
|
37182
|
-
},
|
|
37183
|
-
clientInfo: {
|
|
37184
|
-
name: this.clientName,
|
|
37185
|
-
version: "1.0.0",
|
|
37186
37013
|
},
|
|
37187
|
-
|
|
37188
|
-
|
|
37189
|
-
|
|
37190
|
-
|
|
37014
|
+
required: ["operation"],
|
|
37015
|
+
};
|
|
37016
|
+
}
|
|
37017
|
+
async execute(args) {
|
|
37018
|
+
let operation = args.operation;
|
|
37019
|
+
let resultText = "";
|
|
37020
|
+
switch (operation) {
|
|
37021
|
+
case "read_variable": {
|
|
37022
|
+
if (!args.name) {
|
|
37023
|
+
resultText = "Error: name is required";
|
|
37024
|
+
}
|
|
37025
|
+
else {
|
|
37026
|
+
let result = {};
|
|
37027
|
+
let name = args.name;
|
|
37028
|
+
let keys = name.split(",");
|
|
37029
|
+
for (let i = 0; i < keys.length; i++) {
|
|
37030
|
+
let key = keys[i].trim();
|
|
37031
|
+
let value = this.chatContext.getGlobalVariables().get(key);
|
|
37032
|
+
result[key] = value;
|
|
37033
|
+
}
|
|
37034
|
+
resultText = JSON.stringify(result);
|
|
37035
|
+
}
|
|
37036
|
+
break;
|
|
37037
|
+
}
|
|
37038
|
+
case "write_variable": {
|
|
37039
|
+
if (!args.name) {
|
|
37040
|
+
resultText = "Error: name is required";
|
|
37041
|
+
break;
|
|
37042
|
+
}
|
|
37043
|
+
if (args.value == undefined) {
|
|
37044
|
+
resultText = "Error: value is required";
|
|
37045
|
+
break;
|
|
37046
|
+
}
|
|
37047
|
+
let key = args.name;
|
|
37048
|
+
this.chatContext.getGlobalVariables().set(key.trim(), args.value);
|
|
37049
|
+
resultText = "success";
|
|
37050
|
+
break;
|
|
37051
|
+
}
|
|
37052
|
+
case "list_all_variable": {
|
|
37053
|
+
resultText = JSON.stringify([
|
|
37054
|
+
...this.chatContext.getGlobalVariables().keys(),
|
|
37055
|
+
]);
|
|
37056
|
+
break;
|
|
37191
37057
|
}
|
|
37192
|
-
catch (ignored) { }
|
|
37193
37058
|
}
|
|
37194
|
-
|
|
37195
|
-
|
|
37196
|
-
|
|
37197
|
-
|
|
37198
|
-
|
|
37199
|
-
|
|
37200
|
-
|
|
37059
|
+
return {
|
|
37060
|
+
content: [
|
|
37061
|
+
{
|
|
37062
|
+
type: "text",
|
|
37063
|
+
text: resultText || "",
|
|
37064
|
+
},
|
|
37065
|
+
],
|
|
37066
|
+
};
|
|
37201
37067
|
}
|
|
37202
|
-
|
|
37203
|
-
|
|
37204
|
-
|
|
37205
|
-
|
|
37206
|
-
|
|
37068
|
+
}
|
|
37069
|
+
|
|
37070
|
+
const CHAT_SYSTEM_TEMPLATE = `
|
|
37071
|
+
You are {{name}}, it is an action-oriented assistant in the browser, a general-purpose intelligent agent running in the browser environment.
|
|
37072
|
+
|
|
37073
|
+
<tool_instructions>
|
|
37074
|
+
General Principles:
|
|
37075
|
+
- Only one tool can be called at a time.
|
|
37076
|
+
- Users may not be able to clearly describe their needs in a single conversation. When needs are ambiguous or lack details, assistant can appropriately initiate follow-up questions before making tool calls. Follow-up rounds should not exceed two rounds.
|
|
37077
|
+
- Users may switch topics multiple times during ongoing conversations. When calling tools, assistant must focus ONLY on the current user question and ignore previous conversation topics unless they are directly related to the current request. Each question should be treated as independent unless explicitly building on previous context.
|
|
37078
|
+
|
|
37079
|
+
For non-chat related tasks issued by users, the following tools need to be called to complete them:
|
|
37080
|
+
<if ${TOOL_NAME$1}Tool>
|
|
37081
|
+
- ${TOOL_NAME$1}: This tool is used to execute tasks, delegate to Javis AI assistant with full computer control.
|
|
37082
|
+
</if>
|
|
37083
|
+
<if ${TOOL_NAME$3}Tool>
|
|
37084
|
+
- ${TOOL_NAME$3}: When a user's query involves finding content in a webpage within a browser tab, extracting webpage content, summarizing webpage content, translating webpage content, read PDF page content, or converting webpage content into a more understandable format, this tool should be used. If the task requires performing actions based on webpage content, deepAction should be used. only needs to provide the required invocation parameters according to the tool's needs; users do not need to manually provide the content of the browser tab.
|
|
37085
|
+
</if>
|
|
37086
|
+
<if ${TOOL_NAME$2}Tool>
|
|
37087
|
+
- ${TOOL_NAME$2}: Search the web for information using search engine API. This tool can perform web searches to find current information, news, articles, and other web content related to the query. It returns search results with titles, descriptions, URLs, and other relevant metadata. Use this tool when you need to find current information from the internet that may not be available in your training data.
|
|
37088
|
+
</if>
|
|
37089
|
+
<if ${TOOL_NAME}Tool>
|
|
37090
|
+
- ${TOOL_NAME}: This tool is used to read output variables from task nodes and write input variables to task nodes, mainly used to retrieve variable results after task execution is completed.
|
|
37091
|
+
</if>
|
|
37092
|
+
</tool_instructions>
|
|
37093
|
+
|
|
37094
|
+
<if memory>
|
|
37095
|
+
The assistant always focuses on the user's current question and will not allow previous conversation turns or irrelevant memory content to interfere with the response to the user's current question. Each question should be handled independently unless it explicitly builds upon prior context.
|
|
37096
|
+
Before responding to user questions, the assistant intelligently analyzes the relevance of memories. When responding, the assistant first determines whether the user's current question is related to information in the retrieved memories, and only incorporates memory data when there is clear contextual relevance. If the user's question is unrelated to the retrieved memories, the assistant will directly respond to the current question without referencing memory content, ensuring the conversation flows naturally.
|
|
37097
|
+
Avoid forcing the use of memories when they are irrelevant to the current context, prioritizing the accuracy and relevance of responses over the inclusion of memories.
|
|
37098
|
+
<retrieved_memories>
|
|
37099
|
+
{{memory}}
|
|
37100
|
+
</retrieved_memories>
|
|
37101
|
+
</if>
|
|
37102
|
+
|
|
37103
|
+
<if tabs>
|
|
37104
|
+
The information about the browser tabs currently open by the user is as follows:
|
|
37105
|
+
<browser_tabs>
|
|
37106
|
+
{{tabs}}
|
|
37107
|
+
</browser_tabs>
|
|
37108
|
+
</if>
|
|
37109
|
+
|
|
37110
|
+
Current datetime: {{datetime}}
|
|
37111
|
+
The output language should match the user's conversation language.
|
|
37112
|
+
`;
|
|
37113
|
+
function getChatSystemPrompt(tools, datetime, memory, tabs) {
|
|
37114
|
+
const systemPrompt = global.prompts.get(GlobalPromptKey.chat_system) || CHAT_SYSTEM_TEMPLATE;
|
|
37115
|
+
const toolVars = {};
|
|
37116
|
+
for (let i = 0; i < tools.length; i++) {
|
|
37117
|
+
toolVars[tools[i].name + "Tool"] = true;
|
|
37207
37118
|
}
|
|
37208
|
-
|
|
37209
|
-
|
|
37119
|
+
return PromptTemplate.render(systemPrompt, {
|
|
37120
|
+
name: config$1.name,
|
|
37121
|
+
datetime: datetime,
|
|
37122
|
+
memory: memory || "",
|
|
37123
|
+
tabs: getTabsInfo(tabs),
|
|
37124
|
+
...toolVars,
|
|
37125
|
+
}).trim();
|
|
37126
|
+
}
|
|
37127
|
+
function getTabsInfo(tabs) {
|
|
37128
|
+
if (!tabs || tabs.length == 0) {
|
|
37129
|
+
return "Empty";
|
|
37210
37130
|
}
|
|
37211
|
-
|
|
37212
|
-
|
|
37213
|
-
|
|
37214
|
-
|
|
37215
|
-
|
|
37216
|
-
|
|
37217
|
-
|
|
37218
|
-
|
|
37219
|
-
|
|
37220
|
-
|
|
37221
|
-
|
|
37222
|
-
|
|
37131
|
+
return JSON.stringify(tabs.slice(0, 10).map((tab) => {
|
|
37132
|
+
return {
|
|
37133
|
+
tabId: tab.tabId,
|
|
37134
|
+
title: sub(tab.title, 50),
|
|
37135
|
+
url: sub(tab.url, 300),
|
|
37136
|
+
active: tab.active,
|
|
37137
|
+
lastAccessed: tab.lastAccessed,
|
|
37138
|
+
};
|
|
37139
|
+
}), null, 2);
|
|
37140
|
+
}
|
|
37141
|
+
|
|
37142
|
+
class ChatAgent {
|
|
37143
|
+
constructor(config, chatId = uuidv4(), memory, tools) {
|
|
37144
|
+
this.tools = tools ?? [];
|
|
37145
|
+
this.memory = memory ?? new EkoMemory();
|
|
37146
|
+
this.chatContext = new ChatContext(chatId, config);
|
|
37147
|
+
global.chatMap.set(chatId, this.chatContext);
|
|
37223
37148
|
}
|
|
37224
|
-
async
|
|
37225
|
-
|
|
37226
|
-
|
|
37227
|
-
|
|
37228
|
-
|
|
37229
|
-
|
|
37230
|
-
|
|
37231
|
-
|
|
37232
|
-
|
|
37233
|
-
|
|
37234
|
-
|
|
37235
|
-
|
|
37236
|
-
|
|
37237
|
-
"
|
|
37238
|
-
|
|
37239
|
-
...this.headers,
|
|
37240
|
-
},
|
|
37241
|
-
body: JSON.stringify({
|
|
37242
|
-
jsonrpc: "2.0",
|
|
37243
|
-
id: id,
|
|
37244
|
-
method: method,
|
|
37245
|
-
params: {
|
|
37246
|
-
...params,
|
|
37247
|
-
},
|
|
37248
|
-
}),
|
|
37249
|
-
keepalive: true,
|
|
37250
|
-
signal: signal,
|
|
37251
|
-
});
|
|
37252
|
-
if (method.startsWith("notifications/")) {
|
|
37253
|
-
return;
|
|
37254
|
-
}
|
|
37255
|
-
if (method == "initialize") {
|
|
37256
|
-
this.mcpSessionId =
|
|
37257
|
-
response.headers.get("Mcp-Session-Id") ||
|
|
37258
|
-
response.headers.get("mcp-session-id");
|
|
37149
|
+
async chat(params) {
|
|
37150
|
+
return this.doChat(params, false);
|
|
37151
|
+
}
|
|
37152
|
+
async doChat(params, segmentedExecution) {
|
|
37153
|
+
const runStartTime = Date.now();
|
|
37154
|
+
let reactLoopNum = 0;
|
|
37155
|
+
let errorInfo = null;
|
|
37156
|
+
try {
|
|
37157
|
+
if (params.callback?.chatCallback) {
|
|
37158
|
+
await params.callback.chatCallback.onMessage({
|
|
37159
|
+
streamType: "chat",
|
|
37160
|
+
chatId: this.chatContext.getChatId(),
|
|
37161
|
+
messageId: params.messageId,
|
|
37162
|
+
type: "chat_start",
|
|
37163
|
+
});
|
|
37259
37164
|
}
|
|
37260
|
-
const
|
|
37261
|
-
|
|
37262
|
-
|
|
37263
|
-
|
|
37264
|
-
|
|
37265
|
-
|
|
37266
|
-
|
|
37267
|
-
|
|
37268
|
-
const
|
|
37269
|
-
|
|
37270
|
-
|
|
37271
|
-
if (done) {
|
|
37272
|
-
break;
|
|
37273
|
-
}
|
|
37274
|
-
const text = decoder.decode(value);
|
|
37275
|
-
str += text;
|
|
37276
|
-
if (str.indexOf("\n\n") > -1) {
|
|
37277
|
-
const chunks = str.split("\n\n");
|
|
37278
|
-
for (let i = 0; i < chunks.length - 1; i++) {
|
|
37279
|
-
const chunk = chunks[i];
|
|
37280
|
-
const chunkData = this.parseChunk(chunk);
|
|
37281
|
-
if (chunkData.event == "message") {
|
|
37282
|
-
message = JSON.parse(chunkData.data);
|
|
37283
|
-
if (message.id == id) {
|
|
37284
|
-
return message;
|
|
37285
|
-
}
|
|
37286
|
-
}
|
|
37287
|
-
}
|
|
37288
|
-
str = chunks[chunks.length - 1];
|
|
37289
|
-
}
|
|
37165
|
+
const chatTools = mergeTools(this.buildInnerTools(params), this.tools);
|
|
37166
|
+
await this.buildSystemPrompt(params, chatTools);
|
|
37167
|
+
await this.addUserMessage(params.messageId, params.user);
|
|
37168
|
+
const config = this.chatContext.getConfig();
|
|
37169
|
+
const rlm = new RetryLanguageModel(config.llms, config.chatLlms);
|
|
37170
|
+
for (; reactLoopNum < 15; reactLoopNum++) {
|
|
37171
|
+
const messages = this.memory.buildMessages();
|
|
37172
|
+
const results = await callChatLLM(params.messageId, this.chatContext, rlm, messages, convertTools(chatTools), undefined, 0, params.callback, params.signal);
|
|
37173
|
+
const finalResult = await this.handleCallResult(params.messageId, chatTools, results, params.callback);
|
|
37174
|
+
if (finalResult) {
|
|
37175
|
+
return finalResult;
|
|
37290
37176
|
}
|
|
37291
|
-
|
|
37292
|
-
|
|
37177
|
+
if (params.signal?.aborted) {
|
|
37178
|
+
const error = new Error("Operation was interrupted");
|
|
37179
|
+
error.name = "AbortError";
|
|
37180
|
+
throw error;
|
|
37181
|
+
}
|
|
37182
|
+
}
|
|
37183
|
+
reactLoopNum--;
|
|
37184
|
+
return "Unfinished";
|
|
37185
|
+
}
|
|
37186
|
+
catch (e) {
|
|
37187
|
+
Log.error("chat error: ", e);
|
|
37188
|
+
if (e instanceof Error) {
|
|
37189
|
+
errorInfo = e.name + ": " + e.message;
|
|
37293
37190
|
}
|
|
37294
37191
|
else {
|
|
37295
|
-
|
|
37296
|
-
const message = await response.json();
|
|
37297
|
-
this.handleError(method, message);
|
|
37298
|
-
return message;
|
|
37192
|
+
errorInfo = String(e);
|
|
37299
37193
|
}
|
|
37194
|
+
return errorInfo;
|
|
37300
37195
|
}
|
|
37301
|
-
|
|
37302
|
-
if (
|
|
37303
|
-
|
|
37196
|
+
finally {
|
|
37197
|
+
if (params.callback?.chatCallback) {
|
|
37198
|
+
await params.callback.chatCallback.onMessage({
|
|
37199
|
+
streamType: "chat",
|
|
37200
|
+
chatId: this.chatContext.getChatId(),
|
|
37201
|
+
messageId: params.messageId,
|
|
37202
|
+
type: "chat_end",
|
|
37203
|
+
error: errorInfo,
|
|
37204
|
+
duration: Date.now() - runStartTime,
|
|
37205
|
+
reactLoopNum: reactLoopNum + 1,
|
|
37206
|
+
});
|
|
37304
37207
|
}
|
|
37305
|
-
throw e;
|
|
37306
37208
|
}
|
|
37307
37209
|
}
|
|
37308
|
-
|
|
37309
|
-
if (!
|
|
37310
|
-
|
|
37210
|
+
async initMessages() {
|
|
37211
|
+
if (!global.chatService) {
|
|
37212
|
+
return;
|
|
37311
37213
|
}
|
|
37312
|
-
|
|
37313
|
-
|
|
37314
|
-
|
|
37315
|
-
|
|
37316
|
-
|
|
37317
|
-
|
|
37214
|
+
const messages = this.memory.getMessages();
|
|
37215
|
+
if (messages.length == 0) {
|
|
37216
|
+
const messages = await global.chatService.loadMessages(this.chatContext.getChatId());
|
|
37217
|
+
if (messages && messages.length > 0) {
|
|
37218
|
+
await this.memory.addMessages(messages);
|
|
37219
|
+
}
|
|
37318
37220
|
}
|
|
37319
|
-
|
|
37320
|
-
|
|
37321
|
-
|
|
37322
|
-
|
|
37323
|
-
|
|
37324
|
-
|
|
37221
|
+
}
|
|
37222
|
+
async buildSystemPrompt(params, chatTools) {
|
|
37223
|
+
let _memory = undefined;
|
|
37224
|
+
if (global.chatService) {
|
|
37225
|
+
try {
|
|
37226
|
+
const userPrompt = params.user
|
|
37227
|
+
.map((part) => (part.type == "text" ? part.text : ""))
|
|
37228
|
+
.join("\n")
|
|
37229
|
+
.trim();
|
|
37230
|
+
if (userPrompt) {
|
|
37231
|
+
_memory = await global.chatService.memoryRecall(this.chatContext.getChatId(), userPrompt);
|
|
37232
|
+
}
|
|
37325
37233
|
}
|
|
37326
|
-
|
|
37327
|
-
|
|
37234
|
+
catch (e) {
|
|
37235
|
+
Log.error("chat service memory recall error: ", e);
|
|
37328
37236
|
}
|
|
37329
37237
|
}
|
|
37330
|
-
|
|
37331
|
-
|
|
37332
|
-
|
|
37333
|
-
|
|
37334
|
-
for (let j = 0; j < lines.length; j++) {
|
|
37335
|
-
const line = lines[j];
|
|
37336
|
-
if (line.startsWith("id:")) {
|
|
37337
|
-
chunk_obj["id"] = line.substring(3).trim();
|
|
37238
|
+
let _tabs = undefined;
|
|
37239
|
+
if (global.browserService) {
|
|
37240
|
+
try {
|
|
37241
|
+
_tabs = await global.browserService.loadTabs(this.chatContext.getChatId());
|
|
37338
37242
|
}
|
|
37339
|
-
|
|
37340
|
-
|
|
37243
|
+
catch (e) {
|
|
37244
|
+
Log.error("browser service load tabs error: ", e);
|
|
37341
37245
|
}
|
|
37342
|
-
|
|
37343
|
-
|
|
37246
|
+
}
|
|
37247
|
+
const datetime = params.datetime || new Date().toLocaleString();
|
|
37248
|
+
const systemPrompt = getChatSystemPrompt(chatTools, datetime, _memory, _tabs);
|
|
37249
|
+
this.memory.setSystemPrompt(systemPrompt);
|
|
37250
|
+
}
|
|
37251
|
+
async addUserMessage(messageId, user) {
|
|
37252
|
+
const message = {
|
|
37253
|
+
id: messageId,
|
|
37254
|
+
role: "user",
|
|
37255
|
+
timestamp: Date.now(),
|
|
37256
|
+
content: user,
|
|
37257
|
+
};
|
|
37258
|
+
await this.addMessages([message]);
|
|
37259
|
+
return message;
|
|
37260
|
+
}
|
|
37261
|
+
async addMessages(messages, storage = true) {
|
|
37262
|
+
await this.memory.addMessages(messages);
|
|
37263
|
+
if (storage && global.chatService) {
|
|
37264
|
+
await global.chatService.addMessage(this.chatContext.getChatId(), messages);
|
|
37265
|
+
}
|
|
37266
|
+
}
|
|
37267
|
+
buildInnerTools(params) {
|
|
37268
|
+
const tools = [];
|
|
37269
|
+
tools.push(new DeepActionTool(this.chatContext, params));
|
|
37270
|
+
if (global.browserService) {
|
|
37271
|
+
tools.push(new WebpageQaTool(this.chatContext, params));
|
|
37272
|
+
}
|
|
37273
|
+
tools.push(new WebSearchTool(this.chatContext, params));
|
|
37274
|
+
tools.push(new TaskVariableStorageTool(this.chatContext, params));
|
|
37275
|
+
return tools;
|
|
37276
|
+
}
|
|
37277
|
+
getChatContext() {
|
|
37278
|
+
return this.chatContext;
|
|
37279
|
+
}
|
|
37280
|
+
async handleCallResult(messageId, chatTools, results, chatStreamCallback) {
|
|
37281
|
+
let text = null;
|
|
37282
|
+
const toolResults = [];
|
|
37283
|
+
if (results.length == 0) {
|
|
37284
|
+
return null;
|
|
37285
|
+
}
|
|
37286
|
+
for (let i = 0; i < results.length; i++) {
|
|
37287
|
+
const result = results[i];
|
|
37288
|
+
if (result.type == "text") {
|
|
37289
|
+
text = result.text;
|
|
37290
|
+
continue;
|
|
37344
37291
|
}
|
|
37345
|
-
|
|
37346
|
-
|
|
37347
|
-
|
|
37348
|
-
|
|
37292
|
+
let toolResult;
|
|
37293
|
+
try {
|
|
37294
|
+
const args = typeof result.input == "string"
|
|
37295
|
+
? JSON.parse(result.input || "{}")
|
|
37296
|
+
: result.input || {};
|
|
37297
|
+
const tool = getTool(chatTools, result.toolName);
|
|
37298
|
+
if (!tool) {
|
|
37299
|
+
throw new Error(result.toolName + " tool does not exist");
|
|
37349
37300
|
}
|
|
37301
|
+
toolResult = await tool.execute(args, result, messageId);
|
|
37302
|
+
}
|
|
37303
|
+
catch (e) {
|
|
37304
|
+
Log.error("tool call error: ", result.toolName, result.input, e);
|
|
37305
|
+
toolResult = {
|
|
37306
|
+
content: [
|
|
37307
|
+
{
|
|
37308
|
+
type: "text",
|
|
37309
|
+
text: e + "",
|
|
37310
|
+
},
|
|
37311
|
+
],
|
|
37312
|
+
isError: true,
|
|
37313
|
+
};
|
|
37314
|
+
}
|
|
37315
|
+
const callback = chatStreamCallback?.chatCallback;
|
|
37316
|
+
if (callback) {
|
|
37317
|
+
await callback.onMessage({
|
|
37318
|
+
streamType: "chat",
|
|
37319
|
+
chatId: this.chatContext.getChatId(),
|
|
37320
|
+
messageId: messageId,
|
|
37321
|
+
type: "tool_result",
|
|
37322
|
+
toolCallId: result.toolCallId,
|
|
37323
|
+
toolName: result.toolName,
|
|
37324
|
+
params: result.input || {},
|
|
37325
|
+
toolResult: toolResult,
|
|
37326
|
+
});
|
|
37350
37327
|
}
|
|
37328
|
+
const llmToolResult = convertToolResult(result, toolResult);
|
|
37329
|
+
toolResults.push(llmToolResult);
|
|
37330
|
+
}
|
|
37331
|
+
await this.addMessages([
|
|
37332
|
+
{
|
|
37333
|
+
id: this.memory.genMessageId(),
|
|
37334
|
+
role: "assistant",
|
|
37335
|
+
timestamp: Date.now(),
|
|
37336
|
+
content: convertAssistantToolResults(results),
|
|
37337
|
+
},
|
|
37338
|
+
]);
|
|
37339
|
+
if (toolResults.length > 0) {
|
|
37340
|
+
await this.addMessages([
|
|
37341
|
+
{
|
|
37342
|
+
id: this.memory.genMessageId(),
|
|
37343
|
+
role: "tool",
|
|
37344
|
+
timestamp: Date.now(),
|
|
37345
|
+
content: convertToolResults(toolResults),
|
|
37346
|
+
},
|
|
37347
|
+
]);
|
|
37348
|
+
return null;
|
|
37349
|
+
}
|
|
37350
|
+
else {
|
|
37351
|
+
return text;
|
|
37351
37352
|
}
|
|
37352
|
-
return chunk_obj;
|
|
37353
37353
|
}
|
|
37354
37354
|
}
|
|
37355
37355
|
|
|
@@ -37363,19 +37363,24 @@ exports.Chain = Chain;
|
|
|
37363
37363
|
exports.ChatAgent = ChatAgent;
|
|
37364
37364
|
exports.ChatContext = ChatContext;
|
|
37365
37365
|
exports.Context = TaskContext;
|
|
37366
|
+
exports.DeepActionTool = DeepActionTool;
|
|
37366
37367
|
exports.Eko = Eko;
|
|
37367
37368
|
exports.EkoMemory = EkoMemory;
|
|
37368
37369
|
exports.ForeachTaskTool = ForeachTaskTool;
|
|
37369
37370
|
exports.HumanInteractTool = HumanInteractTool;
|
|
37370
37371
|
exports.Log = Log;
|
|
37371
37372
|
exports.Planner = Planner;
|
|
37373
|
+
exports.PromptTemplate = PromptTemplate;
|
|
37372
37374
|
exports.RetryLanguageModel = RetryLanguageModel;
|
|
37373
37375
|
exports.SimpleHttpMcpClient = SimpleHttpMcpClient;
|
|
37374
37376
|
exports.SimpleSseMcpClient = SimpleSseMcpClient;
|
|
37375
37377
|
exports.TaskContext = TaskContext;
|
|
37376
37378
|
exports.TaskNodeStatusTool = TaskNodeStatusTool;
|
|
37379
|
+
exports.TaskVariableStorageTool = TaskVariableStorageTool;
|
|
37377
37380
|
exports.VariableStorageTool = VariableStorageTool;
|
|
37378
37381
|
exports.WatchTriggerTool = WatchTriggerTool;
|
|
37382
|
+
exports.WebSearchTool = WebSearchTool;
|
|
37383
|
+
exports.WebpageQaTool = WebpageQaTool;
|
|
37379
37384
|
exports.buildAgentTree = buildAgentTree;
|
|
37380
37385
|
exports.buildSimpleAgentWorkflow = buildSimpleAgentWorkflow;
|
|
37381
37386
|
exports.call_timeout = call_timeout;
|