mito-ai 0.1.55__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.
Files changed (69) hide show
  1. mito_ai/_version.py +1 -1
  2. mito_ai/anthropic_client.py +7 -6
  3. mito_ai/completions/models.py +1 -1
  4. mito_ai/completions/prompt_builders/agent_execution_prompt.py +18 -50
  5. mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +77 -92
  6. mito_ai/completions/prompt_builders/agent_system_message.py +211 -275
  7. mito_ai/completions/prompt_builders/chat_prompt.py +15 -100
  8. mito_ai/completions/prompt_builders/chat_system_message.py +96 -72
  9. mito_ai/completions/prompt_builders/explain_code_prompt.py +22 -24
  10. mito_ai/completions/prompt_builders/inline_completer_prompt.py +78 -107
  11. mito_ai/completions/prompt_builders/prompt_constants.py +10 -48
  12. mito_ai/completions/prompt_builders/prompt_section_registry/__init__.py +70 -0
  13. mito_ai/completions/prompt_builders/prompt_section_registry/active_cell_code.py +15 -0
  14. mito_ai/completions/prompt_builders/prompt_section_registry/active_cell_id.py +10 -0
  15. mito_ai/completions/prompt_builders/prompt_section_registry/active_cell_output.py +20 -0
  16. mito_ai/completions/prompt_builders/prompt_section_registry/base.py +37 -0
  17. mito_ai/completions/prompt_builders/prompt_section_registry/error_traceback.py +17 -0
  18. mito_ai/completions/prompt_builders/prompt_section_registry/example.py +19 -0
  19. mito_ai/completions/prompt_builders/prompt_section_registry/files.py +17 -0
  20. mito_ai/completions/prompt_builders/prompt_section_registry/generic.py +15 -0
  21. mito_ai/completions/prompt_builders/prompt_section_registry/get_cell_output_tool_response.py +21 -0
  22. mito_ai/completions/prompt_builders/prompt_section_registry/notebook.py +19 -0
  23. mito_ai/completions/prompt_builders/prompt_section_registry/rules.py +39 -0
  24. mito_ai/completions/prompt_builders/{utils.py → prompt_section_registry/selected_context.py} +51 -42
  25. mito_ai/completions/prompt_builders/prompt_section_registry/streamlit_app_status.py +25 -0
  26. mito_ai/completions/prompt_builders/prompt_section_registry/task.py +12 -0
  27. mito_ai/completions/prompt_builders/prompt_section_registry/variables.py +18 -0
  28. mito_ai/completions/prompt_builders/smart_debug_prompt.py +48 -63
  29. mito_ai/constants.py +0 -3
  30. mito_ai/tests/completions/test_prompt_section_registry.py +44 -0
  31. mito_ai/tests/message_history/test_message_history_utils.py +273 -340
  32. mito_ai/tests/providers/test_anthropic_client.py +7 -3
  33. mito_ai/utils/message_history_utils.py +68 -44
  34. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +1 -1
  35. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
  36. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
  37. mito_ai-0.1.55.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.49c79c62671528877c61.js → mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.dfd7975de75d64db80d6.js +487 -120
  38. mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.dfd7975de75d64db80d6.js.map +1 -0
  39. mito_ai-0.1.55.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.9dfbffc3592eb6f0aef9.js → mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.1e7b5cf362385f109883.js +3 -3
  40. mito_ai-0.1.55.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.9dfbffc3592eb6f0aef9.js.map → mito_ai-0.1.56.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.1e7b5cf362385f109883.js.map +1 -1
  41. {mito_ai-0.1.55.dist-info → mito_ai-0.1.56.dist-info}/METADATA +5 -1
  42. {mito_ai-0.1.55.dist-info → mito_ai-0.1.56.dist-info}/RECORD +68 -52
  43. mito_ai-0.1.55.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.49c79c62671528877c61.js.map +0 -1
  44. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  45. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
  46. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +0 -0
  47. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +0 -0
  48. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  49. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js +0 -0
  50. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js.map +0 -0
  51. {mito_ai-0.1.55.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
  52. {mito_ai-0.1.55.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
  53. {mito_ai-0.1.55.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
  54. {mito_ai-0.1.55.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
  55. {mito_ai-0.1.55.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
  56. {mito_ai-0.1.55.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
  57. {mito_ai-0.1.55.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
  58. {mito_ai-0.1.55.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
  59. {mito_ai-0.1.55.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
  60. {mito_ai-0.1.55.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
  61. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +0 -0
  62. {mito_ai-0.1.55.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
  63. {mito_ai-0.1.55.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
  64. {mito_ai-0.1.55.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
  65. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.css +0 -0
  66. {mito_ai-0.1.55.data → mito_ai-0.1.56.data}/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.js +0 -0
  67. {mito_ai-0.1.55.dist-info → mito_ai-0.1.56.dist-info}/WHEEL +0 -0
  68. {mito_ai-0.1.55.dist-info → mito_ai-0.1.56.dist-info}/entry_points.txt +0 -0
  69. {mito_ai-0.1.55.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 2
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.prompt_constants import (
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 trim_sections_from_message_content(content: str) -> str:
21
- """
22
- Removes specific metadata sections from message content to reduce token count so
23
- that users don't exceed the token limit for the LLM.
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
- # Replace metadata sections with placeholders
29
- section_headings = [
30
- FILES_SECTION_HEADING,
31
- VARIABLES_SECTION_HEADING,
32
- JUPYTER_NOTEBOOK_SECTION_HEADING,
33
- GET_CELL_OUTPUT_TOOL_RESPONSE_SECTION_HEADING,
34
- ACTIVE_CELL_OUTPUT_SECTION_HEADING,
35
- ACTIVE_CELL_ID_SECTION_HEADING,
36
- STREAMLIT_APP_STATUS_SECTION_HEADING
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
- for i in range(len(messages) - MESSAGE_HISTORY_TRIM_THRESHOLD):
61
- content = messages[i].get("content")
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"] = trim_sections_from_message_content(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
- messages[i]["content"] = trim_sections_from_message_content(text_content)
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
@@ -720,7 +720,7 @@
720
720
  "semver": {},
721
721
  "vscode-diff": {},
722
722
  "mito_ai": {
723
- "version": "0.1.55",
723
+ "version": "0.1.56",
724
724
  "singleton": true,
725
725
  "import": "/home/runner/work/mito/mito/mito-ai/lib/index.js"
726
726
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mito_ai",
3
- "version": "0.1.55",
3
+ "version": "0.1.56",
4
4
  "description": "AI chat for JupyterLab",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -141,7 +141,7 @@
141
141
  "schemaDir": "schema",
142
142
  "themePath": "style/theme/theme.css",
143
143
  "_build": {
144
- "load": "static/remoteEntry.9dfbffc3592eb6f0aef9.js",
144
+ "load": "static/remoteEntry.1e7b5cf362385f109883.js",
145
145
  "extension": "./extension",
146
146
  "style": "./style"
147
147
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mito_ai",
3
- "version": "0.1.55",
3
+ "version": "0.1.56",
4
4
  "description": "AI chat for JupyterLab",
5
5
  "keywords": [
6
6
  "jupyter",