mito-ai 0.1.55__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.
- mito_ai/__init__.py +2 -0
- mito_ai/_version.py +1 -1
- mito_ai/anthropic_client.py +7 -6
- mito_ai/chart_wizard/__init__.py +3 -0
- mito_ai/chart_wizard/handlers.py +52 -0
- mito_ai/chart_wizard/urls.py +23 -0
- mito_ai/completions/completion_handlers/completion_handler.py +11 -2
- mito_ai/completions/completion_handlers/scratchpad_result_handler.py +66 -0
- mito_ai/completions/handlers.py +5 -0
- mito_ai/completions/models.py +24 -3
- mito_ai/completions/prompt_builders/agent_execution_prompt.py +18 -50
- mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +82 -95
- mito_ai/completions/prompt_builders/agent_system_message.py +304 -276
- mito_ai/completions/prompt_builders/chart_conversion_prompt.py +27 -0
- mito_ai/completions/prompt_builders/chat_prompt.py +15 -100
- mito_ai/completions/prompt_builders/chat_system_message.py +98 -72
- 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 +35 -45
- 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/{utils.py → prompt_section_registry/selected_context.py} +51 -42
- 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/scratchpad_result_prompt.py +17 -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/provider_utils.py +8 -1
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +102 -102
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
- mito_ai-0.1.55.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.49c79c62671528877c61.js → mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.9d26322f3e78beb2b666.js +2778 -297
- mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.9d26322f3e78beb2b666.js.map +1 -0
- mito_ai-0.1.55.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.9dfbffc3592eb6f0aef9.js → mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.79c1ea8a3cda73a4cb6f.js +17 -17
- mito_ai-0.1.55.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.9dfbffc3592eb6f0aef9.js.map → mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.79c1ea8a3cda73a4cb6f.js.map +1 -1
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.css +7 -2
- {mito_ai-0.1.55.dist-info → mito_ai-0.1.57.dist-info}/METADATA +5 -1
- {mito_ai-0.1.55.dist-info → mito_ai-0.1.57.dist-info}/RECORD +78 -56
- mito_ai-0.1.55.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.49c79c62671528877c61.js.map +0 -1
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +0 -0
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +0 -0
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js +0 -0
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js.map +0 -0
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +0 -0
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.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
- {mito_ai-0.1.55.data → mito_ai-0.1.57.data}/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.js +0 -0
- {mito_ai-0.1.55.dist-info → mito_ai-0.1.57.dist-info}/WHEEL +0 -0
- {mito_ai-0.1.55.dist-info → mito_ai-0.1.57.dist-info}/entry_points.txt +0 -0
- {mito_ai-0.1.55.dist-info → mito_ai-0.1.57.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,21 +11,35 @@ import json
|
|
|
11
11
|
from typing import Final
|
|
12
12
|
from mito_ai.utils.schema import MITO_FOLDER
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
VARIABLES_SECTION_HEADING = "Defined Variables:"
|
|
17
|
-
CODE_SECTION_HEADING = "Code in the active code cell:"
|
|
18
|
-
ACTIVE_CELL_ID_SECTION_HEADING = "The ID of the active code cell:"
|
|
19
|
-
ACTIVE_CELL_OUTPUT_SECTION_HEADING = "Output of the active code cell:"
|
|
20
|
-
GET_CELL_OUTPUT_TOOL_RESPONSE_SECTION_HEADING = "Output of the code cell you just applied the CELL_UPDATE to:"
|
|
21
|
-
JUPYTER_NOTEBOOK_SECTION_HEADING = "Jupyter Notebook:"
|
|
22
|
-
STREAMLIT_APP_STATUS_SECTION_HEADING = "Streamlit App Status:"
|
|
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.
|
|
23
16
|
|
|
24
|
-
|
|
25
|
-
CONTENT_REMOVED_PLACEHOLDER = "Content removed to save space"
|
|
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.
|
|
26
18
|
|
|
27
|
-
|
|
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"').
|
|
28
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
|
+
|
|
42
|
+
CITATION_RULES = """
|
|
29
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:
|
|
30
44
|
|
|
31
45
|
Single line citation:
|
|
@@ -36,19 +50,18 @@ Multiline citation (for citing a range of lines):
|
|
|
36
50
|
|
|
37
51
|
Citation Rules:
|
|
38
52
|
|
|
39
|
-
1. Every fact or
|
|
53
|
+
1. Every fact or conclusion you draw based on the user's notebook must include a MITO_CITATION so that the user can verify your work.
|
|
40
54
|
2. When choosing the citation, select the code that will most help the user validate the fact or statement that you shared with them.
|
|
41
55
|
3. Place the citation immediately after the statement it supports. Do not explain the citation with phrases like "See", "Derived from", etc. Just provide the citation object.
|
|
42
56
|
4. For the "line_number" field, use the line number within the cell that is most relevant to the citation. Important: The cell line number should be 0-indexed and should not skip comments.
|
|
43
57
|
5. For multiline citations, use the "first_line-last_line" format when the insight spans multiple lines of code. Both line numbers should be 0-indexed.
|
|
44
58
|
6. If you cannot find relevant information in the notebook to answer a question, clearly state this and do not provide a citation.
|
|
45
|
-
7. You
|
|
59
|
+
7. You only need to provide a citation when sharing an insight with the user. For example you should use a MITO_CITATION when you writing insights like any of the following: "The highest trading volume day was on January 15th, 2025", "The MSE was 6.8", "Apple's COGS are represented by the Orange bar in the graph". If you are not writing a quantitative insight and instead are just referring to a block of code like "I updated Cell 5 to include a graph", then you should instead use a MITO_CELL_REF.
|
|
46
60
|
8. Do not include the citation in the code block as a comment. ONLY include the citation in the message field of your response.
|
|
47
61
|
"""
|
|
48
62
|
|
|
49
|
-
CELL_REFERENCE_RULES = """
|
|
50
|
-
|
|
51
|
-
When referring to specific cells in the notebook in your messages, use cell references so the user can easily navigate to the cell you're talking about. The user sees cells numbered as "Cell 1", "Cell 2", etc., but internally cells are identified by their unique IDs.
|
|
63
|
+
CELL_REFERENCE_RULES = """
|
|
64
|
+
When referring to specific cells in the notebook in your messages, use cell references so the user can easily navigate to the cell you're talking about. Cell references are displayed to the user "Cell 1", "Cell 2", etc., but internally cells are identified by their unique IDs.
|
|
52
65
|
|
|
53
66
|
To reference a cell, use this format inline in your message:
|
|
54
67
|
[MITO_CELL_REF:cell_id]
|
|
@@ -58,35 +71,12 @@ This will be displayed to the user as a clickable "Cell N" link that navigates t
|
|
|
58
71
|
Cell Reference Rules:
|
|
59
72
|
|
|
60
73
|
1. Use cell references when discussing specific cells you've created or modified (e.g., "I've added the data cleaning code in [MITO_CELL_REF:abc123]").
|
|
61
|
-
2.
|
|
62
|
-
3.
|
|
63
|
-
4.
|
|
64
|
-
5.
|
|
65
|
-
6. Cell references are different from citations. Use citations for specific line-level insights; use cell references for general cell-level navigation.
|
|
66
|
-
|
|
67
|
-
Example:
|
|
68
|
-
"I've loaded the sales data in [MITO_CELL_REF:c68fdf19-db8c-46dd-926f-d90ad35bb3bc] and will now calculate the monthly totals."
|
|
74
|
+
2. The cell_id must be an actual cell ID from the notebook - do not make up IDs.
|
|
75
|
+
3. Place the reference inline where it makes sense in your message, similar to how you would write "Cell 3" in natural language.
|
|
76
|
+
4. Do not use cell references in code - only in the message field of your responses.
|
|
77
|
+
5. You only need to provide a cell reference when you want to make it easy for the user to navigate to a specific cell in the notebook. For example you should use a MITO_CELL_REF when you are stating things like: "I've loaded the sales data in [MITO_CELL_REF:c68fdf19-db8c-46dd-926f-d90ad35bb3bc]" or "[MITO_CELL_REF:a91fde20-cc7f-g6ee-146g-e10bc34abdbh] creates the graph showing the total highest closing stock price for each company". If you are not referencing an entire code block and instead of providing justification for a specific conclucions that you drew like "The most common used car in the lot is a 2005 Honda CRV", then you should instead use a MITO_CITATION.
|
|
69
78
|
"""
|
|
70
79
|
|
|
71
|
-
def get_active_cell_output_str(has_active_cell_output: bool) -> str:
|
|
72
|
-
"""
|
|
73
|
-
Used to tell the AI about the output of the active code cell.
|
|
74
|
-
We use this in the chat prompt.
|
|
75
|
-
"""
|
|
76
|
-
if has_active_cell_output:
|
|
77
|
-
return f"{ACTIVE_CELL_OUTPUT_SECTION_HEADING}\nAttatched is an image of the output of the active code cell for your context."
|
|
78
|
-
else:
|
|
79
|
-
return ""
|
|
80
|
-
|
|
81
|
-
def cell_update_output_str(has_cell_update_output: bool) -> str:
|
|
82
|
-
"""
|
|
83
|
-
Used to respond to the GET_CELL_OUTPUT tool, telling the agent the output of the cell it requested
|
|
84
|
-
"""
|
|
85
|
-
if has_cell_update_output:
|
|
86
|
-
return f"{GET_CELL_OUTPUT_TOOL_RESPONSE_SECTION_HEADING}\nAttatched is an image of code cell output that you requested."
|
|
87
|
-
else:
|
|
88
|
-
return ""
|
|
89
|
-
|
|
90
80
|
def redact_sensitive_info(connections: dict) -> dict:
|
|
91
81
|
"""
|
|
92
82
|
Redacts sensitive information from connections data.
|
|
@@ -184,7 +174,7 @@ Here is the schema:
|
|
|
184
174
|
return DATABASE_RULES
|
|
185
175
|
|
|
186
176
|
|
|
187
|
-
CHAT_CODE_FORMATTING_RULES = """
|
|
177
|
+
CHAT_CODE_FORMATTING_RULES = """
|
|
188
178
|
- COMPLETE REPLACEMENT: Your code will COMPLETELY REPLACE the entire contents of the active code cell.
|
|
189
179
|
- INCLUDE ALL CODE: You MUST return the COMPLETE, FULL contents of the entire code cell - including ALL existing code that should remain plus your modifications.
|
|
190
180
|
- NEVER PARTIAL CODE: NEVER return only a portion, snippet, or subset of the code cell. Partial responses will break the user's notebook by deleting important code.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from mito_ai.completions.prompt_builders.prompt_section_registry.base import PromptSection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from typing import List, Type
|
|
8
|
+
from .files import FilesSection
|
|
9
|
+
from .variables import VariablesSection
|
|
10
|
+
from .active_cell_code import ActiveCellCodeSection
|
|
11
|
+
from .notebook import NotebookSection
|
|
12
|
+
from .active_cell_id import ActiveCellIdSection
|
|
13
|
+
from .active_cell_output import ActiveCellOutputSection
|
|
14
|
+
from .get_cell_output_tool_response import GetCellOutputToolResponseSection
|
|
15
|
+
from .streamlit_app_status import StreamlitAppStatusSection
|
|
16
|
+
from .selected_context import SelectedContextSection
|
|
17
|
+
from .rules import RulesSection
|
|
18
|
+
from .task import TaskSection
|
|
19
|
+
from .error_traceback import ErrorTracebackSection
|
|
20
|
+
from .example import ExampleSection
|
|
21
|
+
from .generic import GenericSection
|
|
22
|
+
from .base import Prompt, PromptSection
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SectionRegistry:
|
|
26
|
+
"""Namespace for easy section construction."""
|
|
27
|
+
Files = FilesSection
|
|
28
|
+
Variables = VariablesSection
|
|
29
|
+
ActiveCellCode = ActiveCellCodeSection
|
|
30
|
+
Notebook = NotebookSection
|
|
31
|
+
ActiveCellId = ActiveCellIdSection
|
|
32
|
+
ActiveCellOutput = ActiveCellOutputSection
|
|
33
|
+
GetCellOutputToolResponse = GetCellOutputToolResponseSection
|
|
34
|
+
StreamlitAppStatus = StreamlitAppStatusSection
|
|
35
|
+
SelectedContext = SelectedContextSection
|
|
36
|
+
Rules = RulesSection
|
|
37
|
+
Task = TaskSection
|
|
38
|
+
ErrorTraceback = ErrorTracebackSection
|
|
39
|
+
Example = ExampleSection
|
|
40
|
+
Generic = GenericSection
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Export as SG (Section Generator) for easy usage
|
|
44
|
+
SG = SectionRegistry()
|
|
45
|
+
|
|
46
|
+
# Also export function to get all section classes for trimming
|
|
47
|
+
def get_all_section_classes() -> List[Type[PromptSection]]:
|
|
48
|
+
"""Returns all section classes for building trimming mapping."""
|
|
49
|
+
prompt_section_classes: List[Type[PromptSection]] = PromptSection.__subclasses__()
|
|
50
|
+
return prompt_section_classes
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_max_trim_after_messages() -> int:
|
|
54
|
+
"""
|
|
55
|
+
Returns the maximum trim_after_messages value from all section classes.
|
|
56
|
+
|
|
57
|
+
This is used for cache boundary calculation - we cache messages that are
|
|
58
|
+
older than the max trim threshold, since those messages are stable and
|
|
59
|
+
won't be edited anymore.
|
|
60
|
+
|
|
61
|
+
Returns 0 if all sections have trim_after_messages = None.
|
|
62
|
+
"""
|
|
63
|
+
section_classes = get_all_section_classes()
|
|
64
|
+
max_value = 0
|
|
65
|
+
for section_class in section_classes:
|
|
66
|
+
trim_value = getattr(section_class, 'trim_after_messages', None)
|
|
67
|
+
if trim_value is not None and trim_value > max_value:
|
|
68
|
+
max_value = trim_value
|
|
69
|
+
return max_value
|
|
70
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ActiveCellCodeSection(PromptSection):
|
|
8
|
+
"""Section for code in the active code cell."""
|
|
9
|
+
trim_after_messages: int = 3
|
|
10
|
+
|
|
11
|
+
def __init__(self, code: str):
|
|
12
|
+
self.code = code
|
|
13
|
+
self.content = f"```python\n{code}\n```"
|
|
14
|
+
self.name = "ActiveCellCode"
|
|
15
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ActiveCellIdSection(PromptSection):
|
|
8
|
+
"""Section for the ID of the active code cell."""
|
|
9
|
+
trim_after_messages = None
|
|
10
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ActiveCellOutputSection(PromptSection):
|
|
8
|
+
"""Section for output of the active code cell."""
|
|
9
|
+
trim_after_messages: int = 3
|
|
10
|
+
exclude_if_empty: bool = True
|
|
11
|
+
|
|
12
|
+
def __init__(self, has_active_cell_output: bool):
|
|
13
|
+
self.name = "ActiveCellOutput"
|
|
14
|
+
self.has_active_cell_output = has_active_cell_output
|
|
15
|
+
self.content = ""
|
|
16
|
+
if has_active_cell_output:
|
|
17
|
+
# The actual image is attatched to the message, its not part of the text content
|
|
18
|
+
self.content = f"Attatched is an image of the output of the active code cell."
|
|
19
|
+
|
|
20
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from abc import ABC
|
|
5
|
+
from typing import Optional, List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PromptSection(ABC):
|
|
9
|
+
"""Abstract base class for all prompt sections."""
|
|
10
|
+
|
|
11
|
+
# Class variable: trimming threshold (None = never trim)
|
|
12
|
+
trim_after_messages: Optional[int] = 3
|
|
13
|
+
|
|
14
|
+
# Class variable: if True, exclude XML tags when content is empty
|
|
15
|
+
exclude_if_empty: bool = False
|
|
16
|
+
|
|
17
|
+
def __init__(self, content: str):
|
|
18
|
+
self.content = content
|
|
19
|
+
self.name = self.__class__.__name__.replace("Section", "")
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
# If exclude_if_empty is True and content is empty, return empty string
|
|
23
|
+
if self.exclude_if_empty and (self.content is None or self.content.strip() == ""):
|
|
24
|
+
return ""
|
|
25
|
+
return f"<{self.name}>\n{self.content}\n</{self.name}>"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Prompt:
|
|
29
|
+
"""Container for multiple prompt sections."""
|
|
30
|
+
def __init__(self, sections: List[PromptSection]):
|
|
31
|
+
self.sections = sections
|
|
32
|
+
|
|
33
|
+
def __str__(self) -> str:
|
|
34
|
+
# Filter out empty strings to exclude sections that return "" when they have no content
|
|
35
|
+
section_strings = [str(section) for section in self.sections]
|
|
36
|
+
return "\n\n".join(s for s in section_strings if s)
|
|
37
|
+
|
|
@@ -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 .base import PromptSection
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ErrorTracebackSection(PromptSection):
|
|
9
|
+
"""Section for error traceback - never trimmed."""
|
|
10
|
+
trim_after_messages: Optional[int] = None
|
|
11
|
+
|
|
12
|
+
def __init__(self, code_cell_id: str, traceback: str):
|
|
13
|
+
self.code_cell_id = code_cell_id
|
|
14
|
+
self.traceback = traceback
|
|
15
|
+
self.content = f"Cell ID: {code_cell_id}\n\n{traceback}"
|
|
16
|
+
self.name = "Error"
|
|
17
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExampleSection(PromptSection):
|
|
8
|
+
"""Section for examples - contains nested section XML tags."""
|
|
9
|
+
trim_after_messages: int = 3
|
|
10
|
+
|
|
11
|
+
def __init__(self, name: str, content: str):
|
|
12
|
+
# content can contain nested XML like: "<Files>...</Files><Variables>...</Variables>"
|
|
13
|
+
super().__init__(content)
|
|
14
|
+
self.example_name = name # e.g., "Example 1", "Cell Modification Example"
|
|
15
|
+
|
|
16
|
+
def __str__(self) -> str:
|
|
17
|
+
# Render as: <Example name="Example 1">...</Example>
|
|
18
|
+
return f'<Example name="{self.example_name}">{self.content}</Example>'
|
|
19
|
+
|
|
@@ -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, Optional
|
|
5
|
+
from .base import PromptSection
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FilesSection(PromptSection):
|
|
9
|
+
"""Section for files in the current directory."""
|
|
10
|
+
trim_after_messages: int = 3
|
|
11
|
+
exclude_if_empty: bool = True
|
|
12
|
+
|
|
13
|
+
def __init__(self, files: Optional[List[str]]):
|
|
14
|
+
self.files = files
|
|
15
|
+
self.content = '\n'.join([f"file_name: {file}" for file in files or []])
|
|
16
|
+
self.name = "Files"
|
|
17
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GenericSection(PromptSection):
|
|
9
|
+
"""Generic section that can be used with a custom name and content."""
|
|
10
|
+
trim_after_messages: Optional[int] = None
|
|
11
|
+
|
|
12
|
+
def __init__(self, name: str, content: str):
|
|
13
|
+
self.content = content
|
|
14
|
+
self.name = name
|
|
15
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
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 Optional
|
|
5
|
+
from .base import PromptSection
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GetCellOutputToolResponseSection(PromptSection):
|
|
9
|
+
"""Section for output of the code cell after applying CELL_UPDATE."""
|
|
10
|
+
trim_after_messages: int = 3
|
|
11
|
+
exclude_if_empty: bool = True
|
|
12
|
+
|
|
13
|
+
def __init__(self, base64EncodedCellOutput: Optional[str]):
|
|
14
|
+
self.name = "GetCellOutputToolResponse"
|
|
15
|
+
self.base64EncodedCellOutput = base64EncodedCellOutput
|
|
16
|
+
|
|
17
|
+
self.content = ""
|
|
18
|
+
if base64EncodedCellOutput is not None and base64EncodedCellOutput != '':
|
|
19
|
+
# The actual image is attatched to the message, its not part of the text content
|
|
20
|
+
self.content = f"Attatched is an image of code cell output that you requested."
|
|
21
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
|
|
6
|
+
from mito_ai.completions.models import AIOptimizedCell
|
|
7
|
+
from .base import PromptSection
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NotebookSection(PromptSection):
|
|
11
|
+
"""Section for Jupyter notebook content."""
|
|
12
|
+
trim_after_messages: int = 6
|
|
13
|
+
exclude_if_empty: bool = False
|
|
14
|
+
|
|
15
|
+
def __init__(self, cells: List[AIOptimizedCell]):
|
|
16
|
+
self.cells = cells
|
|
17
|
+
self.content = '\n'.join([f"{cell}" for cell in cells or []])
|
|
18
|
+
self.name = "Notebook"
|
|
19
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
from typing import Optional, List, Dict
|
|
6
|
+
from mito_ai.rules.utils import get_rule
|
|
7
|
+
|
|
8
|
+
def get_rules_str(additional_context: Optional[List[Dict[str, str]]]) -> str:
|
|
9
|
+
"""
|
|
10
|
+
Extract the rules from the additional context array, and retrieve the rule content.
|
|
11
|
+
"""
|
|
12
|
+
if not additional_context:
|
|
13
|
+
return ""
|
|
14
|
+
|
|
15
|
+
selected_rules = [context["value"] for context in additional_context if context.get("type") == "rule"]
|
|
16
|
+
if len(selected_rules) == 0:
|
|
17
|
+
return ""
|
|
18
|
+
|
|
19
|
+
rules_content = []
|
|
20
|
+
for rule in selected_rules:
|
|
21
|
+
rule_content = get_rule(rule)
|
|
22
|
+
if rule_content is None or rule_content == "":
|
|
23
|
+
continue
|
|
24
|
+
|
|
25
|
+
rules_content.append(f"{rule}:\n\n{rule_content}")
|
|
26
|
+
|
|
27
|
+
return '\n'.join(rules_content)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RulesSection(PromptSection):
|
|
31
|
+
"""Section for rules - never trimmed."""
|
|
32
|
+
trim_after_messages: Optional[int] = None
|
|
33
|
+
exclude_if_empty: bool = True
|
|
34
|
+
|
|
35
|
+
def __init__(self, additional_context: Optional[List[Dict[str, str]]]):
|
|
36
|
+
self.additional_context = additional_context
|
|
37
|
+
self.content = get_rules_str(additional_context)
|
|
38
|
+
self.name = "Rules"
|
|
39
|
+
|
mito_ai/completions/prompt_builders/{utils.py → prompt_section_registry/selected_context.py}
RENAMED
|
@@ -1,91 +1,100 @@
|
|
|
1
1
|
# Copyright (c) Saga Inc.
|
|
2
2
|
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def get_rules_str(additional_context: Optional[List[Dict[str, str]]]) -> str:
|
|
9
|
-
"""
|
|
10
|
-
Extract the rules from the additional context array, and retrieve the rule content.
|
|
11
|
-
"""
|
|
12
|
-
if not additional_context:
|
|
13
|
-
return ""
|
|
14
|
-
|
|
15
|
-
selected_rules = [context["value"] for context in additional_context if context.get("type") == "rule"]
|
|
16
|
-
if len(selected_rules) == 0:
|
|
17
|
-
return ""
|
|
18
|
-
|
|
19
|
-
rules_str = ""
|
|
20
|
-
for rule in selected_rules:
|
|
21
|
-
rule_content = get_rule(rule)
|
|
22
|
-
if rule_content is None or rule_content == "":
|
|
23
|
-
continue
|
|
24
|
-
|
|
25
|
-
rules_str += f"===========\n\nCustom Instructions Provided by User: {rule}\n\n{rule_content}\n\n==========="
|
|
26
|
-
|
|
27
|
-
return rules_str
|
|
28
|
-
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
from typing import Optional, List, Dict
|
|
6
|
+
import json
|
|
29
7
|
|
|
30
8
|
def get_selected_context_str(additional_context: Optional[List[Dict[str, str]]]) -> str:
|
|
31
9
|
"""
|
|
32
|
-
|
|
10
|
+
Render the selected context from the additional context array.
|
|
33
11
|
"""
|
|
12
|
+
|
|
34
13
|
if not additional_context:
|
|
35
14
|
return ""
|
|
36
|
-
|
|
15
|
+
|
|
37
16
|
# STEP 1: Extract each context type into a separate list
|
|
17
|
+
# Filter out any non-dict items and ensure we only process dictionaries
|
|
38
18
|
selected_variables = [context["value"] for context in additional_context if context.get("type") == "variable"]
|
|
39
19
|
selected_files = [context["value"] for context in additional_context if context.get("type") == "file"]
|
|
40
20
|
selected_db_connections = [context["value"] for context in additional_context if context.get("type") == "db"]
|
|
41
21
|
selected_images = [context["value"] for context in additional_context if context.get("type", "").startswith("image/")]
|
|
42
22
|
selected_cells = [context["value"] for context in additional_context if context.get("type") == "cell"]
|
|
23
|
+
selected_line_selections = [context["value"] for context in additional_context if context.get("type") == "line_selection"]
|
|
43
24
|
|
|
44
25
|
# STEP 2: Create a list of strings (instructions) for each context type
|
|
45
26
|
context_parts = []
|
|
46
|
-
|
|
27
|
+
|
|
47
28
|
if len(selected_variables) > 0:
|
|
48
29
|
context_parts.append(
|
|
49
30
|
"The following variables have been selected by the user to be used in the task:\n"
|
|
50
31
|
+ "\n".join(selected_variables)
|
|
51
32
|
)
|
|
52
|
-
|
|
33
|
+
|
|
53
34
|
if len(selected_files) > 0:
|
|
54
35
|
context_parts.append(
|
|
55
36
|
"The following files have been selected by the user to be used in the task:\n"
|
|
56
37
|
+ "\n".join(selected_files)
|
|
57
38
|
)
|
|
58
|
-
|
|
39
|
+
|
|
59
40
|
if len(selected_db_connections) > 0:
|
|
60
41
|
context_parts.append(
|
|
61
42
|
"The following database connections have been selected by the user to be used in the task:\n"
|
|
62
43
|
+ "\n".join(selected_db_connections)
|
|
63
44
|
)
|
|
64
|
-
|
|
45
|
+
|
|
65
46
|
if len(selected_images) > 0:
|
|
66
47
|
context_parts.append(
|
|
67
48
|
"The following images have been selected by the user to be used in the task:\n"
|
|
68
49
|
+ "\n".join(selected_images)
|
|
69
50
|
)
|
|
70
|
-
|
|
51
|
+
|
|
71
52
|
if len(selected_cells) > 0:
|
|
72
53
|
context_parts.append(
|
|
73
54
|
"The following cells have been selected by the user to be used in the task:\n"
|
|
74
55
|
+ "\n".join(selected_cells)
|
|
75
56
|
)
|
|
76
57
|
|
|
58
|
+
if len(selected_line_selections) > 0:
|
|
59
|
+
# Parse the line selection JSON values and format them for the prompt
|
|
60
|
+
line_selection_strs = []
|
|
61
|
+
for line_selection_json in selected_line_selections:
|
|
62
|
+
try:
|
|
63
|
+
selection_info = json.loads(line_selection_json)
|
|
64
|
+
cell_id = selection_info.get("cellId", "")
|
|
65
|
+
start_line = selection_info.get("startLine", 0)
|
|
66
|
+
end_line = selection_info.get("endLine", 0)
|
|
67
|
+
selected_code = selection_info.get("selectedCode", "")
|
|
68
|
+
|
|
69
|
+
# Format: Cell {cell_id} lines X-Y (0 indexed)\n[selected code]
|
|
70
|
+
if start_line == end_line:
|
|
71
|
+
line_info = f"Cell {cell_id} line {start_line} (0 indexed)"
|
|
72
|
+
else:
|
|
73
|
+
line_info = f"Cell {cell_id} lines {start_line}-{end_line} (0 indexed)"
|
|
74
|
+
|
|
75
|
+
line_selection_strs.append(f"{line_info}\n```python\n{selected_code}\n```")
|
|
76
|
+
except (json.JSONDecodeError, KeyError):
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
if line_selection_strs:
|
|
80
|
+
context_parts.append(
|
|
81
|
+
"The user has selected the following lines of code to focus on:\n"
|
|
82
|
+
+ "\n\n".join(line_selection_strs)
|
|
83
|
+
)
|
|
84
|
+
|
|
77
85
|
# STEP 3: Combine into a single string
|
|
78
86
|
return "\n\n".join(context_parts)
|
|
79
87
|
|
|
80
88
|
|
|
81
|
-
|
|
82
|
-
"""
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
class SelectedContextSection(PromptSection):
|
|
90
|
+
"""Section for selected context - never trimmed."""
|
|
91
|
+
trim_after_messages: Optional[int] = None
|
|
92
|
+
exclude_if_empty: bool = True
|
|
93
|
+
|
|
94
|
+
def __init__(self, additional_context: Optional[List[Dict[str, str]]]):
|
|
95
|
+
self.additional_context = additional_context
|
|
96
|
+
self.content = get_selected_context_str(additional_context)
|
|
97
|
+
self.name = "SelectedContext"
|
|
98
|
+
|
|
99
|
+
|
|
91
100
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
|
|
6
|
+
def get_streamlit_app_status_str(notebook_id: str, notebook_path: str) -> str:
|
|
7
|
+
"""
|
|
8
|
+
Get the streamlit app status string.
|
|
9
|
+
"""
|
|
10
|
+
from mito_ai.path_utils import does_notebook_id_have_corresponding_app
|
|
11
|
+
if does_notebook_id_have_corresponding_app(notebook_id, notebook_path):
|
|
12
|
+
return "The notebook has an existing Streamlit app that you can edit"
|
|
13
|
+
return "The notebook does not have an existing Streamlit app. If you want to show an app to the user, you must create a new one."
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StreamlitAppStatusSection(PromptSection):
|
|
17
|
+
"""Section for Streamlit app status."""
|
|
18
|
+
trim_after_messages: int = 3
|
|
19
|
+
|
|
20
|
+
def __init__(self, notebook_id: str, notebook_path: str):
|
|
21
|
+
self.notebook_id = notebook_id
|
|
22
|
+
self.notebook_path = notebook_path
|
|
23
|
+
self.content = get_streamlit_app_status_str(notebook_id, notebook_path)
|
|
24
|
+
self.name = "StreamlitAppStatus"
|
|
25
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Copyright (c) Saga Inc.
|
|
2
|
+
# Distributed under the terms of the GNU Affero General Public License v3.0 License.
|
|
3
|
+
|
|
4
|
+
from .base import PromptSection
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TaskSection(PromptSection):
|
|
9
|
+
"""Section for user task - never trimmed."""
|
|
10
|
+
trim_after_messages: Optional[int] = None
|
|
11
|
+
exclude_if_empty: bool = False
|
|
12
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
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, Optional
|
|
5
|
+
from .base import PromptSection
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class VariablesSection(PromptSection):
|
|
9
|
+
"""Section for defined variables."""
|
|
10
|
+
trim_after_messages: int = 6
|
|
11
|
+
exclude_if_empty: bool = False
|
|
12
|
+
|
|
13
|
+
def __init__(self, variables: Optional[List[str]]):
|
|
14
|
+
self.variables = variables
|
|
15
|
+
self.content = '\n'.join([f"{variable}" for variable in variables or []])
|
|
16
|
+
self.name = "Variables"
|
|
17
|
+
|
|
18
|
+
|