diff --git a/vizro-ai/changelog.d/20240131_142042_anna_xiong_refactor_ai_utils.md b/vizro-ai/changelog.d/20240131_142042_anna_xiong_refactor_ai_utils.md
new file mode 100644
index 000000000..f1f65e73c
--- /dev/null
+++ b/vizro-ai/changelog.d/20240131_142042_anna_xiong_refactor_ai_utils.md
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
diff --git a/vizro-ai/src/vizro_ai/_vizro_ai.py b/vizro-ai/src/vizro_ai/_vizro_ai.py
index 4808bff16..c3c378cf8 100644
--- a/vizro-ai/src/vizro_ai/_vizro_ai.py
+++ b/vizro-ai/src/vizro_ai/_vizro_ai.py
@@ -1,6 +1,5 @@
import logging
-import traceback
-from typing import Any, Callable, Dict, Optional, Union
+from typing import Any, Dict, Union
import pandas as pd
@@ -8,15 +7,11 @@
from vizro_ai.chains._llm_models import LLM_MODELS
from vizro_ai.components import GetCodeExplanation, GetDebugger
from vizro_ai.task_pipeline._pipeline_manager import PipelineManager
-from vizro_ai.utils import _safeguard_check
+from vizro_ai.utils.helper import DebugFailure, _debug_helper, _display_markdown_and_chart, _exec_code, _is_jupyter
logger = logging.getLogger(__name__)
-class DebugFailure(Exception):
- pass
-
-
class VizroAI:
"""Vizro-AI main class."""
@@ -136,75 +131,3 @@ def plot(
# TODO Tentative for integration test
if self._return_all_text:
return output_dict
-
-
-def _debug_helper(
- code_string: str, max_debug_retry: int, fix_chain: Callable, df: pd.DataFrame = None
-) -> Dict[bool, str]:
- """Debugging helper."""
- # TODO plug logic back into component
- retry_success = False
- last_exception = None
- for attempt in range(max_debug_retry):
- try:
- _exec_code(code=code_string, local_args={"df": df}, is_notebook_env=_is_jupyter())
- retry_success = True
- break
- except Exception as e:
- error_info = f"{traceback.format_exc()}"
- code_string = fix_chain(chain_input=error_info, code_snippet=code_string)
- last_exception = e
-
- code_string = code_string if retry_success else f"Failed to debug code {code_string}, error: {last_exception}"
-
- return {"debug_status": retry_success, "code_string": code_string}
-
-
-def _exec_code(
- code: str, local_args: Optional[Dict] = None, show_fig: bool = False, is_notebook_env: bool = True
-) -> None:
- """Execute code in notebook with correct namespace."""
- from IPython import get_ipython
-
- if show_fig and "\nfig.show()" not in code:
- code += "\nfig.show()"
- elif not show_fig:
- code = code.replace("fig.show()", "")
- namespace = get_ipython().user_ns if is_notebook_env else globals()
- if local_args:
- namespace.update(local_args)
- _safeguard_check(code)
-
- exec(code, namespace) # nosec
-
-
-# Taken from rich.console. See https://github.com/Textualize/rich.
-def _is_jupyter() -> bool: # pragma: no cover
- """Checks if we're running in a Jupyter notebook."""
- try:
- from IPython import get_ipython
- except NameError:
- return False
- ipython = get_ipython()
- shell = ipython.__class__.__name__
- if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell":
- return True # Jupyter notebook or qtconsole
- elif shell == "TerminalInteractiveShell":
- return False # Terminal running IPython
- else:
- return False # Other type (?)
-
-
-def _display_markdown_and_chart(df: pd.DataFrame, code_snippet: str, biz_insights: str, code_explain: str) -> None:
- # TODO change default test str to other
- """Display chart and Markdown format description in jupyter."""
- try:
- # pylint: disable=import-outside-toplevel
- from IPython.display import Markdown, display
- except Exception as exc:
- raise ImportError("Please install IPython before proceeding in jupyter environment.") from exc
- # TODO clean up the formatting markdown code
- markdown_code = f"```\n{code_snippet}\n```"
- output_text = f"
Insights:
\n\n{biz_insights}\n
Code:
\n\n{code_explain}\n{markdown_code}"
- display(Markdown(output_text))
- _exec_code(code_snippet, local_args={"df": df}, show_fig=True, is_notebook_env=_is_jupyter())
diff --git a/vizro-ai/src/vizro_ai/utils/__init__.py b/vizro-ai/src/vizro_ai/utils/__init__.py
index 3b664f31c..e69de29bb 100644
--- a/vizro-ai/src/vizro_ai/utils/__init__.py
+++ b/vizro-ai/src/vizro_ai/utils/__init__.py
@@ -1,3 +0,0 @@
-from .safeguard import _safeguard_check
-
-__all__ = ["_safeguard_check"]
diff --git a/vizro-ai/src/vizro_ai/utils/helper.py b/vizro-ai/src/vizro_ai/utils/helper.py
new file mode 100644
index 000000000..4bf904707
--- /dev/null
+++ b/vizro-ai/src/vizro_ai/utils/helper.py
@@ -0,0 +1,85 @@
+"""Helper Functions For Vizro AI."""
+import traceback
+from typing import Callable, Dict, Optional
+
+import pandas as pd
+
+from .safeguard import _safeguard_check
+
+
+# Taken from rich.console. See https://github.com/Textualize/rich.
+def _is_jupyter() -> bool: # pragma: no cover
+ """Checks if we're running in a Jupyter notebook."""
+ try:
+ from IPython import get_ipython
+ except NameError:
+ return False
+ ipython = get_ipython()
+ shell = ipython.__class__.__name__
+ if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell":
+ return True # Jupyter notebook or qtconsole
+ elif shell == "TerminalInteractiveShell":
+ return False # Terminal running IPython
+ else:
+ return False # Other type (?)
+
+
+def _debug_helper(
+ code_string: str, max_debug_retry: int, fix_chain: Callable, df: pd.DataFrame = None
+) -> Dict[bool, str]:
+ """Debugging helper."""
+ retry_success = False
+ last_exception = None
+ is_jupyter = _is_jupyter()
+ for attempt in range(max_debug_retry):
+ try:
+ _exec_code(code=code_string, local_args={"df": df}, is_notebook_env=is_jupyter)
+ retry_success = True
+ break
+ except Exception as e:
+ error_info = f"{traceback.format_exc()}"
+ code_string = fix_chain(chain_input=error_info, code_snippet=code_string)
+ last_exception = e
+
+ code_string = code_string if retry_success else f"Failed to debug code {code_string}, error: {last_exception}"
+
+ return {"debug_status": retry_success, "code_string": code_string}
+
+
+def _exec_code(
+ code: str, local_args: Optional[Dict] = None, show_fig: bool = False, is_notebook_env: bool = True
+) -> None:
+ """Execute code in notebook with correct namespace."""
+ from IPython import get_ipython
+
+ if show_fig and "\nfig.show()" not in code:
+ code += "\nfig.show()"
+ elif not show_fig:
+ code = code.replace("fig.show()", "")
+ namespace = get_ipython().user_ns if is_notebook_env else globals()
+ if local_args:
+ namespace.update(local_args)
+ _safeguard_check(code)
+
+ exec(code, namespace) # nosec
+
+
+def _display_markdown_and_chart(df: pd.DataFrame, code_snippet: str, biz_insights: str, code_explain: str) -> None:
+ # TODO change default test str to other
+ """Display chart and Markdown format description in jupyter."""
+ try:
+ # pylint: disable=import-outside-toplevel
+ from IPython.display import Markdown, display
+ except Exception as exc:
+ raise ImportError("Please install IPython before proceeding in jupyter environment.") from exc
+ # TODO clean up the formatting markdown code to render in jupyter
+ markdown_code = f"```\n{code_snippet}\n```"
+ output_text = f"Insights:
\n\n{biz_insights}\n
Code:
\n\n{code_explain}\n{markdown_code}"
+ display(Markdown(output_text))
+ _exec_code(code_snippet, local_args={"df": df}, show_fig=True, is_notebook_env=_is_jupyter())
+
+
+class DebugFailure(Exception):
+ """Debug Failure."""
+
+ pass
diff --git a/vizro-ai/tests/unit/vizro-ai/utils/test_safeguard_code.py b/vizro-ai/tests/unit/vizro-ai/utils/test_safeguard_code.py
index 533f03801..9bd58121c 100644
--- a/vizro-ai/tests/unit/vizro-ai/utils/test_safeguard_code.py
+++ b/vizro-ai/tests/unit/vizro-ai/utils/test_safeguard_code.py
@@ -1,7 +1,7 @@
import re
import pytest
-from vizro_ai.utils import _safeguard_check
+from vizro_ai.utils.safeguard import _safeguard_check
class TestMaliciousImports: