From d60b3c5a96d1f51f8a42bf20d71fa3b46982956b Mon Sep 17 00:00:00 2001
From: leehuwuj <leehuwuj@gmail.com>
Date: Mon, 27 May 2024 13:09:59 +0700
Subject: [PATCH] refactor code and add changeset

---
 .changeset/itchy-ads-travel.md                |  5 ++++
 helpers/index.ts                              |  5 ++++
 .../engines/python/agent/tools/interpreter.py | 20 +++++++-------
 .../streaming/fastapi/app/api/routers/chat.py |  8 +++++-
 .../fastapi/app/api/routers/messaging.py      | 26 ++++++++++++-------
 templates/types/streaming/fastapi/main.py     |  4 +--
 6 files changed, 46 insertions(+), 22 deletions(-)
 create mode 100644 .changeset/itchy-ads-travel.md

diff --git a/.changeset/itchy-ads-travel.md b/.changeset/itchy-ads-travel.md
new file mode 100644
index 00000000..ac3ee4cd
--- /dev/null
+++ b/.changeset/itchy-ads-travel.md
@@ -0,0 +1,5 @@
+---
+"create-llama": patch
+---
+
+Add support E2B code interpreter tool for FastAPI
diff --git a/helpers/index.ts b/helpers/index.ts
index ef602d28..e65998ba 100644
--- a/helpers/index.ts
+++ b/helpers/index.ts
@@ -171,6 +171,11 @@ export const installTemplate = async (
         );
       }
     }
+
+    // Create tool-output directory
+    if (props.tools && props.tools.length > 0) {
+      await fsExtra.mkdir(path.join(props.root, "tool-output"));
+    }
   } else {
     // this is a frontend for a full-stack app, create .env file with model information
     await createFrontendEnvFile(props.root, {
diff --git a/templates/components/engines/python/agent/tools/interpreter.py b/templates/components/engines/python/agent/tools/interpreter.py
index 55716a76..bc913935 100644
--- a/templates/components/engines/python/agent/tools/interpreter.py
+++ b/templates/components/engines/python/agent/tools/interpreter.py
@@ -32,11 +32,6 @@ class E2BCodeInterpreter:
         self.api_key = api_key
         self.filesever_url_prefix = filesever_url_prefix
 
-    def code_interpret(
-        self, code_interpreter: CodeInterpreter, code: str
-    ) -> Tuple[List, List]:
-        pass
-
     def get_output_path(self, filename: str) -> str:
         # if output directory doesn't exist, create it
         if not os.path.exists(self.output_dir):
@@ -48,8 +43,12 @@ class E2BCodeInterpreter:
         buffer = base64.b64decode(base64_data)
         output_path = self.get_output_path(filename)
 
-        with open(output_path, "wb") as file:
-            file.write(buffer)
+        try:
+            with open(output_path, "wb") as file:
+                file.write(buffer)
+        except IOError as e:
+            logger.error(f"Failed to write to file {output_path}: {str(e)}")
+            raise e
 
         logger.info(f"Saved file to {output_path}")
 
@@ -89,7 +88,7 @@ class E2BCodeInterpreter:
 
         return output
 
-    def interpret(self, code: str) -> Dict:
+    def interpret(self, code: str) -> E2BToolOutput:
         with CodeInterpreter(api_key=self.api_key) as interpreter:
             logger.info(
                 f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}"
@@ -106,7 +105,7 @@ class E2BCodeInterpreter:
                     output = E2BToolOutput(
                         is_error=False, logs=exec.logs, results=results
                     )
-            return output.dict()
+            return output
 
 
 def code_interpret(code: str) -> Dict:
@@ -127,7 +126,8 @@ def code_interpret(code: str) -> Dict:
     interpreter = E2BCodeInterpreter(
         api_key=api_key, filesever_url_prefix=filesever_url_prefix
     )
-    return interpreter.interpret(code)
+    output = interpreter.interpret(code)
+    return output.dict()
 
 
 # Specify as functions tools to be loaded by the ToolFactory
diff --git a/templates/types/streaming/fastapi/app/api/routers/chat.py b/templates/types/streaming/fastapi/app/api/routers/chat.py
index c92ca3d4..a23cc440 100644
--- a/templates/types/streaming/fastapi/app/api/routers/chat.py
+++ b/templates/types/streaming/fastapi/app/api/routers/chat.py
@@ -93,7 +93,13 @@ async def chat(
 
     event_handler = EventCallbackHandler()
     chat_engine.callback_manager.handlers.append(event_handler)  # type: ignore
-    response = await chat_engine.astream_chat(last_message_content, messages)
+    try:
+        response = await chat_engine.astream_chat(last_message_content, messages)
+    except Exception as e:
+        raise HTTPException(
+            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+            detail=f"Error in chat engine: {e}",
+        )
 
     async def content_generator():
         # Yield the text response
diff --git a/templates/types/streaming/fastapi/app/api/routers/messaging.py b/templates/types/streaming/fastapi/app/api/routers/messaging.py
index a239657a..94cc5851 100644
--- a/templates/types/streaming/fastapi/app/api/routers/messaging.py
+++ b/templates/types/streaming/fastapi/app/api/routers/messaging.py
@@ -1,5 +1,6 @@
 import json
 import asyncio
+import logging
 from typing import AsyncGenerator, Dict, Any, List, Optional
 from llama_index.core.callbacks.base import BaseCallbackHandler
 from llama_index.core.callbacks.schema import CBEventType
@@ -7,6 +8,9 @@ from llama_index.core.tools.types import ToolOutput
 from pydantic import BaseModel
 
 
+logger = logging.getLogger(__name__)
+
+
 class CallbackEvent(BaseModel):
     event_type: CBEventType
     payload: Optional[Dict[str, Any]] = None
@@ -72,15 +76,19 @@ class CallbackEvent(BaseModel):
                     }
 
     def to_response(self):
-        match self.event_type:
-            case "retrieve":
-                return self.get_retrieval_message()
-            case "function_call":
-                return self.get_tool_message()
-            case "agent_step":
-                return self.get_agent_tool_response()
-            case _:
-                return None
+        try:
+            match self.event_type:
+                case "retrieve":
+                    return self.get_retrieval_message()
+                case "function_call":
+                    return self.get_tool_message()
+                case "agent_step":
+                    return self.get_agent_tool_response()
+                case _:
+                    return None
+        except Exception as e:
+            logger.error(f"Error in converting event to response: {e}")
+            return None
 
 
 class EventCallbackHandler(BaseCallbackHandler):
diff --git a/templates/types/streaming/fastapi/main.py b/templates/types/streaming/fastapi/main.py
index a7569a52..b095b616 100644
--- a/templates/types/streaming/fastapi/main.py
+++ b/templates/types/streaming/fastapi/main.py
@@ -41,8 +41,8 @@ if environment == "dev":
 # Mount the data files to serve the file viewer
 if os.path.exists("data"):
     app.mount("/api/files/data", StaticFiles(directory="data"), name="data-static")
-# Mount the tool output files
-if os.path.exists("config/tools.yaml"):
+# Mount the output files from tools
+if os.path.exists("tool-output"):
     app.mount(
         "/api/files/tool-output",
         StaticFiles(directory="tool-output"),
-- 
GitLab