diff --git a/.changeset/stale-berries-hear.md b/.changeset/stale-berries-hear.md new file mode 100644 index 000000000..ab4551c2e --- /dev/null +++ b/.changeset/stale-berries-hear.md @@ -0,0 +1,5 @@ +--- +"create-llama": patch +--- + +bump: code interpreter v1 diff --git a/helpers/env-variables.ts b/helpers/env-variables.ts index 6ea0aab0a..aa6103339 100644 --- a/helpers/env-variables.ts +++ b/helpers/env-variables.ts @@ -470,11 +470,11 @@ const getSystemPromptEnv = ( }); const systemPrompt = - "'" + + '"' + DEFAULT_SYSTEM_PROMPT + (dataSources?.length ? `\n${DATA_SOURCES_PROMPT}` : "") + (toolSystemPrompt ? `\n${toolSystemPrompt}` : "") + - "'"; + '"'; systemPromptEnv.push({ name: "SYSTEM_PROMPT", diff --git a/helpers/tools.ts b/helpers/tools.ts index 33bd96e60..acc5a9a00 100644 --- a/helpers/tools.ts +++ b/helpers/tools.ts @@ -124,7 +124,7 @@ For better results, you can specify the region parameter to get results from a s dependencies: [ { name: "e2b_code_interpreter", - version: "0.0.11b38", + version: "1.0.3", }, ], supportedFrameworks: ["fastapi", "express", "nextjs"], @@ -155,7 +155,7 @@ For better results, you can specify the region parameter to get results from a s dependencies: [ { name: "e2b_code_interpreter", - version: "0.0.11b38", + version: "1.0.3", }, ], supportedFrameworks: ["fastapi", "express", "nextjs"], diff --git a/templates/components/engines/python/agent/tools/interpreter.py b/templates/components/engines/python/agent/tools/interpreter.py index e108bd9bb..20f93b85f 100644 --- a/templates/components/engines/python/agent/tools/interpreter.py +++ b/templates/components/engines/python/agent/tools/interpreter.py @@ -5,7 +5,7 @@ from typing import List, Optional from app.services.file import DocumentFile, FileService -from e2b_code_interpreter import CodeInterpreter +from e2b_code_interpreter import Sandbox from e2b_code_interpreter.models import Logs from llama_index.core.tools import FunctionTool from pydantic import BaseModel @@ -61,7 +61,7 @@ def _init_interpreter(self, sandbox_files: List[str] = []): Lazily initialize the interpreter. """ logger.info(f"Initializing interpreter with {len(sandbox_files)} files") - self.interpreter = CodeInterpreter(api_key=self.api_key) + self.interpreter = Sandbox(api_key=self.api_key) if len(sandbox_files) > 0: for file_path in sandbox_files: file_name = os.path.basename(file_path) @@ -159,11 +159,11 @@ def interpret( if self.interpreter is None: self._init_interpreter(sandbox_files) - if self.interpreter and self.interpreter.notebook: + if self.interpreter: logger.info( f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}" ) - exec = self.interpreter.notebook.exec_cell(code) + exec = self.interpreter.run_code(code) if exec.error: error_message = f"The code failed to execute successfully. Error: {exec.error}. Try to fix the code and run again." diff --git a/templates/components/engines/typescript/agent/tools/code-generator.ts b/templates/components/engines/typescript/agent/tools/code-generator.ts index ec6f10e83..610cf727d 100644 --- a/templates/components/engines/typescript/agent/tools/code-generator.ts +++ b/templates/components/engines/typescript/agent/tools/code-generator.ts @@ -103,6 +103,7 @@ export class CodeGeneratorTool implements BaseTool { const artifact = await this.generateArtifact( input.requirement, input.oldCode, + input.sandboxFiles, // help the generated code use exact files ); if (input.sandboxFiles) { artifact.files = input.sandboxFiles; @@ -117,10 +118,12 @@ export class CodeGeneratorTool implements BaseTool { async generateArtifact( query: string, oldCode?: string, + attachments?: string[], ): Promise { const userMessage = ` ${query} ${oldCode ? `The existing code is: \n\`\`\`${oldCode}\`\`\`` : ""} + ${attachments ? `The attachments are: \n${attachments.join("\n")}` : ""} `; const messages: ChatMessage[] = [ { role: "system", content: CODE_GENERATION_PROMPT }, diff --git a/templates/components/engines/typescript/agent/tools/interpreter.ts b/templates/components/engines/typescript/agent/tools/interpreter.ts index 37b645882..9cea9443c 100644 --- a/templates/components/engines/typescript/agent/tools/interpreter.ts +++ b/templates/components/engines/typescript/agent/tools/interpreter.ts @@ -1,4 +1,4 @@ -import { CodeInterpreter, Logs, Result } from "@e2b/code-interpreter"; +import { Logs, Result, Sandbox } from "@e2b/code-interpreter"; import type { JSONSchemaType } from "ajv"; import fs from "fs"; import { BaseTool, ToolMetadata } from "llamaindex"; @@ -82,7 +82,7 @@ export class InterpreterTool implements BaseTool { private apiKey?: string; private fileServerURLPrefix?: string; metadata: ToolMetadata>; - codeInterpreter?: CodeInterpreter; + codeInterpreter?: Sandbox; constructor(params?: InterpreterToolParams) { this.metadata = params?.metadata || DEFAULT_META_DATA; @@ -104,26 +104,27 @@ export class InterpreterTool implements BaseTool { public async initInterpreter(input: InterpreterParameter) { if (!this.codeInterpreter) { - this.codeInterpreter = await CodeInterpreter.create({ + this.codeInterpreter = await Sandbox.create({ apiKey: this.apiKey, }); - } - // upload files to sandbox - if (input.sandboxFiles) { - console.log(`Uploading ${input.sandboxFiles.length} files to sandbox`); - try { - for (const filePath of input.sandboxFiles) { - const fileName = path.basename(filePath); - const localFilePath = path.join(this.uploadedFilesDir, fileName); - const content = fs.readFileSync(localFilePath); - - const arrayBuffer = new Uint8Array(content).buffer; - await this.codeInterpreter?.files.write(filePath, arrayBuffer); + // upload files to sandbox when it's initialized + if (input.sandboxFiles) { + console.log(`Uploading ${input.sandboxFiles.length} files to sandbox`); + try { + for (const filePath of input.sandboxFiles) { + const fileName = path.basename(filePath); + const localFilePath = path.join(this.uploadedFilesDir, fileName); + const content = fs.readFileSync(localFilePath); + + const arrayBuffer = new Uint8Array(content).buffer; + await this.codeInterpreter?.files.write(filePath, arrayBuffer); + } + } catch (error) { + console.error("Got error when uploading files to sandbox", error); } - } catch (error) { - console.error("Got error when uploading files to sandbox", error); } } + return this.codeInterpreter; } @@ -150,7 +151,7 @@ export class InterpreterTool implements BaseTool { `\n${"=".repeat(50)}\n> Running following AI-generated code:\n${input.code}\n${"=".repeat(50)}`, ); const interpreter = await this.initInterpreter(input); - const exec = await interpreter.notebook.execCell(input.code); + const exec = await interpreter.runCode(input.code); if (exec.error) console.error("[Code Interpreter error]", exec.error); const extraResult = await this.getExtraResult(exec.results[0]); const result: InterpreterToolOutput = { @@ -169,7 +170,7 @@ export class InterpreterTool implements BaseTool { } async close() { - await this.codeInterpreter?.close(); + await this.codeInterpreter?.kill(); } private async getExtraResult( diff --git a/templates/components/routers/python/sandbox.py b/templates/components/routers/python/sandbox.py index 28c0c3f19..df0f83cbb 100644 --- a/templates/components/routers/python/sandbox.py +++ b/templates/components/routers/python/sandbox.py @@ -17,11 +17,11 @@ import os import uuid from dataclasses import asdict -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional from app.engine.tools.artifact import CodeArtifact from app.services.file import FileService -from e2b_code_interpreter import CodeInterpreter, Sandbox # type: ignore +from e2b_code_interpreter import Sandbox # type: ignore from fastapi import APIRouter, HTTPException, Request from pydantic import BaseModel @@ -60,6 +60,14 @@ class FileUpload(BaseModel): name: str +SUPPORTED_TEMPLATES = [ + "nextjs-developer", + "vue-developer", + "streamlit-developer", + "gradio-developer", +] + + @sandbox_router.post("") async def create_sandbox(request: Request): request_data = await request.json() @@ -79,33 +87,22 @@ async def create_sandbox(request: Request): status_code=400, detail="Could not create artifact from the request data" ) - sbx = None - - # Create an interpreter or a sandbox - if artifact.template == "code-interpreter-multilang": - sbx = CodeInterpreter(api_key=os.getenv("E2B_API_KEY"), timeout=SANDBOX_TIMEOUT) - logger.debug(f"Created code interpreter {sbx}") - else: - sbx = Sandbox( - api_key=os.getenv("E2B_API_KEY"), - template=artifact.template, - metadata={"template": artifact.template, "user_id": "default"}, - timeout=SANDBOX_TIMEOUT, - ) - logger.debug(f"Created sandbox {sbx}") + sbx = Sandbox( + api_key=os.getenv("E2B_API_KEY"), + template=( + None if artifact.template not in SUPPORTED_TEMPLATES else artifact.template + ), + metadata={"template": artifact.template, "user_id": "default"}, + timeout=SANDBOX_TIMEOUT, + ) + logger.debug(f"Created sandbox {sbx}") # Install packages if artifact.has_additional_dependencies: - if isinstance(sbx, CodeInterpreter): - sbx.notebook.exec_cell(artifact.install_dependencies_command) - logger.debug( - f"Installed dependencies: {', '.join(artifact.additional_dependencies)} in code interpreter {sbx}" - ) - elif isinstance(sbx, Sandbox): - sbx.commands.run(artifact.install_dependencies_command) - logger.debug( - f"Installed dependencies: {', '.join(artifact.additional_dependencies)} in sandbox {sbx}" - ) + sbx.commands.run(artifact.install_dependencies_command) + logger.debug( + f"Installed dependencies: {', '.join(artifact.additional_dependencies)} in sandbox {sbx}" + ) # Copy files if len(sandbox_files) > 0: @@ -122,7 +119,7 @@ async def create_sandbox(request: Request): # Execute code or return a URL to the running sandbox if artifact.template == "code-interpreter-multilang": - result = sbx.notebook.exec_cell(artifact.code or "") + result = sbx.run_code(artifact.code or "") output_urls = _download_cell_results(result.results) runtime_error = asdict(result.error) if result.error else None return ExecutionResult( @@ -145,7 +142,7 @@ async def create_sandbox(request: Request): def _upload_files( - sandbox: Union[CodeInterpreter, Sandbox], + sandbox: Sandbox, sandbox_files: List[str] = [], ) -> None: for file_path in sandbox_files: diff --git a/templates/types/streaming/express/package.json b/templates/types/streaming/express/package.json index a39ad8e32..c7a65882b 100644 --- a/templates/types/streaming/express/package.json +++ b/templates/types/streaming/express/package.json @@ -24,7 +24,7 @@ "llamaindex": "0.8.2", "pdf2json": "3.0.5", "ajv": "^8.12.0", - "@e2b/code-interpreter": "0.0.9-beta.3", + "@e2b/code-interpreter": "1.0.4", "got": "^14.4.1", "@apidevtools/swagger-parser": "^10.1.0", "formdata-node": "^6.0.3", diff --git a/templates/types/streaming/express/src/controllers/sandbox.controller.ts b/templates/types/streaming/express/src/controllers/sandbox.controller.ts index 6013d1385..1c20ef16f 100644 --- a/templates/types/streaming/express/src/controllers/sandbox.controller.ts +++ b/templates/types/streaming/express/src/controllers/sandbox.controller.ts @@ -13,13 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - CodeInterpreter, - ExecutionError, - Result, - Sandbox, -} from "@e2b/code-interpreter"; +import { ExecutionError, Result, Sandbox } from "@e2b/code-interpreter"; import { Request, Response } from "express"; +import path from "node:path"; import { saveDocument } from "./llamaindex/documents/helper"; type CodeArtifact = { @@ -39,6 +35,8 @@ const sandboxTimeout = 10 * 60 * 1000; // 10 minute in ms export const maxDuration = 60; +const OUTPUT_DIR = path.join("output", "tools"); + export type ExecutionResult = { template: string; stdout: string[]; @@ -48,60 +46,53 @@ export type ExecutionResult = { url: string; }; +// see https://github.com/e2b-dev/fragments/tree/main/sandbox-templates +const SUPPORTED_TEMPLATES = [ + "nextjs-developer", + "vue-developer", + "streamlit-developer", + "gradio-developer", +]; + export const sandbox = async (req: Request, res: Response) => { const { artifact }: { artifact: CodeArtifact } = req.body; - let sbx: Sandbox | CodeInterpreter | undefined = undefined; - - // Create a interpreter or a sandbox - if (artifact.template === "code-interpreter-multilang") { - sbx = await CodeInterpreter.create({ - metadata: { template: artifact.template }, - timeoutMs: sandboxTimeout, - }); - console.log("Created code interpreter", sbx.sandboxID); + let sbx: Sandbox; + const sandboxOpts = { + metadata: { template: artifact.template, userID: "default" }, + timeoutMs: sandboxTimeout, + }; + if (SUPPORTED_TEMPLATES.includes(artifact.template)) { + sbx = await Sandbox.create(artifact.template, sandboxOpts); } else { - sbx = await Sandbox.create(artifact.template, { - metadata: { template: artifact.template, userID: "default" }, - timeoutMs: sandboxTimeout, - }); - console.log("Created sandbox", sbx.sandboxID); + sbx = await Sandbox.create(sandboxOpts); } + console.log("Created sandbox", sbx.sandboxId); // Install packages if (artifact.has_additional_dependencies) { - if (sbx instanceof CodeInterpreter) { - await sbx.notebook.execCell(artifact.install_dependencies_command); - console.log( - `Installed dependencies: ${artifact.additional_dependencies.join(", ")} in code interpreter ${sbx.sandboxID}`, - ); - } else if (sbx instanceof Sandbox) { - await sbx.commands.run(artifact.install_dependencies_command); - console.log( - `Installed dependencies: ${artifact.additional_dependencies.join(", ")} in sandbox ${sbx.sandboxID}`, - ); - } + await sbx.commands.run(artifact.install_dependencies_command); + console.log( + `Installed dependencies: ${artifact.additional_dependencies.join(", ")} in sandbox ${sbx.sandboxId}`, + ); } // Copy code to fs if (artifact.code && Array.isArray(artifact.code)) { artifact.code.forEach(async (file) => { await sbx.files.write(file.file_path, file.file_content); - console.log(`Copied file to ${file.file_path} in ${sbx.sandboxID}`); + console.log(`Copied file to ${file.file_path} in ${sbx.sandboxId}`); }); } else { await sbx.files.write(artifact.file_path, artifact.code); - console.log(`Copied file to ${artifact.file_path} in ${sbx.sandboxID}`); + console.log(`Copied file to ${artifact.file_path} in ${sbx.sandboxId}`); } // Execute code or return a URL to the running sandbox if (artifact.template === "code-interpreter-multilang") { - const result = await (sbx as CodeInterpreter).notebook.execCell( - artifact.code || "", - ); - await (sbx as CodeInterpreter).close(); + const result = await sbx.runCode(artifact.code || ""); + await sbx.kill(); const outputUrls = await downloadCellResults(result.results); - return res.status(200).json({ template: artifact.template, stdout: result.logs.stdout, @@ -121,17 +112,23 @@ async function downloadCellResults( cellResults?: Result[], ): Promise> { if (!cellResults) return []; + const results = await Promise.all( cellResults.map(async (res) => { const formats = res.formats(); // available formats in the result const formatResults = await Promise.all( - formats.map(async (ext) => { - const filename = `${crypto.randomUUID()}.${ext}`; - const base64 = res[ext as keyof Result]; - const buffer = Buffer.from(base64, "base64"); - const fileurl = await saveDocument(filename, buffer); - return { url: fileurl, filename }; - }), + formats + .filter((ext) => ["png", "svg", "jpeg", "pdf"].includes(ext)) + .map(async (ext) => { + const filename = `${crypto.randomUUID()}.${ext}`; + const base64 = res[ext as keyof Result]; + const buffer = Buffer.from(base64, "base64"); + const fileurl = await saveDocument( + path.join(OUTPUT_DIR, filename), + buffer, + ); + return { url: fileurl, filename }; + }), ); return formatResults; }), diff --git a/templates/types/streaming/nextjs/app/api/sandbox/route.ts b/templates/types/streaming/nextjs/app/api/sandbox/route.ts index f524d5507..07336ccdc 100644 --- a/templates/types/streaming/nextjs/app/api/sandbox/route.ts +++ b/templates/types/streaming/nextjs/app/api/sandbox/route.ts @@ -13,12 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - CodeInterpreter, - ExecutionError, - Result, - Sandbox, -} from "@e2b/code-interpreter"; +import { ExecutionError, Result, Sandbox } from "@e2b/code-interpreter"; import fs from "node:fs/promises"; import path from "node:path"; import { saveDocument } from "../chat/llamaindex/documents/helper"; @@ -41,6 +36,8 @@ const sandboxTimeout = 10 * 60 * 1000; // 10 minute in ms export const maxDuration = 60; +const OUTPUT_DIR = path.join("output", "tools"); + export type ExecutionResult = { template: string; stdout: string[]; @@ -50,39 +47,35 @@ export type ExecutionResult = { url: string; }; +// see https://github.com/e2b-dev/fragments/tree/main/sandbox-templates +const SUPPORTED_TEMPLATES = [ + "nextjs-developer", + "vue-developer", + "streamlit-developer", + "gradio-developer", +]; + export async function POST(req: Request) { const { artifact }: { artifact: CodeArtifact } = await req.json(); - let sbx: Sandbox | CodeInterpreter | undefined = undefined; - - // Create a interpreter or a sandbox - if (artifact.template === "code-interpreter-multilang") { - sbx = await CodeInterpreter.create({ - metadata: { template: artifact.template }, - timeoutMs: sandboxTimeout, - }); - console.log("Created code interpreter", sbx.sandboxID); + let sbx: Sandbox; + const sandboxOpts = { + metadata: { template: artifact.template, userID: "default" }, + timeoutMs: sandboxTimeout, + }; + if (SUPPORTED_TEMPLATES.includes(artifact.template)) { + sbx = await Sandbox.create(artifact.template, sandboxOpts); } else { - sbx = await Sandbox.create(artifact.template, { - metadata: { template: artifact.template, userID: "default" }, - timeoutMs: sandboxTimeout, - }); - console.log("Created sandbox", sbx.sandboxID); + sbx = await Sandbox.create(sandboxOpts); } + console.log("Created sandbox", sbx.sandboxId); // Install packages if (artifact.has_additional_dependencies) { - if (sbx instanceof CodeInterpreter) { - await sbx.notebook.execCell(artifact.install_dependencies_command); - console.log( - `Installed dependencies: ${artifact.additional_dependencies.join(", ")} in code interpreter ${sbx.sandboxID}`, - ); - } else if (sbx instanceof Sandbox) { - await sbx.commands.run(artifact.install_dependencies_command); - console.log( - `Installed dependencies: ${artifact.additional_dependencies.join(", ")} in sandbox ${sbx.sandboxID}`, - ); - } + await sbx.commands.run(artifact.install_dependencies_command); + console.log( + `Installed dependencies: ${artifact.additional_dependencies.join(", ")} in sandbox ${sbx.sandboxId}`, + ); } // Copy files @@ -94,7 +87,7 @@ export async function POST(req: Request) { const arrayBuffer = new Uint8Array(fileContent).buffer; await sbx.files.write(sandboxFilePath, arrayBuffer); - console.log(`Copied file to ${sandboxFilePath} in ${sbx.sandboxID}`); + console.log(`Copied file to ${sandboxFilePath} in ${sbx.sandboxId}`); }); } @@ -102,19 +95,17 @@ export async function POST(req: Request) { if (artifact.code && Array.isArray(artifact.code)) { artifact.code.forEach(async (file) => { await sbx.files.write(file.file_path, file.file_content); - console.log(`Copied file to ${file.file_path} in ${sbx.sandboxID}`); + console.log(`Copied file to ${file.file_path} in ${sbx.sandboxId}`); }); } else { await sbx.files.write(artifact.file_path, artifact.code); - console.log(`Copied file to ${artifact.file_path} in ${sbx.sandboxID}`); + console.log(`Copied file to ${artifact.file_path} in ${sbx.sandboxId}`); } // Execute code or return a URL to the running sandbox if (artifact.template === "code-interpreter-multilang") { - const result = await (sbx as CodeInterpreter).notebook.execCell( - artifact.code || "", - ); - await (sbx as CodeInterpreter).close(); + const result = await sbx.runCode(artifact.code || ""); + await sbx.kill(); const outputUrls = await downloadCellResults(result.results); return new Response( JSON.stringify({ @@ -139,17 +130,23 @@ async function downloadCellResults( cellResults?: Result[], ): Promise> { if (!cellResults) return []; + const results = await Promise.all( cellResults.map(async (res) => { const formats = res.formats(); // available formats in the result const formatResults = await Promise.all( - formats.map(async (ext) => { - const filename = `${crypto.randomUUID()}.${ext}`; - const base64 = res[ext as keyof Result]; - const buffer = Buffer.from(base64, "base64"); - const fileurl = await saveDocument(filename, buffer); - return { url: fileurl, filename }; - }), + formats + .filter((ext) => ["png", "svg", "jpeg", "pdf"].includes(ext)) + .map(async (ext) => { + const filename = `${crypto.randomUUID()}.${ext}`; + const base64 = res[ext as keyof Result]; + const buffer = Buffer.from(base64, "base64"); + const fileurl = await saveDocument( + path.join(OUTPUT_DIR, filename), + buffer, + ); + return { url: fileurl, filename }; + }), ); return formatResults; }), diff --git a/templates/types/streaming/nextjs/package.json b/templates/types/streaming/nextjs/package.json index 94e576f07..59b2cbfca 100644 --- a/templates/types/streaming/nextjs/package.json +++ b/templates/types/streaming/nextjs/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@apidevtools/swagger-parser": "^10.1.0", - "@e2b/code-interpreter": "0.0.9-beta.3", + "@e2b/code-interpreter": "1.0.4", "@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-slot": "^1.0.2",