Skip to content

Commit 3aa79da

Browse files
authored
Merge pull request #2147 from mito-ds/dev
Release Jan 14, 2026
2 parents d56b330 + 9da5ff7 commit 3aa79da

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3041
-121
lines changed

.cursor/commands/verify.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Verify Feature Command
2+
3+
When invoked, automatically test and fix the feature that you just created using the Browser tool to access JupyterLab. Continue correcting and verifying the feature until it works.
4+
5+
## Execution Steps
6+
7+
### 0. Setting up Environment
8+
9+
Make sure that the code passes linting first. Check the typescript terminal that is running for errors. You must resolve those first to ensure you are testing the most up to date version of the code.
10+
11+
### 1. Check JupyterLab Status
12+
13+
First, verify JupyterLab is running on port 8888. Navigate to:
14+
```
15+
http://localhost:8888/lab?token=dev-token-for-cursor-testing-12345
16+
```
17+
18+
### 2. Navigate and Load
19+
20+
Use browser to navigate to JupyterLab URL above. Wait 3-5 seconds for full page load, then take a snapshot to understand current state.
21+
22+
### 3. Identify Feature to Test
23+
24+
Use the conversation history to identify the feature to test. Your goal is to QA the new feature that was just created.
25+
26+
### 4. Test the Feature
27+
28+
Interact with the feature using browser tools:
29+
30+
### 5. Check for Issues
31+
32+
After each interaction:
33+
- **Browser console**: Use `browser_console_messages` to check for JavaScript/TypeScript errors
34+
- **Network requests**: Use `browser_network_requests` to verify API calls succeed
35+
- **UI state**: Take snapshots to verify elements render correctly
36+
- **Functionality**: Verify feature works as intended
37+
38+
### 6. Fix Issues Found
39+
40+
- Fix the issues you found if if they are related to the feature you are testing
41+
- Document any additional issues you found that are not related to the feature you are testing
42+
43+
### 7. Rebuild and Re-test
44+
45+
After making fixes:
46+
47+
1. **Setup Environment**:
48+
- For frontend changes: Wait a few seconds for rebuild to complete
49+
- For backend changes: Restart and relaunch the server
50+
51+
2. **Refresh browser**:
52+
- Take new snapshot
53+
- Re-test the feature
54+
- Verify fix worked
55+
56+
### 8. Iterate Until Complete
57+
58+
Repeat steps 4-7 until the feature works correctly.

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# Ignore all the scratch files
2-
scratch*
3-
41
# Ignore all the ipynb files in mitosheet and mito-ai
52
mitosheet/**/*.ipynb
63
mito-ai/**/*.ipynb

mito-ai/mito_ai/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from mito_ai.file_uploads.urls import get_file_uploads_urls
2020
from mito_ai.user.urls import get_user_urls
2121
from mito_ai.chat_history.urls import get_chat_history_urls
22+
from mito_ai.chart_wizard.urls import get_chart_wizard_urls
2223

2324
# Force Matplotlib to use the Jupyter inline backend.
2425
# Background: importing Streamlit sets os.environ["MPLBACKEND"] = "Agg" very early.
@@ -109,6 +110,7 @@ def _load_jupyter_server_extension(server_app) -> None: # type: ignore
109110
handlers.extend(get_file_uploads_urls(base_url)) # type: ignore
110111
handlers.extend(get_user_urls(base_url)) # type: ignore
111112
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
112114

113115
web_app.add_handlers(host_pattern, handlers)
114116
server_app.log.info("Loaded the mito_ai server extension")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
]

mito-ai/mito_ai/completions/completion_handlers/completion_handler.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,20 @@
33

44
from typing import Protocol, TypeVar
55
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
77
from mito_ai.completions.providers import OpenAIProvider
88
from mito_ai.completions.message_history import GlobalMessageHistory
99

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+
)
1120

1221
class CompletionHandler(Protocol[T], metaclass=ABCMeta):
1322
"""Protocol defining the interface for completion handlers.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

mito-ai/mito_ai/completions/handlers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
CodeExplainMetadata,
3535
AgentExecutionMetadata,
3636
InlineCompleterMetadata,
37+
ScratchpadResultMetadata,
3738
MessageType
3839
)
3940
from mito_ai.completions.providers import OpenAIProvider
@@ -45,6 +46,7 @@
4546
from mito_ai.completions.completion_handlers.inline_completer_handler import get_inline_completion
4647
from mito_ai.completions.completion_handlers.agent_execution_handler import get_agent_execution_completion
4748
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
4850
from mito_ai.utils.telemetry_utils import identify
4951

5052
FALLBACK_MODEL = "gpt-4.1" # Default model to use for safety
@@ -314,6 +316,9 @@ async def on_message(self, message: str) -> None: # type: ignore
314316
elif type == MessageType.AGENT_AUTO_ERROR_FIXUP:
315317
agent_auto_error_fixup_metadata = AgentSmartDebugMetadata(**metadata_dict)
316318
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)
317322
elif type == MessageType.INLINE_COMPLETION:
318323
inline_completer_metadata = InlineCompleterMetadata(**metadata_dict)
319324
completion = await get_inline_completion(inline_completer_metadata, self._llm, self._message_history, model)

mito-ai/mito_ai/completions/models.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,26 @@ class CellUpdate(BaseModel):
2929
# for now and rely on the AI to respond with the correct types, following the format
3030
# that we show it in the system prompt.
3131
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+
]
3342
message: str
3443
cell_update: Optional[CellUpdate]
3544
get_cell_output_cell_id: Optional[str]
3645
next_steps: Optional[List[str]]
3746
analysis_assumptions: Optional[List[str]]
3847
streamlit_app_prompt: Optional[str]
48+
question: Optional[str]
49+
answers: Optional[List[str]]
50+
scratchpad_code: Optional[str]
51+
scratchpad_summary: Optional[str]
3952

4053

4154
@dataclass(frozen=True)
@@ -67,6 +80,7 @@ class MessageType(Enum):
6780
STREAMLIT_CONVERSION = "streamlit_conversion"
6881
STOP_AGENT = "stop_agent"
6982
DEPLOY_APP = "deploy_app"
83+
AGENT_SCRATCHPAD_RESULT = "agent:scratchpad-result"
7084

7185

7286
@dataclass(frozen=True)
@@ -136,13 +150,20 @@ class CodeExplainMetadata():
136150
activeCellCode: Optional[str] = None
137151

138152
@dataclass(frozen=True)
139-
class InlineCompleterMetadata():
153+
class InlineCompleterMetadata():
140154
promptType: Literal['inline_completion']
141155
prefix: str
142156
suffix: str
143157
variables: Optional[List[str]] = None
144158
files: Optional[List[str]] = None
145159

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+
146167
@dataclass(frozen=True)
147168
class CompletionRequest:
148169
"""

0 commit comments

Comments
 (0)