Skip to content

Commit b173704

Browse files
committed
refac
1 parent 2628662 commit b173704

3 files changed

Lines changed: 43 additions & 6 deletions

File tree

backend/open_webui/tools/builtin.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from open_webui.models.channels import Channels, ChannelMember, Channel
3737
from open_webui.models.messages import Messages, Message
3838
from open_webui.models.groups import Groups
39-
from open_webui.utils.sanitize import strip_markdown_code_fences
39+
from open_webui.utils.sanitize import sanitize_code
4040

4141
log = logging.getLogger(__name__)
4242

@@ -371,8 +371,8 @@ async def execute_code(
371371
return json.dumps({"error": "Request context not available"})
372372

373373
try:
374-
# Strip markdown fences if model included them
375-
code = strip_markdown_code_fences(code)
374+
# Sanitize code (strips ANSI codes and markdown fences)
375+
code = sanitize_code(code)
376376

377377
# Import blocked modules from config (same as middleware)
378378
from open_webui.config import CODE_INTERPRETER_BLOCKED_MODULES

backend/open_webui/utils/middleware.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
from open_webui.retrieval.utils import get_sources_from_items
7474

7575

76-
from open_webui.utils.sanitize import strip_markdown_code_fences
76+
from open_webui.utils.sanitize import sanitize_code
7777
from open_webui.utils.chat import generate_chat_completion
7878
from open_webui.utils.task import (
7979
get_task_model_id,
@@ -4175,8 +4175,8 @@ async def flush_pending_delta_data(threshold: int = 0):
41754175
try:
41764176
if content_blocks[-1]["attributes"].get("type") == "code":
41774177
code = content_blocks[-1]["content"]
4178-
# Strip markdown fences if model included them
4179-
code = strip_markdown_code_fences(code)
4178+
# Sanitize code (strips ANSI codes and markdown fences)
4179+
code = sanitize_code(code)
41804180

41814181
if CODE_INTERPRETER_BLOCKED_MODULES:
41824182
blocking_code = textwrap.dedent(

backend/open_webui/utils/sanitize.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
import re
22

3+
# ANSI escape code pattern - matches all common ANSI sequences
4+
# This includes color codes, cursor movement, and other terminal control sequences
5+
ANSI_ESCAPE_PATTERN = re.compile(r'\x1b\[[0-9;]*[A-Za-z]|\x1b\([AB]|\x1b[PX^_].*?\x1b\\|\x1b\].*?(?:\x07|\x1b\\)')
6+
7+
8+
def strip_ansi_codes(text: str) -> str:
9+
"""
10+
Strip ANSI escape codes from text.
11+
12+
ANSI escape codes can be introduced by LLMs that include terminal
13+
color codes in their output. These codes cause syntax errors when
14+
the code is sent to Jupyter for execution.
15+
16+
Common ANSI codes include:
17+
- Color codes: \x1b[31m (red), \x1b[32m (green), etc.
18+
- Reset codes: \x1b[0m, \x1b[39m
19+
- Cursor movement: \x1b[1A, \x1b[2J, etc.
20+
"""
21+
return ANSI_ESCAPE_PATTERN.sub('', text)
22+
323

424
def strip_markdown_code_fences(code: str) -> str:
525
"""
@@ -19,3 +39,20 @@ def strip_markdown_code_fences(code: str) -> str:
1939
# Remove closing fence
2040
code = re.sub(r"\n?```\s*$", "", code)
2141
return code.strip()
42+
43+
44+
def sanitize_code(code: str) -> str:
45+
"""
46+
Sanitize code for execution by applying all necessary cleanup steps.
47+
48+
This is the recommended function to use before sending code to
49+
interpreters like Jupyter or Pyodide.
50+
51+
Steps applied:
52+
1. Strip ANSI escape codes (from LLM output)
53+
2. Strip markdown code fences (if model included them)
54+
"""
55+
code = strip_ansi_codes(code)
56+
code = strip_markdown_code_fences(code)
57+
return code
58+

0 commit comments

Comments
 (0)