mito-ai 0.1.54__py3-none-any.whl → 0.1.56__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.
- mito_ai/_version.py +1 -1
- mito_ai/anthropic_client.py +7 -6
- mito_ai/completions/models.py +1 -1
- mito_ai/completions/prompt_builders/agent_execution_prompt.py +18 -50
- mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +77 -92
- mito_ai/completions/prompt_builders/agent_system_message.py +216 -270
- mito_ai/completions/prompt_builders/chat_prompt.py +15 -100
- mito_ai/completions/prompt_builders/chat_system_message.py +102 -63
- mito_ai/completions/prompt_builders/explain_code_prompt.py +22 -24
- mito_ai/completions/prompt_builders/inline_completer_prompt.py +78 -107
- mito_ai/completions/prompt_builders/prompt_constants.py +20 -36
- mito_ai/completions/prompt_builders/prompt_section_registry/__init__.py +70 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/active_cell_code.py +15 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/active_cell_id.py +10 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/active_cell_output.py +20 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/base.py +37 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/error_traceback.py +17 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/example.py +19 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/files.py +17 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/generic.py +15 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/get_cell_output_tool_response.py +21 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/notebook.py +19 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/rules.py +39 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/selected_context.py +100 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/streamlit_app_status.py +25 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/task.py +12 -0
- mito_ai/completions/prompt_builders/prompt_section_registry/variables.py +18 -0
- mito_ai/completions/prompt_builders/smart_debug_prompt.py +48 -63
- mito_ai/constants.py +0 -3
- mito_ai/tests/completions/test_prompt_section_registry.py +44 -0
- mito_ai/tests/message_history/test_message_history_utils.py +273 -340
- mito_ai/tests/providers/test_anthropic_client.py +7 -3
- mito_ai/utils/message_history_utils.py +68 -44
- mito_ai/utils/open_ai_utils.py +3 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +147 -102
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/package.json +3 -2
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +3 -2
- mito_ai-0.1.54.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.31462f8f6a76b1cefbeb.js → mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.dfd7975de75d64db80d6.js +2689 -472
- mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.dfd7975de75d64db80d6.js.map +1 -0
- mito_ai-0.1.54.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.3f3c98eaba66bf084c66.js → mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.1e7b5cf362385f109883.js +21 -19
- mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.1e7b5cf362385f109883.js.map +1 -0
- mito_ai-0.1.54.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js → mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js +15 -7
- mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js.map +1 -0
- mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.css +708 -0
- mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.js +0 -0
- {mito_ai-0.1.54.dist-info → mito_ai-0.1.56.dist-info}/METADATA +5 -1
- {mito_ai-0.1.54.dist-info → mito_ai-0.1.56.dist-info}/RECORD +69 -51
- mito_ai/completions/prompt_builders/utils.py +0 -84
- mito_ai-0.1.54.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.31462f8f6a76b1cefbeb.js.map +0 -1
- mito_ai-0.1.54.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.3f3c98eaba66bf084c66.js.map +0 -1
- mito_ai-0.1.54.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +0 -1
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.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
- {mito_ai-0.1.54.data → mito_ai-0.1.56.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
- {mito_ai-0.1.54.data → mito_ai-0.1.56.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
- {mito_ai-0.1.54.data → mito_ai-0.1.56.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
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.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
- {mito_ai-0.1.54.data → mito_ai-0.1.56.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
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
- {mito_ai-0.1.54.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
- {mito_ai-0.1.54.dist-info → mito_ai-0.1.56.dist-info}/WHEEL +0 -0
- {mito_ai-0.1.54.dist-info → mito_ai-0.1.56.dist-info}/entry_points.txt +0 -0
- {mito_ai-0.1.54.dist-info → mito_ai-0.1.56.dist-info}/licenses/LICENSE +0 -0
|
@@ -385,12 +385,16 @@ def test_caching_system_prompt_scenarios(messages, expected_system_type, expecte
|
|
|
385
385
|
@pytest.mark.parametrize("message_count,expected_cache_boundary", [
|
|
386
386
|
(1, None), # 1 message, No cache boundary
|
|
387
387
|
(3, None), # 3 messages, No cache boundary
|
|
388
|
-
(5, 1), # 5 messages, cache at index
|
|
389
|
-
(10, 6), # 10 messages, cache at index 6
|
|
388
|
+
(5, 1), # 5 messages, cache at index 1 (5 - 3 - 1 = 1)
|
|
389
|
+
(10, 6), # 10 messages, cache at index 6 (10 - 3 - 1 = 6)
|
|
390
390
|
])
|
|
391
|
-
def test_caching_conversation_history(message_count, expected_cache_boundary):
|
|
391
|
+
def test_caching_conversation_history(message_count, expected_cache_boundary, monkeypatch):
|
|
392
392
|
"""Test that conversation history is cached at the keep_recent boundary for different message counts."""
|
|
393
393
|
|
|
394
|
+
# Mock MAX_TRIM_THRESHOLD to use a fixed value for testing
|
|
395
|
+
import mito_ai.anthropic_client as anthropic_client_module
|
|
396
|
+
monkeypatch.setattr(anthropic_client_module, 'MAX_TRIM_THRESHOLD', 3)
|
|
397
|
+
|
|
394
398
|
# Create messages based on the parameter
|
|
395
399
|
messages: List[ChatCompletionMessageParam] = [
|
|
396
400
|
ChatCompletionSystemMessageParam(role="system", content="You are helpful.")
|
|
@@ -2,47 +2,65 @@
|
|
|
2
2
|
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
3
|
|
|
4
4
|
import re
|
|
5
|
-
from typing import List
|
|
6
|
-
from mito_ai.constants import MESSAGE_HISTORY_TRIM_THRESHOLD
|
|
5
|
+
from typing import List, Dict, Optional
|
|
7
6
|
from openai.types.chat import ChatCompletionMessageParam
|
|
8
|
-
from mito_ai.completions.prompt_builders.
|
|
9
|
-
ACTIVE_CELL_ID_SECTION_HEADING,
|
|
10
|
-
ACTIVE_CELL_OUTPUT_SECTION_HEADING,
|
|
11
|
-
GET_CELL_OUTPUT_TOOL_RESPONSE_SECTION_HEADING,
|
|
12
|
-
FILES_SECTION_HEADING,
|
|
13
|
-
STREAMLIT_APP_STATUS_SECTION_HEADING,
|
|
14
|
-
VARIABLES_SECTION_HEADING,
|
|
15
|
-
JUPYTER_NOTEBOOK_SECTION_HEADING,
|
|
16
|
-
CONTENT_REMOVED_PLACEHOLDER
|
|
17
|
-
)
|
|
7
|
+
from mito_ai.completions.prompt_builders.prompt_section_registry import get_all_section_classes
|
|
18
8
|
|
|
19
9
|
|
|
20
|
-
def
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
def build_section_to_trim_message_index_mapping() -> Dict[str, Optional[int]]:
|
|
11
|
+
"""Build mapping from section names to trim_after_messages thresholds."""
|
|
12
|
+
mapping = {}
|
|
13
|
+
for section_class in get_all_section_classes():
|
|
14
|
+
section_name = section_class.__name__.replace("Section", "")
|
|
15
|
+
mapping[section_name] = section_class.trim_after_messages
|
|
16
|
+
return mapping
|
|
24
17
|
|
|
25
|
-
These sections are replaced with a placeholder text.
|
|
26
|
-
"""
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
19
|
+
def trim_message_content(content: str, message_age: int) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Trims sections from XML string based on age and thresholds.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
content: The message content as a string (may contain XML tags)
|
|
25
|
+
message_age: The age of the message (0 = most recent, higher = older)
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The content with trimmed sections removed
|
|
29
|
+
"""
|
|
30
|
+
section_mapping = build_section_to_trim_message_index_mapping()
|
|
31
|
+
|
|
32
|
+
# Special handling for Example sections - remove entirely if they should be trimmed
|
|
33
|
+
# Match: <Example name="...">...</Example> or <Example>...</Example>
|
|
34
|
+
example_pattern = r'<Example(?:\s+name="[^"]*")?>.*?</Example>'
|
|
35
|
+
example_matches = list(re.finditer(example_pattern, content, flags=re.DOTALL))
|
|
36
|
+
|
|
37
|
+
for match in reversed(example_matches): # Process from end to start to preserve indices
|
|
38
|
+
example_threshold = section_mapping.get("Example")
|
|
39
|
+
if example_threshold is not None and message_age >= example_threshold:
|
|
40
|
+
# Remove the entire Example block
|
|
41
|
+
content = content[:match.start()] + content[match.end():]
|
|
42
|
+
|
|
43
|
+
# For other sections, parse and trim based on section_mapping
|
|
44
|
+
# Match XML tags like <SectionName>...</SectionName>
|
|
45
|
+
# Skip Example sections as they're handled separately above
|
|
46
|
+
for section_name, threshold in section_mapping.items():
|
|
47
|
+
if threshold is None:
|
|
48
|
+
# Never trim sections with None threshold
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
if section_name == "Example":
|
|
52
|
+
# Example sections are handled separately above
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
if message_age >= threshold:
|
|
56
|
+
# Pattern to match the section tag and its content
|
|
57
|
+
# Handles both self-closing and content tags
|
|
58
|
+
pattern = rf'<{re.escape(section_name)}>.*?</{re.escape(section_name)}>'
|
|
59
|
+
matches = list(re.finditer(pattern, content, flags=re.DOTALL))
|
|
60
|
+
|
|
61
|
+
for match in reversed(matches): # Process from end to start
|
|
62
|
+
content = content[:match.start()] + content[match.end():]
|
|
38
63
|
|
|
39
|
-
for heading in section_headings:
|
|
40
|
-
content = re.sub(
|
|
41
|
-
f"{re.escape(heading)}\n(?:.+\n)+",
|
|
42
|
-
f"{heading} {CONTENT_REMOVED_PLACEHOLDER}\n",
|
|
43
|
-
content,
|
|
44
|
-
)
|
|
45
|
-
|
|
46
64
|
return content
|
|
47
65
|
|
|
48
66
|
|
|
@@ -50,28 +68,30 @@ def trim_old_messages(messages: List[ChatCompletionMessageParam]) -> List[ChatCo
|
|
|
50
68
|
"""
|
|
51
69
|
Trims metadata sections from messages that are older than the specified number of recent messages.
|
|
52
70
|
We do this in order to reduce the token count of the messages, which helps us stay under the token limit for the LLM.
|
|
71
|
+
|
|
72
|
+
Only trims user messages for now, but the design allows for easy extension to system/assistant messages.
|
|
53
73
|
"""
|
|
54
|
-
if len(messages) <= MESSAGE_HISTORY_TRIM_THRESHOLD:
|
|
55
|
-
return messages
|
|
56
74
|
|
|
57
75
|
# Process all messages except the keep_recent most recent ones.
|
|
58
76
|
# Only trim user messages, which is where this metadata lives.
|
|
59
77
|
# We want to not edit the system messages, as they contain important information / examples.
|
|
60
|
-
|
|
61
|
-
|
|
78
|
+
total_messages = len(messages)
|
|
79
|
+
for i in range(total_messages):
|
|
62
80
|
|
|
81
|
+
# Only trim user messages
|
|
63
82
|
is_user_message = messages[i].get("role") == "user"
|
|
64
83
|
if not is_user_message:
|
|
65
84
|
continue
|
|
66
85
|
|
|
67
86
|
content = messages[i].get("content")
|
|
68
|
-
|
|
69
87
|
if content is None:
|
|
70
88
|
continue
|
|
71
89
|
|
|
90
|
+
message_age = total_messages - i - 1
|
|
91
|
+
|
|
72
92
|
if isinstance(content, str):
|
|
73
93
|
# If content is just a string, then we just trim the metadata sections
|
|
74
|
-
messages[i]["content"] =
|
|
94
|
+
messages[i]["content"] = trim_message_content(content, message_age)
|
|
75
95
|
else:
|
|
76
96
|
# Otherwise, we get rid of the image_url section and just keep the trimmed text
|
|
77
97
|
# We assume that there is only one text section in the content
|
|
@@ -80,8 +100,12 @@ def trim_old_messages(messages: List[ChatCompletionMessageParam]) -> List[ChatCo
|
|
|
80
100
|
if section.get("type") == "text" and "text" in section:
|
|
81
101
|
text_content = section["text"] #type: ignore
|
|
82
102
|
break
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
103
|
+
|
|
104
|
+
trimmed_text = trim_message_content(text_content, message_age)
|
|
105
|
+
# Update the text section with trimmed content
|
|
106
|
+
for section in content:
|
|
107
|
+
if section.get("type") == "text" and "text" in section:
|
|
108
|
+
section["text"] = trimmed_text #type: ignore
|
|
109
|
+
break
|
|
86
110
|
|
|
87
111
|
return messages
|
mito_ai/utils/open_ai_utils.py
CHANGED
|
@@ -171,6 +171,9 @@ def get_open_ai_completion_function_params(
|
|
|
171
171
|
"stream": stream,
|
|
172
172
|
"messages": messages,
|
|
173
173
|
}
|
|
174
|
+
|
|
175
|
+
if model == "gpt-5.2":
|
|
176
|
+
completion_function_params["reasoning_effort"] = "low"
|
|
174
177
|
|
|
175
178
|
# If a response format is provided, we need to convert it to a json schema.
|
|
176
179
|
# Pydantic models are supported by the OpenAI API, however, we need to be able to
|