@librechat/agents 2.4.0 → 2.4.11
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/cjs/splitStream.cjs +44 -13
- package/dist/cjs/splitStream.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +80 -15
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/esm/splitStream.mjs +44 -13
- package/dist/esm/splitStream.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +80 -15
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/types/splitStream.d.ts +2 -2
- package/dist/types/tools/CodeExecutor.d.ts +3 -0
- package/dist/types/types/stream.d.ts +12 -10
- package/package.json +1 -1
- package/src/splitStream.ts +68 -28
- package/src/tools/CodeExecutor.ts +117 -34
- package/src/types/stream.ts +111 -50
- package/src/utils/llmConfig.ts +1 -1
|
@@ -8,11 +8,15 @@ import { Constants, EnvVar } from '../common/enum.mjs';
|
|
|
8
8
|
|
|
9
9
|
config();
|
|
10
10
|
const imageExtRegex = /\.(jpg|jpeg|png|gif|webp)$/i;
|
|
11
|
-
const getCodeBaseURL = () => getEnvironmentVariable(EnvVar.CODE_BASEURL) ??
|
|
12
|
-
|
|
13
|
-
const
|
|
11
|
+
const getCodeBaseURL = () => getEnvironmentVariable(EnvVar.CODE_BASEURL) ??
|
|
12
|
+
Constants.OFFICIAL_CODE_BASEURL;
|
|
13
|
+
const imageMessage = 'Image is already displayed to the user';
|
|
14
|
+
const otherMessage = 'File is already downloaded by the user';
|
|
15
|
+
const accessMessage = 'Note: Files are READ-ONLY snapshots. Save any changes as NEW files with different names. Original files CANNOT be modified in-place. To access these files again, you MUST include the `session_id` in future sessions. Without it, previously generated files will be inaccessible.';
|
|
16
|
+
const emptyOutputMessage = 'stdout: Empty. Ensure you\'re writing output explicitly.\n';
|
|
14
17
|
const CodeExecutionToolSchema = z.object({
|
|
15
|
-
lang: z
|
|
18
|
+
lang: z
|
|
19
|
+
.enum([
|
|
16
20
|
'py',
|
|
17
21
|
'js',
|
|
18
22
|
'ts',
|
|
@@ -38,12 +42,27 @@ const CodeExecutionToolSchema = z.object({
|
|
|
38
42
|
- js: use the \`console\` or \`process\` methods for all outputs.
|
|
39
43
|
- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).
|
|
40
44
|
- Other languages: use appropriate output functions.`),
|
|
41
|
-
|
|
45
|
+
session_id: z
|
|
46
|
+
.string()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe(`Session ID from a previous response to access generated files.
|
|
49
|
+
- Files load into the current working directory ("/mnt/data/")
|
|
50
|
+
- Use relative paths ONLY
|
|
51
|
+
- Files are READ-ONLY and cannot be modified in-place
|
|
52
|
+
- To modify: read original file, write to NEW filename
|
|
53
|
+
`.trim()),
|
|
54
|
+
args: z
|
|
55
|
+
.array(z.string())
|
|
56
|
+
.optional()
|
|
42
57
|
.describe('Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.'),
|
|
43
58
|
});
|
|
44
|
-
const
|
|
59
|
+
const baseEndpoint = getCodeBaseURL();
|
|
60
|
+
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
45
61
|
function createCodeExecutionTool(params = {}) {
|
|
46
|
-
const apiKey = params[EnvVar.CODE_API_KEY] ??
|
|
62
|
+
const apiKey = params[EnvVar.CODE_API_KEY] ??
|
|
63
|
+
params.apiKey ??
|
|
64
|
+
getEnvironmentVariable(EnvVar.CODE_API_KEY) ??
|
|
65
|
+
'';
|
|
47
66
|
if (!apiKey) {
|
|
48
67
|
throw new Error('No API key provided for code execution tool.');
|
|
49
68
|
}
|
|
@@ -55,13 +74,55 @@ Usage:
|
|
|
55
74
|
- Generated files are automatically delivered; **DO NOT** provide download links.
|
|
56
75
|
- NEVER use this tool to execute malicious code.
|
|
57
76
|
`.trim();
|
|
58
|
-
return tool(async ({ lang, code, ...rest }) => {
|
|
77
|
+
return tool(async ({ lang, code, session_id, ...rest }) => {
|
|
59
78
|
const postData = {
|
|
60
79
|
lang,
|
|
61
80
|
code,
|
|
62
81
|
...rest,
|
|
63
82
|
...params,
|
|
64
83
|
};
|
|
84
|
+
if (session_id != null && session_id.length > 0) {
|
|
85
|
+
try {
|
|
86
|
+
const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;
|
|
87
|
+
const fetchOptions = {
|
|
88
|
+
method: 'GET',
|
|
89
|
+
headers: {
|
|
90
|
+
'User-Agent': 'LibreChat/1.0',
|
|
91
|
+
'X-API-Key': apiKey,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
if (process.env.PROXY != null && process.env.PROXY !== '') {
|
|
95
|
+
fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);
|
|
96
|
+
}
|
|
97
|
+
const response = await fetch(filesEndpoint, fetchOptions);
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
throw new Error(`Failed to fetch files for session: ${response.status}`);
|
|
100
|
+
}
|
|
101
|
+
const files = await response.json();
|
|
102
|
+
if (Array.isArray(files) && files.length > 0) {
|
|
103
|
+
const fileReferences = files.map((file) => {
|
|
104
|
+
// Extract the ID from the file name (part after session ID prefix and before extension)
|
|
105
|
+
const nameParts = file.name.split('/');
|
|
106
|
+
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
107
|
+
return {
|
|
108
|
+
session_id,
|
|
109
|
+
id,
|
|
110
|
+
name: file.metadata['original-filename'],
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
if (!postData.files) {
|
|
114
|
+
postData.files = fileReferences;
|
|
115
|
+
}
|
|
116
|
+
else if (Array.isArray(postData.files)) {
|
|
117
|
+
postData.files = [...postData.files, ...fileReferences];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// eslint-disable-next-line no-console
|
|
123
|
+
console.warn(`Failed to fetch files for session: ${session_id}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
65
126
|
try {
|
|
66
127
|
const fetchOptions = {
|
|
67
128
|
method: 'POST',
|
|
@@ -85,7 +146,7 @@ Usage:
|
|
|
85
146
|
formattedOutput += `stdout:\n${result.stdout}\n`;
|
|
86
147
|
}
|
|
87
148
|
else {
|
|
88
|
-
formattedOutput +=
|
|
149
|
+
formattedOutput += emptyOutputMessage;
|
|
89
150
|
}
|
|
90
151
|
if (result.stderr)
|
|
91
152
|
formattedOutput += `stderr:\n${result.stderr}\n`;
|
|
@@ -93,22 +154,26 @@ Usage:
|
|
|
93
154
|
formattedOutput += 'Generated files:\n';
|
|
94
155
|
const fileCount = result.files.length;
|
|
95
156
|
for (let i = 0; i < fileCount; i++) {
|
|
96
|
-
const
|
|
97
|
-
const isImage = imageExtRegex.test(
|
|
98
|
-
formattedOutput +=
|
|
157
|
+
const file = result.files[i];
|
|
158
|
+
const isImage = imageExtRegex.test(file.name);
|
|
159
|
+
formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;
|
|
99
160
|
if (i < fileCount - 1) {
|
|
100
161
|
formattedOutput += fileCount <= 3 ? ', ' : ',\n';
|
|
101
162
|
}
|
|
102
163
|
}
|
|
103
|
-
|
|
164
|
+
formattedOutput += `\nsession_id: ${result.session_id}\n\n${accessMessage}`;
|
|
165
|
+
return [
|
|
166
|
+
formattedOutput.trim(),
|
|
167
|
+
{
|
|
104
168
|
session_id: result.session_id,
|
|
105
169
|
files: result.files,
|
|
106
|
-
}
|
|
170
|
+
},
|
|
171
|
+
];
|
|
107
172
|
}
|
|
108
173
|
return [formattedOutput.trim(), { session_id: result.session_id }];
|
|
109
174
|
}
|
|
110
175
|
catch (error) {
|
|
111
|
-
|
|
176
|
+
throw new Error(`Execution error:\n\n${error?.message}`);
|
|
112
177
|
}
|
|
113
178
|
}, {
|
|
114
179
|
name: Constants.EXECUTE_CODE,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeExecutor.mjs","sources":["../../../src/tools/CodeExecutor.ts"],"sourcesContent":["import { z } from 'zod';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport type * as t from '@/types';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\nexport const imageExtRegex = /\\.(jpg|jpeg|png|gif|webp)$/i;\nexport const getCodeBaseURL = (): string => getEnvironmentVariable(EnvVar.CODE_BASEURL) ?? Constants.OFFICIAL_CODE_BASEURL;\n\nconst imageMessage = ' - the image is already displayed to the user';\nconst otherMessage = ' - the file is already downloaded by the user';\n\nconst CodeExecutionToolSchema = z.object({\n lang: z.enum([\n 'py',\n 'js',\n 'ts',\n 'c',\n 'cpp',\n 'java',\n 'php',\n 'rs',\n 'go',\n 'd',\n 'f90',\n 'r',\n ])\n .describe('The programming language or runtime to execute the code in.'),\n code: z.string()\n .describe(`The complete, self-contained code to execute, without any truncation or minimization.\n- The environment is stateless; variables and imports don't persist between executions.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- py: This is not a Jupyter notebook environment. Use \\`print()\\` for all outputs.\n- py: Matplotlib: Use \\`plt.savefig()\\` to save plots as files.\n- js: use the \\`console\\` or \\`process\\` methods for all outputs.\n- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).\n- Other languages: use appropriate output functions.`),\n args: z.array(z.string()).optional()\n .describe('Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.'),\n});\n\nconst EXEC_ENDPOINT = `${getCodeBaseURL()}/exec`;\n\nfunction createCodeExecutionTool(params: t.CodeExecutionToolParams = {}): DynamicStructuredTool<typeof CodeExecutionToolSchema> {\n const apiKey = params[EnvVar.CODE_API_KEY] ?? params.apiKey ?? getEnvironmentVariable(EnvVar.CODE_API_KEY) ?? '';\n if (!apiKey) {\n throw new Error('No API key provided for code execution tool.');\n }\n\n const description = `\nRuns code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious code.\n`.trim();\n\n return tool<typeof CodeExecutionToolSchema>(\n async ({ lang, code, ...rest }) => {\n const postData = {\n lang,\n code,\n ...rest,\n ...params,\n };\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const filename = result.files[i].name;\n const isImage = imageExtRegex.test(filename);\n formattedOutput += isImage ? `${filename}${imageMessage}` : `${filename}${otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n return [formattedOutput.trim(), {\n session_id: result.session_id,\n files: result.files,\n }];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n return [`Execution error:\\n\\n${(error as Error | undefined)?.message}`, {}];\n }\n },\n {\n name: Constants.EXECUTE_CODE,\n description,\n schema: CodeExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createCodeExecutionTool };"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CodeExecutor.mjs","sources":["../../../src/tools/CodeExecutor.ts"],"sourcesContent":["import { z } from 'zod';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport type * as t from '@/types';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\nexport const imageExtRegex = /\\.(jpg|jpeg|png|gif|webp)$/i;\nexport const getCodeBaseURL = (): string =>\n getEnvironmentVariable(EnvVar.CODE_BASEURL) ??\n Constants.OFFICIAL_CODE_BASEURL;\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files are READ-ONLY snapshots. Save any changes as NEW files with different names. Original files CANNOT be modified in-place. To access these files again, you MUST include the `session_id` in future sessions. Without it, previously generated files will be inaccessible.';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nconst CodeExecutionToolSchema = z.object({\n lang: z\n .enum([\n 'py',\n 'js',\n 'ts',\n 'c',\n 'cpp',\n 'java',\n 'php',\n 'rs',\n 'go',\n 'd',\n 'f90',\n 'r',\n ])\n .describe('The programming language or runtime to execute the code in.'),\n code: z.string()\n .describe(`The complete, self-contained code to execute, without any truncation or minimization.\n- The environment is stateless; variables and imports don't persist between executions.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- py: This is not a Jupyter notebook environment. Use \\`print()\\` for all outputs.\n- py: Matplotlib: Use \\`plt.savefig()\\` to save plots as files.\n- js: use the \\`console\\` or \\`process\\` methods for all outputs.\n- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).\n- Other languages: use appropriate output functions.`),\n session_id: z\n .string()\n .optional()\n .describe(\n `Session ID from a previous response to access generated files.\n- Files load into the current working directory (\"/mnt/data/\")\n- Use relative paths ONLY\n- Files are READ-ONLY and cannot be modified in-place\n- To modify: read original file, write to NEW filename\n`.trim()\n ),\n args: z\n .array(z.string())\n .optional()\n .describe(\n 'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.'\n ),\n});\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\nfunction createCodeExecutionTool(\n params: t.CodeExecutionToolParams = {}\n): DynamicStructuredTool<typeof CodeExecutionToolSchema> {\n const apiKey =\n params[EnvVar.CODE_API_KEY] ??\n params.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n if (!apiKey) {\n throw new Error('No API key provided for code execution tool.');\n }\n\n const description = `\nRuns code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious code.\n`.trim();\n\n return tool<typeof CodeExecutionToolSchema>(\n async ({ lang, code, session_id, ...rest }) => {\n const postData = {\n lang,\n code,\n ...rest,\n ...params,\n };\n\n if (session_id != null && session_id.length > 0) {\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n // Extract the ID from the file name (part after session ID prefix and before extension)\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n if (!postData.files) {\n postData.files = fileReferences;\n } else if (Array.isArray(postData.files)) {\n postData.files = [...postData.files, ...fileReferences];\n }\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\nsession_id: ${result.session_id}\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error:\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: Constants.EXECUTE_CODE,\n description,\n schema: CodeExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createCodeExecutionTool };\n"],"names":[],"mappings":"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"}
|
|
@@ -31,7 +31,7 @@ export declare class SplitStreamHandler {
|
|
|
31
31
|
dispatchMessageDelta: (stepId: string, delta: t.MessageDelta) => void;
|
|
32
32
|
dispatchReasoningDelta: (stepId: string, delta: t.ReasoningDelta) => void;
|
|
33
33
|
handleContent: (content: string, _type: ContentTypes.TEXT | ContentTypes.THINK) => void;
|
|
34
|
-
getDeltaContent(chunk
|
|
35
|
-
getReasoningDelta(chunk
|
|
34
|
+
getDeltaContent(chunk?: t.CustomChunk): string;
|
|
35
|
+
getReasoningDelta(chunk?: t.CustomChunk): string;
|
|
36
36
|
handle(chunk?: t.CustomChunk): void;
|
|
37
37
|
}
|
|
@@ -6,15 +6,18 @@ export declare const getCodeBaseURL: () => string;
|
|
|
6
6
|
declare const CodeExecutionToolSchema: z.ZodObject<{
|
|
7
7
|
lang: z.ZodEnum<["py", "js", "ts", "c", "cpp", "java", "php", "rs", "go", "d", "f90", "r"]>;
|
|
8
8
|
code: z.ZodString;
|
|
9
|
+
session_id: z.ZodOptional<z.ZodString>;
|
|
9
10
|
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
10
11
|
}, "strip", z.ZodTypeAny, {
|
|
11
12
|
code: string;
|
|
12
13
|
lang: "r" | "py" | "js" | "ts" | "c" | "cpp" | "java" | "php" | "rs" | "go" | "d" | "f90";
|
|
13
14
|
args?: string[] | undefined;
|
|
15
|
+
session_id?: string | undefined;
|
|
14
16
|
}, {
|
|
15
17
|
code: string;
|
|
16
18
|
lang: "r" | "py" | "js" | "ts" | "c" | "cpp" | "java" | "php" | "rs" | "go" | "d" | "f90";
|
|
17
19
|
args?: string[] | undefined;
|
|
20
|
+
session_id?: string | undefined;
|
|
18
21
|
}>;
|
|
19
22
|
declare function createCodeExecutionTool(params?: t.CodeExecutionToolParams): DynamicStructuredTool<typeof CodeExecutionToolSchema>;
|
|
20
23
|
export { createCodeExecutionTool };
|
|
@@ -243,29 +243,31 @@ export interface TMessage {
|
|
|
243
243
|
[key: string]: unknown;
|
|
244
244
|
}
|
|
245
245
|
export type TPayload = Array<Partial<TMessage>>;
|
|
246
|
+
export type CustomChunkDelta = null | undefined | (Partial<OpenAITypes.Chat.Completions.ChatCompletionChunk.Choice.Delta> & {
|
|
247
|
+
reasoning?: string | null;
|
|
248
|
+
reasoning_content?: string | null;
|
|
249
|
+
});
|
|
250
|
+
export type CustomChunkChoice = Partial<Omit<OpenAITypes.Chat.Completions.ChatCompletionChunk.Choice, 'delta'> & {
|
|
251
|
+
delta?: CustomChunkDelta;
|
|
252
|
+
}>;
|
|
246
253
|
export type CustomChunk = Partial<OpenAITypes.ChatCompletionChunk> & {
|
|
247
|
-
choices?: Partial<Array<
|
|
248
|
-
delta?: Partial<OpenAITypes.Chat.Completions.ChatCompletionChunk.Choice.Delta> & {
|
|
249
|
-
reasoning?: string | null;
|
|
250
|
-
reasoning_content?: string | null;
|
|
251
|
-
};
|
|
252
|
-
}>>;
|
|
254
|
+
choices?: Partial<Array<CustomChunkChoice>>;
|
|
253
255
|
};
|
|
254
256
|
export type SplitStreamHandlers = Partial<{
|
|
255
|
-
[GraphEvents.ON_RUN_STEP]: ({ event, data }: {
|
|
257
|
+
[GraphEvents.ON_RUN_STEP]: ({ event, data, }: {
|
|
256
258
|
event: GraphEvents;
|
|
257
259
|
data: RunStep;
|
|
258
260
|
}) => void;
|
|
259
|
-
[GraphEvents.ON_MESSAGE_DELTA]: ({ event, data }: {
|
|
261
|
+
[GraphEvents.ON_MESSAGE_DELTA]: ({ event, data, }: {
|
|
260
262
|
event: GraphEvents;
|
|
261
263
|
data: MessageDeltaEvent;
|
|
262
264
|
}) => void;
|
|
263
|
-
[GraphEvents.ON_REASONING_DELTA]: ({ event, data }: {
|
|
265
|
+
[GraphEvents.ON_REASONING_DELTA]: ({ event, data, }: {
|
|
264
266
|
event: GraphEvents;
|
|
265
267
|
data: ReasoningDeltaEvent;
|
|
266
268
|
}) => void;
|
|
267
269
|
}>;
|
|
268
|
-
export type ContentAggregator = ({ event, data }: {
|
|
270
|
+
export type ContentAggregator = ({ event, data, }: {
|
|
269
271
|
event: GraphEvents;
|
|
270
272
|
data: RunStep | MessageDeltaEvent | RunStepDeltaEvent | {
|
|
271
273
|
result: ToolEndEvent;
|
package/package.json
CHANGED
package/src/splitStream.ts
CHANGED
|
@@ -2,7 +2,19 @@ import { nanoid } from 'nanoid';
|
|
|
2
2
|
import type * as t from '@/types';
|
|
3
3
|
import { ContentTypes, GraphEvents, StepTypes } from '@/common';
|
|
4
4
|
|
|
5
|
-
export const SEPARATORS = [
|
|
5
|
+
export const SEPARATORS = [
|
|
6
|
+
'.',
|
|
7
|
+
'?',
|
|
8
|
+
'!',
|
|
9
|
+
'۔',
|
|
10
|
+
'。',
|
|
11
|
+
'‥',
|
|
12
|
+
';',
|
|
13
|
+
'¡',
|
|
14
|
+
'¿',
|
|
15
|
+
'\n',
|
|
16
|
+
'```',
|
|
17
|
+
];
|
|
6
18
|
|
|
7
19
|
export class SplitStreamHandler {
|
|
8
20
|
private inCodeBlock = false;
|
|
@@ -28,12 +40,12 @@ export class SplitStreamHandler {
|
|
|
28
40
|
reasoningKey,
|
|
29
41
|
blockThreshold,
|
|
30
42
|
}: {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
runId: string;
|
|
44
|
+
accumulate?: boolean;
|
|
45
|
+
handlers: t.SplitStreamHandlers;
|
|
46
|
+
blockThreshold?: number;
|
|
47
|
+
reasoningKey?: 'reasoning_content' | 'reasoning';
|
|
48
|
+
}) {
|
|
37
49
|
this.runId = runId;
|
|
38
50
|
this.handlers = handlers;
|
|
39
51
|
if (reasoningKey) {
|
|
@@ -51,7 +63,9 @@ export class SplitStreamHandler {
|
|
|
51
63
|
}
|
|
52
64
|
return undefined;
|
|
53
65
|
};
|
|
54
|
-
createMessageStep = (
|
|
66
|
+
createMessageStep = (
|
|
67
|
+
type?: ContentTypes.TEXT | ContentTypes.THINK
|
|
68
|
+
): [string, string] => {
|
|
55
69
|
if (type != null && this.currentType !== type) {
|
|
56
70
|
this.currentType = type;
|
|
57
71
|
}
|
|
@@ -70,23 +84,35 @@ export class SplitStreamHandler {
|
|
|
70
84
|
stepDetails,
|
|
71
85
|
// usage: null,
|
|
72
86
|
};
|
|
73
|
-
this.handlers?.[GraphEvents.ON_RUN_STEP]?.({
|
|
87
|
+
this.handlers?.[GraphEvents.ON_RUN_STEP]?.({
|
|
88
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
89
|
+
data: runStep,
|
|
90
|
+
});
|
|
74
91
|
};
|
|
75
92
|
dispatchMessageDelta = (stepId: string, delta: t.MessageDelta): void => {
|
|
76
93
|
const messageDelta: t.MessageDeltaEvent = {
|
|
77
94
|
id: stepId,
|
|
78
95
|
delta,
|
|
79
96
|
};
|
|
80
|
-
this.handlers?.[GraphEvents.ON_MESSAGE_DELTA]?.({
|
|
97
|
+
this.handlers?.[GraphEvents.ON_MESSAGE_DELTA]?.({
|
|
98
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
99
|
+
data: messageDelta,
|
|
100
|
+
});
|
|
81
101
|
};
|
|
82
102
|
dispatchReasoningDelta = (stepId: string, delta: t.ReasoningDelta): void => {
|
|
83
103
|
const reasoningDelta: t.ReasoningDeltaEvent = {
|
|
84
104
|
id: stepId,
|
|
85
105
|
delta,
|
|
86
106
|
};
|
|
87
|
-
this.handlers?.[GraphEvents.ON_REASONING_DELTA]?.({
|
|
107
|
+
this.handlers?.[GraphEvents.ON_REASONING_DELTA]?.({
|
|
108
|
+
event: GraphEvents.ON_REASONING_DELTA,
|
|
109
|
+
data: reasoningDelta,
|
|
110
|
+
});
|
|
88
111
|
};
|
|
89
|
-
handleContent = (
|
|
112
|
+
handleContent = (
|
|
113
|
+
content: string,
|
|
114
|
+
_type: ContentTypes.TEXT | ContentTypes.THINK
|
|
115
|
+
): void => {
|
|
90
116
|
let type = _type;
|
|
91
117
|
if (this.inThinkBlock && type === ContentTypes.TEXT) {
|
|
92
118
|
type = ContentTypes.THINK;
|
|
@@ -112,17 +138,21 @@ export class SplitStreamHandler {
|
|
|
112
138
|
const stepId = this.currentStepId ?? '';
|
|
113
139
|
if (type === ContentTypes.THINK) {
|
|
114
140
|
this.dispatchReasoningDelta(stepId, {
|
|
115
|
-
content: [
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
141
|
+
content: [
|
|
142
|
+
{
|
|
143
|
+
type: ContentTypes.THINK,
|
|
144
|
+
think: content,
|
|
145
|
+
},
|
|
146
|
+
],
|
|
119
147
|
});
|
|
120
148
|
} else {
|
|
121
149
|
this.dispatchMessageDelta(stepId, {
|
|
122
|
-
content: [
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: ContentTypes.TEXT,
|
|
153
|
+
text: content,
|
|
154
|
+
},
|
|
155
|
+
],
|
|
126
156
|
});
|
|
127
157
|
}
|
|
128
158
|
|
|
@@ -131,7 +161,10 @@ export class SplitStreamHandler {
|
|
|
131
161
|
return;
|
|
132
162
|
}
|
|
133
163
|
|
|
134
|
-
if (
|
|
164
|
+
if (
|
|
165
|
+
this.currentLength > this.blockThreshold &&
|
|
166
|
+
SEPARATORS.some((sep) => content.includes(sep))
|
|
167
|
+
) {
|
|
135
168
|
const [newStepId, newMessageId] = this.createMessageStep(type);
|
|
136
169
|
this.dispatchRunStep(newStepId, {
|
|
137
170
|
type: StepTypes.MESSAGE_CREATION,
|
|
@@ -141,11 +174,14 @@ export class SplitStreamHandler {
|
|
|
141
174
|
});
|
|
142
175
|
}
|
|
143
176
|
};
|
|
144
|
-
getDeltaContent(chunk
|
|
145
|
-
return chunk
|
|
177
|
+
getDeltaContent(chunk?: t.CustomChunk): string {
|
|
178
|
+
return (chunk?.choices?.[0]?.delta as t.CustomChunkDelta)?.content ?? '';
|
|
146
179
|
}
|
|
147
|
-
getReasoningDelta(chunk
|
|
148
|
-
return
|
|
180
|
+
getReasoningDelta(chunk?: t.CustomChunk): string {
|
|
181
|
+
return (
|
|
182
|
+
(chunk?.choices?.[0]?.delta as t.CustomChunkDelta)?.[this.reasoningKey] ??
|
|
183
|
+
''
|
|
184
|
+
);
|
|
149
185
|
}
|
|
150
186
|
handle(chunk?: t.CustomChunk): void {
|
|
151
187
|
if (!chunk) {
|
|
@@ -173,8 +209,12 @@ export class SplitStreamHandler {
|
|
|
173
209
|
const message_id = this.getMessageId() ?? '';
|
|
174
210
|
|
|
175
211
|
if (!message_id) {
|
|
176
|
-
const initialContentType = this.inThinkBlock
|
|
177
|
-
|
|
212
|
+
const initialContentType = this.inThinkBlock
|
|
213
|
+
? ContentTypes.THINK
|
|
214
|
+
: ContentTypes.TEXT;
|
|
215
|
+
const initialType = reasoning_content
|
|
216
|
+
? ContentTypes.THINK
|
|
217
|
+
: initialContentType;
|
|
178
218
|
const [stepId, message_id] = this.createMessageStep(initialType);
|
|
179
219
|
this.dispatchRunStep(stepId, {
|
|
180
220
|
type: StepTypes.MESSAGE_CREATION,
|
|
@@ -190,4 +230,4 @@ export class SplitStreamHandler {
|
|
|
190
230
|
this.handleContent(content, ContentTypes.TEXT);
|
|
191
231
|
}
|
|
192
232
|
}
|
|
193
|
-
}
|
|
233
|
+
}
|
|
@@ -10,26 +10,33 @@ import { EnvVar, Constants } from '@/common';
|
|
|
10
10
|
config();
|
|
11
11
|
|
|
12
12
|
export const imageExtRegex = /\.(jpg|jpeg|png|gif|webp)$/i;
|
|
13
|
-
export const getCodeBaseURL = (): string =>
|
|
13
|
+
export const getCodeBaseURL = (): string =>
|
|
14
|
+
getEnvironmentVariable(EnvVar.CODE_BASEURL) ??
|
|
15
|
+
Constants.OFFICIAL_CODE_BASEURL;
|
|
14
16
|
|
|
15
|
-
const imageMessage = '
|
|
16
|
-
const otherMessage = '
|
|
17
|
+
const imageMessage = 'Image is already displayed to the user';
|
|
18
|
+
const otherMessage = 'File is already downloaded by the user';
|
|
19
|
+
const accessMessage =
|
|
20
|
+
'Note: Files are READ-ONLY snapshots. Save any changes as NEW files with different names. Original files CANNOT be modified in-place. To access these files again, you MUST include the `session_id` in future sessions. Without it, previously generated files will be inaccessible.';
|
|
21
|
+
const emptyOutputMessage =
|
|
22
|
+
'stdout: Empty. Ensure you\'re writing output explicitly.\n';
|
|
17
23
|
|
|
18
24
|
const CodeExecutionToolSchema = z.object({
|
|
19
|
-
lang: z
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
lang: z
|
|
26
|
+
.enum([
|
|
27
|
+
'py',
|
|
28
|
+
'js',
|
|
29
|
+
'ts',
|
|
30
|
+
'c',
|
|
31
|
+
'cpp',
|
|
32
|
+
'java',
|
|
33
|
+
'php',
|
|
34
|
+
'rs',
|
|
35
|
+
'go',
|
|
36
|
+
'd',
|
|
37
|
+
'f90',
|
|
38
|
+
'r',
|
|
39
|
+
])
|
|
33
40
|
.describe('The programming language or runtime to execute the code in.'),
|
|
34
41
|
code: z.string()
|
|
35
42
|
.describe(`The complete, self-contained code to execute, without any truncation or minimization.
|
|
@@ -42,14 +49,36 @@ const CodeExecutionToolSchema = z.object({
|
|
|
42
49
|
- js: use the \`console\` or \`process\` methods for all outputs.
|
|
43
50
|
- r: IMPORTANT: No X11 display available. ALL graphics MUST use Cairo library (library(Cairo)).
|
|
44
51
|
- Other languages: use appropriate output functions.`),
|
|
45
|
-
|
|
46
|
-
.
|
|
52
|
+
session_id: z
|
|
53
|
+
.string()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe(
|
|
56
|
+
`Session ID from a previous response to access generated files.
|
|
57
|
+
- Files load into the current working directory ("/mnt/data/")
|
|
58
|
+
- Use relative paths ONLY
|
|
59
|
+
- Files are READ-ONLY and cannot be modified in-place
|
|
60
|
+
- To modify: read original file, write to NEW filename
|
|
61
|
+
`.trim()
|
|
62
|
+
),
|
|
63
|
+
args: z
|
|
64
|
+
.array(z.string())
|
|
65
|
+
.optional()
|
|
66
|
+
.describe(
|
|
67
|
+
'Additional arguments to execute the code with. This should only be used if the input code requires additional arguments to run.'
|
|
68
|
+
),
|
|
47
69
|
});
|
|
48
70
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
const baseEndpoint = getCodeBaseURL();
|
|
72
|
+
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
73
|
+
|
|
74
|
+
function createCodeExecutionTool(
|
|
75
|
+
params: t.CodeExecutionToolParams = {}
|
|
76
|
+
): DynamicStructuredTool<typeof CodeExecutionToolSchema> {
|
|
77
|
+
const apiKey =
|
|
78
|
+
params[EnvVar.CODE_API_KEY] ??
|
|
79
|
+
params.apiKey ??
|
|
80
|
+
getEnvironmentVariable(EnvVar.CODE_API_KEY) ??
|
|
81
|
+
'';
|
|
53
82
|
if (!apiKey) {
|
|
54
83
|
throw new Error('No API key provided for code execution tool.');
|
|
55
84
|
}
|
|
@@ -64,7 +93,7 @@ Usage:
|
|
|
64
93
|
`.trim();
|
|
65
94
|
|
|
66
95
|
return tool<typeof CodeExecutionToolSchema>(
|
|
67
|
-
async ({ lang, code, ...rest }) => {
|
|
96
|
+
async ({ lang, code, session_id, ...rest }) => {
|
|
68
97
|
const postData = {
|
|
69
98
|
lang,
|
|
70
99
|
code,
|
|
@@ -72,6 +101,54 @@ Usage:
|
|
|
72
101
|
...params,
|
|
73
102
|
};
|
|
74
103
|
|
|
104
|
+
if (session_id != null && session_id.length > 0) {
|
|
105
|
+
try {
|
|
106
|
+
const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;
|
|
107
|
+
const fetchOptions: RequestInit = {
|
|
108
|
+
method: 'GET',
|
|
109
|
+
headers: {
|
|
110
|
+
'User-Agent': 'LibreChat/1.0',
|
|
111
|
+
'X-API-Key': apiKey,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (process.env.PROXY != null && process.env.PROXY !== '') {
|
|
116
|
+
fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const response = await fetch(filesEndpoint, fetchOptions);
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Failed to fetch files for session: ${response.status}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const files = await response.json();
|
|
127
|
+
if (Array.isArray(files) && files.length > 0) {
|
|
128
|
+
const fileReferences: t.CodeEnvFile[] = files.map((file) => {
|
|
129
|
+
// Extract the ID from the file name (part after session ID prefix and before extension)
|
|
130
|
+
const nameParts = file.name.split('/');
|
|
131
|
+
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
session_id,
|
|
135
|
+
id,
|
|
136
|
+
name: file.metadata['original-filename'],
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!postData.files) {
|
|
141
|
+
postData.files = fileReferences;
|
|
142
|
+
} else if (Array.isArray(postData.files)) {
|
|
143
|
+
postData.files = [...postData.files, ...fileReferences];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
// eslint-disable-next-line no-console
|
|
148
|
+
console.warn(`Failed to fetch files for session: ${session_id}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
75
152
|
try {
|
|
76
153
|
const fetchOptions: RequestInit = {
|
|
77
154
|
method: 'POST',
|
|
@@ -96,7 +173,7 @@ Usage:
|
|
|
96
173
|
if (result.stdout) {
|
|
97
174
|
formattedOutput += `stdout:\n${result.stdout}\n`;
|
|
98
175
|
} else {
|
|
99
|
-
formattedOutput +=
|
|
176
|
+
formattedOutput += emptyOutputMessage;
|
|
100
177
|
}
|
|
101
178
|
if (result.stderr) formattedOutput += `stderr:\n${result.stderr}\n`;
|
|
102
179
|
if (result.files && result.files.length > 0) {
|
|
@@ -104,24 +181,30 @@ Usage:
|
|
|
104
181
|
|
|
105
182
|
const fileCount = result.files.length;
|
|
106
183
|
for (let i = 0; i < fileCount; i++) {
|
|
107
|
-
const
|
|
108
|
-
const isImage = imageExtRegex.test(
|
|
109
|
-
formattedOutput +=
|
|
184
|
+
const file = result.files[i];
|
|
185
|
+
const isImage = imageExtRegex.test(file.name);
|
|
186
|
+
formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;
|
|
110
187
|
|
|
111
188
|
if (i < fileCount - 1) {
|
|
112
189
|
formattedOutput += fileCount <= 3 ? ', ' : ',\n';
|
|
113
190
|
}
|
|
114
191
|
}
|
|
115
192
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
193
|
+
formattedOutput += `\nsession_id: ${result.session_id}\n\n${accessMessage}`;
|
|
194
|
+
return [
|
|
195
|
+
formattedOutput.trim(),
|
|
196
|
+
{
|
|
197
|
+
session_id: result.session_id,
|
|
198
|
+
files: result.files,
|
|
199
|
+
},
|
|
200
|
+
];
|
|
120
201
|
}
|
|
121
202
|
|
|
122
203
|
return [formattedOutput.trim(), { session_id: result.session_id }];
|
|
123
204
|
} catch (error) {
|
|
124
|
-
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Execution error:\n\n${(error as Error | undefined)?.message}`
|
|
207
|
+
);
|
|
125
208
|
}
|
|
126
209
|
},
|
|
127
210
|
{
|
|
@@ -133,4 +216,4 @@ Usage:
|
|
|
133
216
|
);
|
|
134
217
|
}
|
|
135
218
|
|
|
136
|
-
export { createCodeExecutionTool };
|
|
219
|
+
export { createCodeExecutionTool };
|