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