mito-ai 0.1.56__py3-none-any.whl → 0.1.57__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. mito_ai/__init__.py +2 -0
  2. mito_ai/_version.py +1 -1
  3. mito_ai/chart_wizard/__init__.py +3 -0
  4. mito_ai/chart_wizard/handlers.py +52 -0
  5. mito_ai/chart_wizard/urls.py +23 -0
  6. mito_ai/completions/completion_handlers/completion_handler.py +11 -2
  7. mito_ai/completions/completion_handlers/scratchpad_result_handler.py +66 -0
  8. mito_ai/completions/handlers.py +5 -0
  9. mito_ai/completions/models.py +23 -2
  10. mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +5 -3
  11. mito_ai/completions/prompt_builders/agent_system_message.py +97 -5
  12. mito_ai/completions/prompt_builders/chart_conversion_prompt.py +27 -0
  13. mito_ai/completions/prompt_builders/chat_system_message.py +2 -0
  14. mito_ai/completions/prompt_builders/prompt_constants.py +28 -0
  15. mito_ai/completions/prompt_builders/scratchpad_result_prompt.py +17 -0
  16. mito_ai/utils/provider_utils.py +8 -1
  17. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +102 -102
  18. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
  19. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
  20. mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.dfd7975de75d64db80d6.js → mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.9d26322f3e78beb2b666.js +2371 -257
  21. mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.9d26322f3e78beb2b666.js.map +1 -0
  22. mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.1e7b5cf362385f109883.js → mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.79c1ea8a3cda73a4cb6f.js +17 -17
  23. mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.1e7b5cf362385f109883.js.map → mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.79c1ea8a3cda73a4cb6f.js.map +1 -1
  24. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.css +7 -2
  25. {mito_ai-0.1.56.dist-info → mito_ai-0.1.57.dist-info}/METADATA +1 -1
  26. {mito_ai-0.1.56.dist-info → mito_ai-0.1.57.dist-info}/RECORD +51 -45
  27. mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.dfd7975de75d64db80d6.js.map +0 -1
  28. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  29. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
  30. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +0 -0
  31. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +0 -0
  32. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  33. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js +0 -0
  34. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js.map +0 -0
  35. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_apis_signOut_mjs-node_module-75790d.688c25857e7b81b1740f.js +0 -0
  36. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_apis_signOut_mjs-node_module-75790d.688c25857e7b81b1740f.js.map +0 -0
  37. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js +0 -0
  38. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js.map +0 -0
  39. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +0 -0
  40. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +0 -0
  41. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +0 -0
  42. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +0 -0
  43. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_react-dom_client_js-node_modules_aws-amplify_ui-react_dist_styles_css.b43d4249e4d3dac9ad7b.js +0 -0
  44. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_react-dom_client_js-node_modules_aws-amplify_ui-react_dist_styles_css.b43d4249e4d3dac9ad7b.js.map +0 -0
  45. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +0 -0
  46. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +0 -0
  47. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
  48. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
  49. {mito_ai-0.1.56.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.js +0 -0
  50. {mito_ai-0.1.56.dist-info → mito_ai-0.1.57.dist-info}/WHEEL +0 -0
  51. {mito_ai-0.1.56.dist-info → mito_ai-0.1.57.dist-info}/entry_points.txt +0 -0
  52. {mito_ai-0.1.56.dist-info → mito_ai-0.1.57.dist-info}/licenses/LICENSE +0 -0
mito_ai/__init__.py CHANGED
@@ -19,6 +19,7 @@ from mito_ai.app_manager.handlers import AppManagerHandler
19
19
  from mito_ai.file_uploads.urls import get_file_uploads_urls
20
20
  from mito_ai.user.urls import get_user_urls
21
21
  from mito_ai.chat_history.urls import get_chat_history_urls
22
+ from mito_ai.chart_wizard.urls import get_chart_wizard_urls
22
23
 
23
24
  # Force Matplotlib to use the Jupyter inline backend.
24
25
  # Background: importing Streamlit sets os.environ["MPLBACKEND"] = "Agg" very early.
@@ -109,6 +110,7 @@ def _load_jupyter_server_extension(server_app) -> None: # type: ignore
109
110
  handlers.extend(get_file_uploads_urls(base_url)) # type: ignore
110
111
  handlers.extend(get_user_urls(base_url)) # type: ignore
111
112
  handlers.extend(get_chat_history_urls(base_url, global_message_history)) # type: ignore
113
+ handlers.extend(get_chart_wizard_urls(base_url, open_ai_provider)) # type: ignore
112
114
 
113
115
  web_app.add_handlers(host_pattern, handlers)
114
116
  server_app.log.info("Loaded the mito_ai server extension")
mito_ai/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = VERSION = '0.1.56'
4
+ __version__ = VERSION = '0.1.57'
@@ -0,0 +1,3 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
@@ -0,0 +1,52 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ import json
5
+ import tornado
6
+ from typing import List
7
+ from jupyter_server.base.handlers import APIHandler
8
+ from openai.types.chat import ChatCompletionMessageParam
9
+ from mito_ai.completions.providers import OpenAIProvider
10
+ from mito_ai.utils.anthropic_utils import FAST_ANTHROPIC_MODEL
11
+ from mito_ai.completions.models import MessageType
12
+ from mito_ai.completions.prompt_builders.chart_conversion_prompt import create_chart_conversion_prompt
13
+
14
+ class ChartWizardHandler(APIHandler):
15
+ def initialize(self, llm: OpenAIProvider) -> None:
16
+ """Initialize the handler with the LLM provider."""
17
+ super().initialize()
18
+ self._llm = llm
19
+
20
+ @tornado.web.authenticated
21
+ async def post(self) -> None:
22
+ """POST endpoint that receives code from the frontend and sends it to LLM."""
23
+ try:
24
+ data = json.loads(self.request.body.decode('utf-8'))
25
+ code = data.get('code', '')
26
+
27
+ # Create prompt using the prompt builder
28
+ prompt = create_chart_conversion_prompt(code)
29
+
30
+ # Call LLM
31
+ messages: List[ChatCompletionMessageParam] = [{"role": "user", "content": prompt}]
32
+ converted_code = await self._llm.request_completions(
33
+ messages=messages,
34
+ model=FAST_ANTHROPIC_MODEL,
35
+ message_type=MessageType.CHAT,
36
+ thread_id=None
37
+ )
38
+
39
+ # Return the converted code
40
+ self.write({
41
+ "message": "Code converted successfully",
42
+ "converted_code": converted_code
43
+ })
44
+ self.finish()
45
+ except json.JSONDecodeError:
46
+ self.set_status(400)
47
+ self.write({"error": "Invalid JSON in request body"})
48
+ self.finish()
49
+ except Exception as e:
50
+ self.set_status(500)
51
+ self.write({"error": str(e)})
52
+ self.finish()
@@ -0,0 +1,23 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ from typing import List, Tuple, Any
5
+ from jupyter_server.utils import url_path_join
6
+ from mito_ai.chart_wizard.handlers import ChartWizardHandler
7
+ from mito_ai.completions.providers import OpenAIProvider
8
+
9
+
10
+ def get_chart_wizard_urls(base_url: str, llm: OpenAIProvider) -> List[Tuple[str, Any, dict]]:
11
+ """Get all chart wizard related URL patterns.
12
+
13
+ Args:
14
+ base_url: The base URL for the Jupyter server
15
+ llm: The OpenAI provider instance
16
+
17
+ Returns:
18
+ List of (url_pattern, handler_class, handler_kwargs) tuples
19
+ """
20
+ BASE_URL = base_url + "/mito-ai"
21
+ return [
22
+ (url_path_join(BASE_URL, "chart-wizard"), ChartWizardHandler, {"llm": llm}),
23
+ ]
@@ -3,11 +3,20 @@
3
3
 
4
4
  from typing import Protocol, TypeVar
5
5
  from abc import abstractmethod, ABCMeta
6
- from mito_ai.completions.models import ChatMessageMetadata, SmartDebugMetadata, CodeExplainMetadata, AgentExecutionMetadata, InlineCompleterMetadata, AgentSmartDebugMetadata
6
+ from mito_ai.completions.models import ChatMessageMetadata, ScratchpadResultMetadata, SmartDebugMetadata, CodeExplainMetadata, AgentExecutionMetadata, InlineCompleterMetadata, AgentSmartDebugMetadata
7
7
  from mito_ai.completions.providers import OpenAIProvider
8
8
  from mito_ai.completions.message_history import GlobalMessageHistory
9
9
 
10
- T = TypeVar('T', ChatMessageMetadata, SmartDebugMetadata, CodeExplainMetadata, AgentExecutionMetadata, AgentSmartDebugMetadata, InlineCompleterMetadata, contravariant=True)
10
+ T = TypeVar('T',
11
+ ChatMessageMetadata,
12
+ SmartDebugMetadata,
13
+ CodeExplainMetadata,
14
+ AgentExecutionMetadata,
15
+ AgentSmartDebugMetadata,
16
+ InlineCompleterMetadata,
17
+ ScratchpadResultMetadata,
18
+ contravariant=True
19
+ )
11
20
 
12
21
  class CompletionHandler(Protocol[T], metaclass=ABCMeta):
13
22
  """Protocol defining the interface for completion handlers.
@@ -0,0 +1,66 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ from typing import List, Literal, Union
5
+ from openai.types.chat import ChatCompletionMessageParam
6
+ from mito_ai.completions.models import ScratchpadResultMetadata, MessageType, ResponseFormatInfo, AgentResponse
7
+ from mito_ai.completions.prompt_builders.scratchpad_result_prompt import create_scratchpad_result_prompt
8
+ from mito_ai.completions.providers import OpenAIProvider
9
+ from mito_ai.completions.message_history import GlobalMessageHistory
10
+ from mito_ai.completions.completion_handlers.completion_handler import CompletionHandler
11
+ from mito_ai.completions.completion_handlers.utils import append_agent_system_message, create_ai_optimized_message
12
+
13
+ __all__ = ["get_scratchpad_result_completion"]
14
+
15
+ class ScratchpadResultHandler(CompletionHandler[ScratchpadResultMetadata]):
16
+ """Handler for scratchpad result completions."""
17
+
18
+ @staticmethod
19
+ async def get_completion(
20
+ metadata: ScratchpadResultMetadata,
21
+ provider: OpenAIProvider,
22
+ message_history: GlobalMessageHistory,
23
+ model: str
24
+ ) -> str:
25
+ """Get a scratchpad result completion from the AI provider."""
26
+
27
+ if metadata.index is not None:
28
+ message_history.truncate_histories(
29
+ thread_id=metadata.threadId,
30
+ index=metadata.index
31
+ )
32
+
33
+ # Add the system message if it doesn't already exist
34
+ await append_agent_system_message(message_history, model, provider, metadata.threadId, True)
35
+
36
+ # Create the prompt
37
+ prompt = create_scratchpad_result_prompt(metadata)
38
+ display_prompt = ""
39
+
40
+ # Add the prompt to the message history
41
+ new_ai_optimized_message = create_ai_optimized_message(prompt, None, None)
42
+ new_display_optimized_message: ChatCompletionMessageParam = {"role": "user", "content": display_prompt}
43
+
44
+ await message_history.append_message(new_ai_optimized_message, new_display_optimized_message, model, provider, metadata.threadId)
45
+
46
+ # Get the completion
47
+ completion = await provider.request_completions(
48
+ messages=message_history.get_ai_optimized_history(metadata.threadId),
49
+ model=model,
50
+ response_format_info=ResponseFormatInfo(
51
+ name='agent_response',
52
+ format=AgentResponse
53
+ ),
54
+ message_type=MessageType.AGENT_SCRATCHPAD_RESULT,
55
+ user_input="",
56
+ thread_id=metadata.threadId
57
+ )
58
+
59
+ ai_response_message: ChatCompletionMessageParam = {"role": "assistant", "content": completion}
60
+
61
+ await message_history.append_message(ai_response_message, ai_response_message, model, provider, metadata.threadId)
62
+
63
+ return completion
64
+
65
+ # Use the static method directly
66
+ get_scratchpad_result_completion = ScratchpadResultHandler.get_completion
@@ -34,6 +34,7 @@ from mito_ai.completions.models import (
34
34
  CodeExplainMetadata,
35
35
  AgentExecutionMetadata,
36
36
  InlineCompleterMetadata,
37
+ ScratchpadResultMetadata,
37
38
  MessageType
38
39
  )
39
40
  from mito_ai.completions.providers import OpenAIProvider
@@ -45,6 +46,7 @@ from mito_ai.completions.completion_handlers.code_explain_handler import get_cod
45
46
  from mito_ai.completions.completion_handlers.inline_completer_handler import get_inline_completion
46
47
  from mito_ai.completions.completion_handlers.agent_execution_handler import get_agent_execution_completion
47
48
  from mito_ai.completions.completion_handlers.agent_auto_error_fixup_handler import get_agent_auto_error_fixup_completion
49
+ from mito_ai.completions.completion_handlers.scratchpad_result_handler import get_scratchpad_result_completion
48
50
  from mito_ai.utils.telemetry_utils import identify
49
51
 
50
52
  FALLBACK_MODEL = "gpt-4.1" # Default model to use for safety
@@ -314,6 +316,9 @@ class CompletionHandler(JupyterHandler, WebSocketHandler):
314
316
  elif type == MessageType.AGENT_AUTO_ERROR_FIXUP:
315
317
  agent_auto_error_fixup_metadata = AgentSmartDebugMetadata(**metadata_dict)
316
318
  completion = await get_agent_auto_error_fixup_completion(agent_auto_error_fixup_metadata, self._llm, self._message_history, model)
319
+ elif type == MessageType.AGENT_SCRATCHPAD_RESULT:
320
+ scratchpad_result_metadata = ScratchpadResultMetadata(**metadata_dict)
321
+ completion = await get_scratchpad_result_completion(scratchpad_result_metadata, self._llm, self._message_history, model)
317
322
  elif type == MessageType.INLINE_COMPLETION:
318
323
  inline_completer_metadata = InlineCompleterMetadata(**metadata_dict)
319
324
  completion = await get_inline_completion(inline_completer_metadata, self._llm, self._message_history, model)
@@ -29,13 +29,26 @@ class CellUpdate(BaseModel):
29
29
  # for now and rely on the AI to respond with the correct types, following the format
30
30
  # that we show it in the system prompt.
31
31
  class AgentResponse(BaseModel):
32
- type: Literal['cell_update', 'get_cell_output', 'run_all_cells', 'finished_task', 'create_streamlit_app', 'edit_streamlit_app']
32
+ type: Literal[
33
+ 'cell_update',
34
+ 'get_cell_output',
35
+ 'run_all_cells',
36
+ 'finished_task',
37
+ 'create_streamlit_app',
38
+ 'edit_streamlit_app',
39
+ 'ask_user_question',
40
+ 'scratchpad',
41
+ ]
33
42
  message: str
34
43
  cell_update: Optional[CellUpdate]
35
44
  get_cell_output_cell_id: Optional[str]
36
45
  next_steps: Optional[List[str]]
37
46
  analysis_assumptions: Optional[List[str]]
38
47
  streamlit_app_prompt: Optional[str]
48
+ question: Optional[str]
49
+ answers: Optional[List[str]]
50
+ scratchpad_code: Optional[str]
51
+ scratchpad_summary: Optional[str]
39
52
 
40
53
 
41
54
  @dataclass(frozen=True)
@@ -67,6 +80,7 @@ class MessageType(Enum):
67
80
  STREAMLIT_CONVERSION = "streamlit_conversion"
68
81
  STOP_AGENT = "stop_agent"
69
82
  DEPLOY_APP = "deploy_app"
83
+ AGENT_SCRATCHPAD_RESULT = "agent:scratchpad-result"
70
84
 
71
85
 
72
86
  @dataclass(frozen=True)
@@ -136,13 +150,20 @@ class CodeExplainMetadata():
136
150
  activeCellCode: Optional[str] = None
137
151
 
138
152
  @dataclass(frozen=True)
139
- class InlineCompleterMetadata():
153
+ class InlineCompleterMetadata():
140
154
  promptType: Literal['inline_completion']
141
155
  prefix: str
142
156
  suffix: str
143
157
  variables: Optional[List[str]] = None
144
158
  files: Optional[List[str]] = None
145
159
 
160
+ @dataclass(frozen=True)
161
+ class ScratchpadResultMetadata():
162
+ promptType: Literal['agent:scratchpad-result']
163
+ threadId: ThreadID
164
+ scratchpadResult: str
165
+ index: Optional[int] = None
166
+
146
167
  @dataclass(frozen=True)
147
168
  class CompletionRequest:
148
169
  """
@@ -22,12 +22,12 @@ def create_agent_smart_debug_prompt(md: AgentSmartDebugMetadata) -> str:
22
22
 
23
23
  Use this strategy for this message only. After this message, continue using the original set of instructions that I provided you.
24
24
 
25
- It is very important that When fixing this error, you do not change the original intent of the code cell.
25
+ It is very important that when fixing this error, you do not change the original intent of the code cell.
26
26
 
27
27
  To fix this error, take the following approach:
28
28
  Step 1: ERROR ANALYSIS: Analyze the error message to identify why the code cell errored.
29
29
  Step 2: INTENT PRESERVATION: Make sure you understand the intent of the CELL_UPDATE so that you can be sure to preserve it when you create a new CELL_UPDATE
30
- Step 3: ERROR CORRECTION: Respond with a new CELL_UPDATE that is applied to the same cell as the erroring CELL_UPDATE.
30
+ Step 3: ERROR CORRECTION: Respond with a new CELL_UPDATE that is applied to the same cell as the erroring CELL_UPDATE or use the ASK_USER_QUESTION tool to get more information about how to proceed.
31
31
 
32
32
  INSTRUCTIONS FOR EACH PHASE
33
33
 
@@ -43,13 +43,15 @@ INTENT PRESERVATION:
43
43
 
44
44
  ERROR CORRECTION:
45
45
 
46
- - Return the full, updated version of cell {md.error_message_producing_code_cell_id} with the error fixed and a short explanation of the error.
46
+ - Use one of your tools to correct the error or get more information from the user on how to proceed.
47
+ - If you use the CELL_UPDATE tool, you must reutn the full updated version of cell {md.error_message_producing_code_cell_id} with the error fixed and a short explanation of the error.
47
48
  - You can only update code in {md.error_message_producing_code_cell_id}. You are unable to edit the code in any other cell when resolving this error.
48
49
  - Propose a solution that fixes the error and does not change the user's intent.
49
50
  - Make the solution as simple as possible.
50
51
  - Reuse as much of the existing code as possible.
51
52
  - DO NOT ADD TEMPORARY COMMENTS like '# Fixed the typo here' or '# Added this line to fix the error'
52
53
  - If you encounter a ModuleNotFoundError, you can install the package by adding the the following line to the top of the code cell: `!pip install <package_name> --quiet`.
54
+ - If the error is not resolvable without getting more information from the user, you can respond with a ASK_USER_QUESTION tool call.
53
55
  - If you encounter a NameError, you can use the RUN_ALL_CELLS tool to run all cells from the top of the notebook to the bottom to bring the variable into scope.
54
56
  RUN_ALL_CELLS:
55
57
  When you want to execute all cells in the notebook from top to bottom, respond with this format:
@@ -4,13 +4,13 @@
4
4
  from typing import List
5
5
  from mito_ai.completions.prompt_builders.prompt_section_registry import SG, Prompt
6
6
  from mito_ai.completions.prompt_builders.prompt_constants import (
7
+ CHART_CONFIG_RULES,
7
8
  CITATION_RULES,
8
9
  CELL_REFERENCE_RULES,
9
10
  get_database_rules
10
11
  )
11
12
  from mito_ai.completions.prompt_builders.prompt_section_registry.base import PromptSection
12
13
 
13
-
14
14
  def create_agent_system_message_prompt(isChromeBrowser: bool) -> str:
15
15
 
16
16
  # The GET_CELL_OUTPUT tool only works on Chrome based browsers.
@@ -28,6 +28,8 @@ The user is going to ask you to guide them as they complete a task. You will hel
28
28
  You have access to a set of tools that you can use to accomplish the task you've been given. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
29
29
 
30
30
  Each time you use a tool, except for the finished_task tool, the user will execute the tool and provide you with updated information about the notebook and variables defined in the kernel to help you decide what to do next."""))
31
+
32
+ sections.append(SG.Generic("Chart Config Rules", CHART_CONFIG_RULES))
31
33
 
32
34
  sections.append(SG.Generic("TOOL: CELL_UPDATE", """
33
35
 
@@ -223,6 +225,85 @@ Important information:
223
225
  4. If running all cells results in an error, the system will automatically handle the error through the normal error fixing process.
224
226
  5. Do not use this tool repeatedly if it continues to produce errors - instead, focus on fixing the specific error that occurred."""))
225
227
 
228
+ # SCRATCHPAD tool
229
+ sections.append(SG.Generic("TOOL: SCRATCHPAD", """
230
+ When you need to explore data, check the filesystem, analyze mappings, or look up values without leaving code in the notebook, use the SCRATCHPAD tool.
231
+
232
+ Format:
233
+ {{
234
+ type: 'scratchpad',
235
+ message: str,
236
+ scratchpad_code: str,
237
+ scratchpad_summary: str
238
+ }}
239
+
240
+ Important information:
241
+ 1. The scratchpad_code will execute silently against the same kernel as your notebook, so you have access to all variables and dataframes.
242
+ 2. Any variables you create in scratchpad code MUST be prefixed with "scratch_" (e.g., use "scratch_temp_df" not "temp_df", use "scratch_files" not "files"). This prevents them from appearing in the variable list and confusing future decisions.
243
+ 3. CRITICAL: Do NOT modify existing variables. If you need to explore or modify data, create a copy with the scratch_ prefix first. For example, use "scratch_df = df.copy()" and then modify scratch_df, rather than modifying df directly. This ensures existing variables remain unchanged.
244
+ 4. Structure your code to print the information you need. Use print() statements for output that will be captured.
245
+ 5. If you need structured data, consider using JSON: `import json; print(json.dumps(your_data))`
246
+ 6. The results (including any errors) will be included in your next message, so you can use them to inform your next action.
247
+ 7. If the code errors, the error message and traceback will be included in the results. You can then decide to fix the code and try again, ask the user a question, or take a different approach.
248
+ 8. Use scratchpad for exploration work that doesn't belong in the final notebook. Once you have the information, create clean CELL_UPDATES with hardcoded values.
249
+ 9. The scratchpad_summary must be a very short phrase (1–5 words maximum) that begins with a verb ending in "-ing" (e.g., "Checking files", "Exploring data", "Analyzing mappings", "Looking up values"). Avoid full sentences or explanations. This should read like a quick commit message or code label, not a description.
250
+
251
+ Example:
252
+ {{
253
+ type: 'scratchpad',
254
+ message: "I'll check what files are in the current directory to find the data file.",
255
+ scratchpad_code: "import os\\nscratch_files = os.listdir('.')\\nprint('Files:', scratch_files)\\nfor scratch_file in scratch_files:\\n if scratch_file.endswith('.csv'):\\n print(f'CSV file found: {scratch_file}')",
256
+ scratchpad_summary: "Checking files"
257
+ }}
258
+
259
+ Example with dataframe exploration:
260
+ {{
261
+ type: 'scratchpad',
262
+ message: "I'll explore the dataframe structure to understand the columns.",
263
+ scratchpad_code: "scratch_df = df.copy()\\nprint('Columns:', scratch_df.columns.tolist())\\nprint('Shape:', scratch_df.shape)\\nprint('First few rows:')\\nprint(scratch_df.head())",
264
+ scratchpad_summary: "Exploring dataframe"
265
+ }}
266
+ """))
267
+
268
+ # ASK_USER_QUESTION tool
269
+ sections.append(SG.Generic("TOOL: ASK_USER_QUESTION", f"""
270
+
271
+ When you have a specific question that you the user to answer so that you can figure out how to proceed in your work, you can respond in this format:
272
+
273
+ {{
274
+ type: 'ask_user_question',
275
+ message: str,
276
+ question: str,
277
+ answers: Optional[List[str]]
278
+ }}
279
+
280
+ Important information:
281
+ 1. Use this tool when you need clarification from the user on how to proceed. Common scenarios include:
282
+ - A file or resource doesn't exist and you need to know how to proceed
283
+ - There are multiple valid approaches and you want the user to choose
284
+ - You need clarification on ambiguous requirements
285
+ - You encounter an error that requires user input to resolve
286
+ 2. The message should be a short description of what you've tried so far and why you need to ask the user a question now. This helps the user understand the context. The message provides background information but should NOT contain the actual question.
287
+ 3. The question field is REQUIRED and must always be provided. It cannot be null or empty. The question should be a clear, direct question that ends with a question mark. It should be concise and direct - do NOT include instructions or explanations in the question itself, as the answer options should make it clear what information is needed. For example, instead of "Which companies would you like me to compare Meta's stock performance against? Please provide a list of company names or stock tickers", just ask "Which companies would you like me to compare Meta's stock performance against?" The answer options will make it clear that company names or tickers are expected.
288
+ 4. The message and question serve different purposes: the message provides context about what you've tried, while the question is the actual question the user needs to answer. If your message contains a question, extract that question into the question field. For example, if your message says "I need to understand how you'd like to access the tweets", your question should be something like "How would you like to access the tweets?"
289
+ 5. Use the optional list of answers to provide the user multiple choice options to choose from. If it is an open ended question that you cannot create helpful multiple choice answers for, leave it blank and the user will respond in the text input field.
290
+ 6. When creating multiple choice answer options:
291
+ - Make each option distinct and meaningful - avoid options that differ only slightly from each other.
292
+ - If there are no obvious predefined answers, leave it blank and the user will respond in the text input field.
293
+ 7. After the user responds to your question, you will receive their response in the next message and can continue with the task based on their answer.
294
+ 8. Do not use this tool for trivial questions that you can infer from context. Only use it when you cannot proceed in the user's task without answering your specific question first.
295
+
296
+ <Example>
297
+ {{
298
+ type: 'ask_user_question',
299
+ message: "I tried importing apple_prices.csv and confirmed that it does not exist in the current working directory.",
300
+ question: "The file apple_prices.csv does not exist. How do you want to proceed?",
301
+ answers: ["Pull Apple Stock prices using yfinance API", "Create placeholder data", "Skip this step"]
302
+ }}
303
+ </Example>
304
+
305
+ """))
306
+
226
307
  # CREATE_STREAMLIT_APP tool
227
308
  sections.append(SG.Generic("TOOL: CREATE_STREAMLIT_APP", """
228
309
 
@@ -316,18 +397,29 @@ Important information:
316
397
  # RULES section
317
398
  sections.append(SG.Generic("RULES", """
318
399
  - You are working in a Jupyter Lab environment in a .ipynb file.
319
- - In each message you can choose one of the tools to respond with. BUT YOU WILL GET TO SEND MULTIPLE MESSAGES TO THE USER TO ACCOMPLISH YOUR TASK SO DO NOT TRY TO ACCOMPLISH YOUR TASK IN A SINGLE MESSAGE.
400
+ - In each message you can choose one of the tools to respond with. YOU WILL GET TO SEND MULTIPLE MESSAGES TO THE USER TO ACCOMPLISH YOUR TASK SO DO NOT TRY TO ACCOMPLISH YOUR TASK IN A SINGLE MESSAGE.
320
401
  - After you send a CELL_UPDATE, the user will send you a message with the updated variables, code, and files in the current directory. You will use this information to decide what to do next, so it is critical that you wait for the user's response after each CELL_UPDATE before deciding your next action.
321
- - When updating code, keep as much of the original code as possible and do not recreate variables that already exist.
322
402
  - When writing the message, do not explain to the user how to use the CELL_UPDATE or FINISHED_TASK response, they will already know how to use them. Just provide a summary of your thought process. Do not reference any Cell IDs in the message.
323
403
  - When writing the message, do not include leading words like "Explanation:" or "Thought process:". Just provide a summary of your thought process.
324
404
  - When writing the message, use tickmarks when referencing specific variable names. For example, write `sales_df` instead of "sales_df" or just sales_df."""))
325
405
 
326
406
  # CODE STYLE section
327
407
  sections.append(SG.Generic("CODE STYLE", """
328
- - Avoid using try/except blocks and other defensive programming patterns (like checking if files exist before reading them, verifying variables are defined before using them, etc.) unless there is a really good reason. In Jupyter notebooks, errors should surface immediately so users can identify and fix issues. When errors are caught and suppressed or when defensive checks hide problems, users continue running broken code without realizing it, and the agent's auto-error-fix loop cannot trigger. If a column doesn't exist, a file is missing, a variable isn't defined, or a module isn't installed, let it error. The user needs to know.
408
+ - When updating code, keep as much of the original code as possible and do not recreate variables that already exist.
329
409
  - When you want to display a dataframe to the user, just write the dataframe on the last line of the code cell instead of writing print(<dataframe name>). Jupyter will automatically display the dataframe in the notebook.
330
- - When importing matplotlib, write the code `%matplotlib inline` to make sure the graphs render in Jupyter."""))
410
+ - When importing matplotlib, write the code `%matplotlib inline` to make sure the graphs render in Jupyter.
411
+ - Avoid adding try/except blocks unless there is a very good reason. Do not use them for things like:
412
+ ```
413
+ try:
414
+ df = pd.read_csv('my_data.csv')
415
+ except:
416
+ print("File not found")
417
+ ```
418
+ Instead, just let the cell error and use the ask_user_question tool to figure out how to proceed.
419
+ - Avoid defensive if statements like checking if a variable exists in the globals or verifying that a column exists. Instead, just let the code error and use the ask_user_question tool to figure out how to proceed.
420
+ - Do not simulate the data without the user explicity asking you to do so.
421
+ - Do not replace broken code with print statements that explain the issue. Instead, leave the broken code in the notebook and use the ask_user_question tools to communicate the issue to the user and figure out how to proceed.
422
+ """))
331
423
 
332
424
  # CITATION_RULES
333
425
  sections.append(SG.Generic("Citation Rules", f"""{CITATION_RULES}
@@ -0,0 +1,27 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ from typing import List
5
+ from mito_ai.completions.prompt_builders.prompt_section_registry import SG, Prompt
6
+ from mito_ai.completions.prompt_builders.prompt_section_registry.base import PromptSection
7
+ from mito_ai.completions.prompt_builders.prompt_constants import CHART_CONFIG_RULES
8
+
9
+ def create_chart_conversion_prompt(code: str) -> str:
10
+ """
11
+ Create a prompt for converting matplotlib chart code to be used with the Chart Wizard.
12
+
13
+ Args:
14
+ code: The matplotlib chart code to convert
15
+
16
+ Returns:
17
+ A formatted prompt string
18
+ """
19
+ sections: List[PromptSection] = []
20
+
21
+ sections.append(SG.Generic("Instructions", "The following code contains a matplotlib chart. However, the chart must be converted to a specific format for use in our tool. Below you will find the rules used to create an acceptable chart; use these rules to reformat the code."))
22
+
23
+ sections.append(SG.Generic("Chart Config Rules", CHART_CONFIG_RULES))
24
+ sections.append(SG.Generic("Code to Convert", f"```python\n{code}\n```"))
25
+
26
+ prompt = Prompt(sections)
27
+ return str(prompt)
@@ -4,6 +4,7 @@
4
4
  from typing import List
5
5
  from mito_ai.completions.prompt_builders.prompt_section_registry import SG, Prompt
6
6
  from mito_ai.completions.prompt_builders.prompt_constants import (
7
+ CHART_CONFIG_RULES,
7
8
  CHAT_CODE_FORMATTING_RULES,
8
9
  CITATION_RULES,
9
10
  CELL_REFERENCE_RULES,
@@ -31,6 +32,7 @@ Other useful information:
31
32
  2. If the user asks you to generate a dashboard, app, or streamlit app for them, you should tell them that they must use Agent mode to complete the task. You are not able to automatically switch the user to agent mode, but they can switch to it themselves by using the Chat/Agent mode toggle in the bottom left corner of the Ai taskpane.
32
33
  """))
33
34
 
35
+ sections.append(SG.Generic("Chart Config Rules", CHART_CONFIG_RULES))
34
36
  sections.append(SG.Generic("DatabaseRules", get_database_rules()))
35
37
  sections.append(SG.Generic("Citation Rules", CITATION_RULES))
36
38
  sections.append(SG.Generic("Cell Reference Rules", CELL_REFERENCE_RULES))
@@ -11,6 +11,34 @@ import json
11
11
  from typing import Final
12
12
  from mito_ai.utils.schema import MITO_FOLDER
13
13
 
14
+ CHART_CONFIG_RULES = """
15
+ When creating a matplotlib chart, you must use the `# === CHART CONFIG ===` and `# === END CONFIG ===` markers to indicate the start and end of the chart configuration section.
16
+
17
+ The chart configuration section is a list of variables used to customize the chart. This includes the titles, labels, colors, and any variables that affect the chart's appearance.
18
+
19
+ Rules:
20
+ - All imports must appear at the top, before the chart configuration section.
21
+ - Variables with multiple words should be underscore-separated.
22
+ - All colors should be in hex format (e.g., "#3498db"). Use quotes around the hex string: COLOR = "#3498db" or COLOR = '#3498db'. Do NOT nest quotes.
23
+ - Variables can only be strings, numbers, booleans, tuples, or lists.
24
+ - NEVER include comments on the same line as a variable assignment. Each variable assignment must be on its own line with no trailing comments.
25
+ - For string values, use either single or double quotes (e.g., TITLE = "Sales by Product" or TITLE = 'Sales by Product'). Do not use nested quotes (e.g., do NOT use '"value"').
26
+
27
+ Common Mistakes to Avoid:
28
+ - WRONG: COLOR = '"#1877F2" # Meta Blue' (nested quotes and inline comment)
29
+ - WRONG: COLOR = "#1877F2" # Meta Blue (inline comment)
30
+ - WRONG: COLOR = '"#1877F2"' (nested quotes)
31
+ - CORRECT: COLOR = "#1877F2" (simple hex string, no nested quotes, no inline comments)
32
+
33
+ Example:
34
+ # === CHART CONFIG ===
35
+ TITLE = "Sales by Product"
36
+ X_LABEL = "Product"
37
+ Y_LABEL = "Sales"
38
+ BAR_COLOR = "#000000"
39
+ # === END CONFIG ===
40
+ """
41
+
14
42
  CITATION_RULES = """
15
43
  It is important that the user is able to verify any insights that you share with them about their data. To make this easy for the user, you must cite the lines of code that you are drawing the insight from. To provide a citation, use one of the following formats inline in your response:
16
44
 
@@ -0,0 +1,17 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the GNU Affero General Public License v3.0 License.
3
+
4
+ from typing import List
5
+ from mito_ai.completions.models import ScratchpadResultMetadata
6
+ from mito_ai.completions.prompt_builders.prompt_section_registry import SG, Prompt
7
+ from mito_ai.completions.prompt_builders.prompt_section_registry.base import PromptSection
8
+
9
+
10
+ def create_scratchpad_result_prompt(md: ScratchpadResultMetadata) -> str:
11
+ sections: List[PromptSection] = [
12
+ SG.Generic("Reminder", "Continue working on your current task using the scratchpad results below."),
13
+ SG.Generic("Scratchpad Result", f"The result of your scratchpad is: {md.scratchpadResult}"),
14
+ ]
15
+
16
+ prompt = Prompt(sections)
17
+ return str(prompt)
@@ -36,7 +36,14 @@ def does_message_require_fast_model(message_type: MessageType) -> bool:
36
36
  so they don't slow down the user's experience.
37
37
  """
38
38
 
39
- if message_type in (MessageType.CHAT, MessageType.SMART_DEBUG, MessageType.CODE_EXPLAIN, MessageType.AGENT_EXECUTION, MessageType.AGENT_AUTO_ERROR_FIXUP):
39
+ if message_type in (
40
+ MessageType.CHAT,
41
+ MessageType.SMART_DEBUG,
42
+ MessageType.CODE_EXPLAIN,
43
+ MessageType.AGENT_EXECUTION,
44
+ MessageType.AGENT_SCRATCHPAD_RESULT,
45
+ MessageType.AGENT_AUTO_ERROR_FIXUP,
46
+ ):
40
47
  return False
41
48
  elif message_type in (MessageType.INLINE_COMPLETION, MessageType.CHAT_NAME_GENERATION):
42
49
  return True