Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/miroflow-agent/conf/llm/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ top_p: 1.0
min_p: 0.0
top_k: -1
max_tokens: 4096
max_context_length: 65536
openai_base_url: null
keep_tool_result: -1
anthropic_base_url: https://api.anthropic.com
2 changes: 1 addition & 1 deletion apps/miroflow-agent/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies = [
"mcp",
"fastmcp",
"anthropic",
"e2b-code-interpreter>=1.2.1",
# "e2b-code-interpreter>=1.2.1",
"jsonlines>=4.0.0",
"mammoth>=1.9.0",
"numpy>=2.2.5",
Expand Down
29 changes: 19 additions & 10 deletions apps/miroflow-agent/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
set dotenv-load := true
set dotenv-path := "{{justfile_directory()}}/.env"

default:
just --list

Expand Down Expand Up @@ -34,3 +37,8 @@ format-md:
# run precommit before PR
[group('precommit')]
precommit: lint sort-imports format-md format

# run mcp server under test
[group('mcp-test')]
mcp-inspect service:
uv run --directory libs/miroflow-tools mcp dev src/miroflow_tools/mcp_servers/{{service}}
5 changes: 3 additions & 2 deletions libs/miroflow-tools/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencies = [
"fastmcp>=0.1.0",
"playwright>=1.40.0",
"requests>=2.32.0",
"e2b-code-interpreter>=1.2.1",
"e2b-code-interpreter==2.0.0",
"wikipedia",
"mutagen",
"markitdown-mcp>=0.0.1a3",
Expand All @@ -37,6 +37,7 @@ dev = [
"pytest-mock>=3.10.0",
"pytest-timeout>=2.1.0",
"inline-snapshot>=0.23.2",
"mcp[cli]>=1.14.1",
]

[tool.pytest.ini_options]
Expand All @@ -59,4 +60,4 @@ markers = [
"unit: marks tests as unit tests",
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
"requires_api_key: marks tests that require real API credentials",
]
]
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import os

from e2b_code_interpreter import Sandbox
from fastmcp import FastMCP
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("e2b-python-interpreter")
Expand All @@ -30,6 +30,9 @@
# DEFAULT TEMPLATE ID
DEFAULT_TEMPLATE_ID = "all_pip_apt_pkg"

# DEFAULT METADATA
DEFAULT_METADATA = {"env": "miro-thinker"}

# DEFAULT CONFS
DEFAULT_TIMEOUT = 1800 # seconds

Expand All @@ -48,12 +51,14 @@ async def create_sandbox() -> str:
for attempt in range(1, max_retries + 1):
sandbox = None
try:
sandbox = Sandbox(
sandbox = Sandbox.create(
template=DEFAULT_TEMPLATE_ID,
timeout=DEFAULT_TIMEOUT,
api_key=E2B_API_KEY,
metadata=DEFAULT_METADATA,
)
info = sandbox.get_info()
# sandbox.beta_pause()

tmpfiles_dir = os.path.join(LOGS_DIR, "tmpfiles")
os.makedirs(tmpfiles_dir, exist_ok=True)
Expand All @@ -66,7 +71,9 @@ async def create_sandbox() -> str:
finally:
# Set timeout before exit to prevent timeout after function exits
try:
sandbox.set_timeout(DEFAULT_TIMEOUT)
if sandbox is not None:
sandbox.beta_pause()
# sandbox.set_timeout(DEFAULT_TIMEOUT)
except Exception:
pass # Ignore timeout setting errors

Expand All @@ -91,10 +98,10 @@ async def run_command(command: str, sandbox_id: str) -> str:
max_retries = 3
for attempt in range(1, max_retries + 1):
try:
sandbox.set_timeout(
DEFAULT_TIMEOUT
) # refresh the timeout for each command execution
result = sandbox.commands.run(command)
# sandbox.set_timeout(
# DEFAULT_TIMEOUT
# ) # refresh the timeout for each command execution
result = sandbox.commands.run(command, timeout=DEFAULT_TIMEOUT)

# Check if command contains package installation commands
result_str = str(result)
Expand All @@ -115,7 +122,8 @@ async def run_command(command: str, sandbox_id: str) -> str:
finally:
# Set timeout before exit to prevent timeout after function exits
try:
sandbox.set_timeout(DEFAULT_TIMEOUT)
sandbox.beta_pause()
# sandbox.set_timeout(DEFAULT_TIMEOUT)
except Exception:
pass # Ignore timeout setting errors

Expand All @@ -140,11 +148,11 @@ async def run_python_code(code_block: str, sandbox_id: str) -> str:
max_retries = 3
for attempt in range(1, max_retries + 1):
try:
sandbox.set_timeout(
DEFAULT_TIMEOUT
) # refresh the timeout for each command execution
# sandbox.set_timeout(
# DEFAULT_TIMEOUT
# ) # refresh the timeout for each command execution

execution = sandbox.run_code(code_block)
execution = sandbox.run_code(code_block, timeout=DEFAULT_TIMEOUT)
return str(execution)
except Exception as e:
if attempt == max_retries:
Expand All @@ -153,7 +161,8 @@ async def run_python_code(code_block: str, sandbox_id: str) -> str:
finally:
# Set timeout before exit to prevent timeout after function exits
try:
sandbox.set_timeout(DEFAULT_TIMEOUT)
sandbox.beta_pause()
# sandbox.set_timeout(DEFAULT_TIMEOUT)
except Exception:
pass # Ignore timeout setting errors

Expand All @@ -179,9 +188,9 @@ async def upload_file_from_local_to_sandbox(
return f"[ERROR]: Failed to connect to sandbox {sandbox_id}, retry later. Make sure the sandbox is created and the id is correct."

try:
sandbox.set_timeout(
DEFAULT_TIMEOUT
) # refresh the timeout for each command execution
# sandbox.set_timeout(
# DEFAULT_TIMEOUT
# ) # refresh the timeout for each command execution

# Get the uploaded file path
uploaded_file_path = os.path.join(
Expand All @@ -190,15 +199,16 @@ async def upload_file_from_local_to_sandbox(

# Upload the file
with open(local_file_path, "rb") as f:
sandbox.files.write(uploaded_file_path, f)
sandbox.files.write(uploaded_file_path, f, request_timeout=DEFAULT_TIMEOUT)

return f"File uploaded to {uploaded_file_path}\n\n[INFO]: For directly reading local files without uploading to sandbox, consider using the `convert_to_markdown` tool which can read various file types (Doc, PPT, PDF, Excel, CSV, ZIP, etc.) directly from local paths or URLs. Note that `convert_to_markdown` doesn't support files already in the sandbox."
except Exception as e:
return f"[ERROR]: Failed to upload file {local_file_path} to sandbox {sandbox_id}: {e}\n\n[INFO]: This tool is for uploading local files to the sandbox. For security reasons, downloading files from sandbox to local system is not supported. Alternatively, consider using the `convert_to_markdown` tool which can directly read various file types (Doc, PPT, PDF, Excel, CSV, ZIP, etc.) from local paths or URLs without uploading to sandbox."
finally:
# Set timeout before exit to prevent timeout after function exits
try:
sandbox.set_timeout(DEFAULT_TIMEOUT)
sandbox.beta_pause()
# sandbox.set_timeout(DEFAULT_TIMEOUT)
except Exception:
pass # Ignore timeout setting errors

Expand All @@ -224,16 +234,18 @@ async def download_file_from_internet_to_sandbox(
return f"[ERROR]: Failed to connect to sandbox {sandbox_id}, retry later. Make sure the sandbox is created and the id is correct."

try:
sandbox.set_timeout(
DEFAULT_TIMEOUT
) # refresh the timeout for each command execution
# sandbox.set_timeout(
# DEFAULT_TIMEOUT
# ) # refresh the timeout for each command execution

downloaded_file_path = os.path.join(sandbox_file_path, os.path.basename(url))

# Download the file with retry logic
max_retries = 3
for attempt in range(1, max_retries + 1):
result = sandbox.commands.run(f"wget {url} -O {downloaded_file_path}")
result = sandbox.commands.run(
f"wget {url} -O {downloaded_file_path}", timeout=DEFAULT_TIMEOUT
)
if result.exit_code == 0:
return f"File downloaded to {downloaded_file_path}\n\n[INFO]: For directly reading files from internet URLs without downloading to sandbox, consider using the `convert_to_markdown` tool which can read various file types (Doc, PPT, PDF, Excel, CSV, ZIP, etc.) directly from URLs. Note that `convert_to_markdown` doesn't support files already in the sandbox."
elif attempt < max_retries:
Expand All @@ -246,7 +258,8 @@ async def download_file_from_internet_to_sandbox(
finally:
# Set timeout before exit to prevent timeout after function exits
try:
sandbox.set_timeout(DEFAULT_TIMEOUT)
sandbox.beta_pause()
# sandbox.set_timeout(DEFAULT_TIMEOUT)
except Exception:
pass # Ignore timeout setting errors

Expand All @@ -272,9 +285,9 @@ async def download_file_from_sandbox_to_local(
return f"[ERROR]: Failed to connect to sandbox {sandbox_id}, retry later. Make sure the sandbox is created and the id is correct."

try:
sandbox.set_timeout(
DEFAULT_TIMEOUT
) # refresh the timeout for each command execution
# sandbox.set_timeout(
# DEFAULT_TIMEOUT
# ) # refresh the timeout for each command execution

# Create tmpfiles directory if it doesn't exist
if not LOGS_DIR:
Expand All @@ -293,7 +306,9 @@ async def download_file_from_sandbox_to_local(

# Download the file
with open(local_file_path, "wb") as f:
content = sandbox.files.read(sandbox_file_path, format="bytes")
content = sandbox.files.read(
sandbox_file_path, format="bytes", request_timeout=DEFAULT_TIMEOUT
)
f.write(content)

return f"File downloaded successfully to: {local_file_path}\n\n[INFO]: The file can now be accessed by other tools (reading, question-answering, etc.) which only support local files and internet URLs, not sandbox files."
Expand All @@ -302,7 +317,8 @@ async def download_file_from_sandbox_to_local(
finally:
# Set timeout before exit to prevent timeout after function exits
try:
sandbox.set_timeout(DEFAULT_TIMEOUT)
sandbox.beta_pause()
# sandbox.set_timeout(DEFAULT_TIMEOUT)
except Exception:
pass # Ignore timeout setting errors

Expand Down
Loading