mito-ai 0.1.33__py3-none-any.whl → 0.1.49__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 +49 -9
- mito_ai/_version.py +1 -1
- mito_ai/anthropic_client.py +142 -67
- mito_ai/{app_builder → app_deploy}/__init__.py +1 -1
- mito_ai/app_deploy/app_deploy_utils.py +44 -0
- mito_ai/app_deploy/handlers.py +345 -0
- mito_ai/{app_builder → app_deploy}/models.py +35 -22
- mito_ai/app_manager/__init__.py +4 -0
- mito_ai/app_manager/handlers.py +167 -0
- mito_ai/app_manager/models.py +71 -0
- mito_ai/app_manager/utils.py +24 -0
- mito_ai/auth/README.md +18 -0
- mito_ai/auth/__init__.py +6 -0
- mito_ai/auth/handlers.py +96 -0
- mito_ai/auth/urls.py +13 -0
- mito_ai/chat_history/handlers.py +63 -0
- mito_ai/chat_history/urls.py +32 -0
- mito_ai/completions/completion_handlers/agent_execution_handler.py +1 -1
- mito_ai/completions/completion_handlers/chat_completion_handler.py +4 -4
- mito_ai/completions/completion_handlers/utils.py +99 -37
- mito_ai/completions/handlers.py +57 -20
- mito_ai/completions/message_history.py +9 -1
- mito_ai/completions/models.py +31 -7
- mito_ai/completions/prompt_builders/agent_execution_prompt.py +21 -2
- mito_ai/completions/prompt_builders/agent_smart_debug_prompt.py +8 -0
- mito_ai/completions/prompt_builders/agent_system_message.py +115 -42
- mito_ai/completions/prompt_builders/chat_name_prompt.py +6 -6
- mito_ai/completions/prompt_builders/chat_prompt.py +18 -11
- mito_ai/completions/prompt_builders/chat_system_message.py +4 -0
- mito_ai/completions/prompt_builders/prompt_constants.py +23 -4
- mito_ai/completions/prompt_builders/utils.py +72 -10
- mito_ai/completions/providers.py +81 -47
- mito_ai/constants.py +25 -24
- mito_ai/file_uploads/__init__.py +3 -0
- mito_ai/file_uploads/handlers.py +248 -0
- mito_ai/file_uploads/urls.py +21 -0
- mito_ai/gemini_client.py +44 -48
- mito_ai/log/handlers.py +10 -3
- mito_ai/log/urls.py +3 -3
- mito_ai/openai_client.py +30 -44
- mito_ai/path_utils.py +70 -0
- mito_ai/streamlit_conversion/agent_utils.py +37 -0
- mito_ai/streamlit_conversion/prompts/prompt_constants.py +172 -0
- mito_ai/streamlit_conversion/prompts/prompt_utils.py +10 -0
- mito_ai/streamlit_conversion/prompts/streamlit_app_creation_prompt.py +46 -0
- mito_ai/streamlit_conversion/prompts/streamlit_error_correction_prompt.py +28 -0
- mito_ai/streamlit_conversion/prompts/streamlit_finish_todo_prompt.py +45 -0
- mito_ai/streamlit_conversion/prompts/streamlit_system_prompt.py +56 -0
- mito_ai/streamlit_conversion/prompts/update_existing_app_prompt.py +50 -0
- mito_ai/streamlit_conversion/search_replace_utils.py +94 -0
- mito_ai/streamlit_conversion/streamlit_agent_handler.py +144 -0
- mito_ai/streamlit_conversion/streamlit_utils.py +85 -0
- mito_ai/streamlit_conversion/validate_streamlit_app.py +105 -0
- mito_ai/streamlit_preview/__init__.py +6 -0
- mito_ai/streamlit_preview/handlers.py +111 -0
- mito_ai/streamlit_preview/manager.py +152 -0
- mito_ai/streamlit_preview/urls.py +22 -0
- mito_ai/streamlit_preview/utils.py +29 -0
- mito_ai/tests/chat_history/test_chat_history.py +211 -0
- mito_ai/tests/completions/completion_handlers_utils_test.py +190 -0
- mito_ai/tests/deploy_app/test_app_deploy_utils.py +89 -0
- mito_ai/tests/file_uploads/__init__.py +2 -0
- mito_ai/tests/file_uploads/test_handlers.py +282 -0
- mito_ai/tests/message_history/test_generate_short_chat_name.py +0 -4
- mito_ai/tests/message_history/test_message_history_utils.py +103 -23
- mito_ai/tests/open_ai_utils_test.py +18 -22
- mito_ai/tests/providers/test_anthropic_client.py +447 -0
- mito_ai/tests/providers/test_azure.py +2 -6
- mito_ai/tests/providers/test_capabilities.py +120 -0
- mito_ai/tests/{test_gemini_client.py → providers/test_gemini_client.py} +40 -36
- mito_ai/tests/providers/test_mito_server_utils.py +448 -0
- mito_ai/tests/providers/test_model_resolution.py +130 -0
- mito_ai/tests/providers/test_openai_client.py +57 -0
- mito_ai/tests/providers/test_provider_completion_exception.py +66 -0
- mito_ai/tests/providers/test_provider_limits.py +42 -0
- mito_ai/tests/providers/test_providers.py +382 -0
- mito_ai/tests/providers/test_retry_logic.py +389 -0
- mito_ai/tests/providers/test_stream_mito_server_utils.py +140 -0
- mito_ai/tests/providers/utils.py +85 -0
- mito_ai/tests/streamlit_conversion/__init__.py +3 -0
- mito_ai/tests/streamlit_conversion/test_apply_search_replace.py +240 -0
- mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +246 -0
- mito_ai/tests/streamlit_conversion/test_streamlit_utils.py +193 -0
- mito_ai/tests/streamlit_conversion/test_validate_streamlit_app.py +112 -0
- mito_ai/tests/streamlit_preview/test_streamlit_preview_handler.py +118 -0
- mito_ai/tests/streamlit_preview/test_streamlit_preview_manager.py +292 -0
- mito_ai/tests/test_constants.py +31 -3
- mito_ai/tests/test_telemetry.py +12 -0
- mito_ai/tests/user/__init__.py +2 -0
- mito_ai/tests/user/test_user.py +120 -0
- mito_ai/tests/utils/test_anthropic_utils.py +6 -6
- mito_ai/user/handlers.py +45 -0
- mito_ai/user/urls.py +21 -0
- mito_ai/utils/anthropic_utils.py +55 -121
- mito_ai/utils/create.py +17 -1
- mito_ai/utils/error_classes.py +42 -0
- mito_ai/utils/gemini_utils.py +39 -94
- mito_ai/utils/message_history_utils.py +7 -4
- mito_ai/utils/mito_server_utils.py +242 -0
- mito_ai/utils/open_ai_utils.py +38 -155
- mito_ai/utils/provider_utils.py +49 -0
- mito_ai/utils/server_limits.py +1 -1
- mito_ai/utils/telemetry_utils.py +137 -5
- {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +102 -100
- {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/package.json +4 -2
- {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +3 -1
- {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +2 -2
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.281f4b9af60d620c6fb1.js → mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js +15948 -8403
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.8f1845da6bf2b128c049.js.map +1 -0
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +198 -0
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +1 -0
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.4f1d00fd0c58fcc05d8d.js → mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.8b24b5b3b93f95205b56.js +58 -33
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.8b24b5b3b93f95205b56.js.map +1 -0
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.06083e515de4862df010.js → mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js +10 -2
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.5876024bb17dbd6a3ee6.js.map +1 -0
- mito_ai-0.1.49.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 +533 -0
- mito_ai-0.1.49.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 +1 -0
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js +6941 -0
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_auth_dist_esm_providers_cognito_tokenProvider_tokenProvider_-72f1c8.a917210f057fcfe224ad.js.map +1 -0
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +1021 -0
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +1 -0
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +59698 -0
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +1 -0
- mito_ai-0.1.49.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 +7440 -0
- mito_ai-0.1.49.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 +1 -0
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js → mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +2 -240
- mito_ai-0.1.49.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +1 -0
- {mito_ai-0.1.33.dist-info → mito_ai-0.1.49.dist-info}/METADATA +5 -2
- mito_ai-0.1.49.dist-info/RECORD +205 -0
- mito_ai/app_builder/handlers.py +0 -218
- mito_ai/tests/providers_test.py +0 -438
- mito_ai/tests/test_anthropic_client.py +0 -270
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.281f4b9af60d620c6fb1.js.map +0 -1
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.4f1d00fd0c58fcc05d8d.js.map +0 -1
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/style_index_js.06083e515de4862df010.js.map +0 -1
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_html2canvas_dist_html2canvas_js.ea47e8c8c906197f8d19.js +0 -7842
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_html2canvas_dist_html2canvas_js.ea47e8c8c906197f8d19.js.map +0 -1
- mito_ai-0.1.33.data/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.9795f79265ddb416864b.js.map +0 -1
- mito_ai-0.1.33.dist-info/RECORD +0 -134
- {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
- {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
- {mito_ai-0.1.33.data → mito_ai-0.1.49.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
- {mito_ai-0.1.33.data → mito_ai-0.1.49.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.33.dist-info → mito_ai-0.1.49.dist-info}/WHEEL +0 -0
- {mito_ai-0.1.33.dist-info → mito_ai-0.1.49.dist-info}/entry_points.txt +0 -0
- {mito_ai-0.1.33.dist-info → mito_ai-0.1.49.dist-info}/licenses/LICENSE +0 -0
mito_ai/utils/open_ai_utils.py
CHANGED
|
@@ -10,6 +10,8 @@ import asyncio
|
|
|
10
10
|
import json
|
|
11
11
|
import time
|
|
12
12
|
from typing import Any, Dict, List, Optional, Final, Union, AsyncGenerator, Tuple, Callable
|
|
13
|
+
from mito_ai.utils.mito_server_utils import get_response_from_mito_server, stream_response_from_mito_server
|
|
14
|
+
from mito_ai.utils.provider_utils import does_message_require_fast_model
|
|
13
15
|
from tornado.httpclient import AsyncHTTPClient
|
|
14
16
|
from openai.types.chat import ChatCompletionMessageParam
|
|
15
17
|
|
|
@@ -27,6 +29,8 @@ from mito_ai.constants import MITO_OPENAI_URL
|
|
|
27
29
|
__user_email: Optional[str] = None
|
|
28
30
|
__user_id: Optional[str] = None
|
|
29
31
|
|
|
32
|
+
FAST_OPENAI_MODEL = "gpt-4.1-nano"
|
|
33
|
+
|
|
30
34
|
def _prepare_request_data_and_headers(
|
|
31
35
|
last_message_content: Union[str, None],
|
|
32
36
|
ai_completion_data: Dict[str, Any],
|
|
@@ -46,10 +50,7 @@ def _prepare_request_data_and_headers(
|
|
|
46
50
|
|
|
47
51
|
Returns:
|
|
48
52
|
A tuple containing the request data and headers
|
|
49
|
-
"""
|
|
50
|
-
# Check that the user is allowed to use the Mito Server
|
|
51
|
-
check_mito_server_quota(message_type)
|
|
52
|
-
|
|
53
|
+
"""
|
|
53
54
|
global __user_email, __user_id
|
|
54
55
|
|
|
55
56
|
if __user_email is None:
|
|
@@ -89,47 +90,15 @@ async def get_ai_completion_from_mito_server(
|
|
|
89
90
|
message_type
|
|
90
91
|
)
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
start_time = time.time()
|
|
102
|
-
try:
|
|
103
|
-
res = await http_client.fetch(
|
|
104
|
-
# Important: DO NOT CHANGE MITO_AI_URL. If you want to use the dev endpoint,
|
|
105
|
-
# go to the top of this file and change MITO_AI_URL to MITO_AI_DEV_URL. We
|
|
106
|
-
# have a pytest that ensures that the MITO_AI_URL is always set to MITO_AI_PROD_URL
|
|
107
|
-
# before merging into dev. So if you change which variable we are using here, the
|
|
108
|
-
# test will not catch our mistakes.
|
|
109
|
-
MITO_OPENAI_URL,
|
|
110
|
-
method="POST",
|
|
111
|
-
headers=headers,
|
|
112
|
-
body=json.dumps(data),
|
|
113
|
-
# For some reason, we need to add the request_timeout here as well
|
|
114
|
-
request_timeout=http_client_timeout
|
|
115
|
-
)
|
|
116
|
-
print(f"Request completed in {time.time() - start_time:.2f} seconds")
|
|
117
|
-
except Exception as e:
|
|
118
|
-
print(f"Request failed after {time.time() - start_time:.2f} seconds with error: {str(e)}")
|
|
119
|
-
raise
|
|
120
|
-
finally:
|
|
121
|
-
http_client.close()
|
|
122
|
-
|
|
123
|
-
# The lambda function returns a dictionary with a completion entry in it,
|
|
124
|
-
# so we just return that.
|
|
125
|
-
content = json.loads(res.body)
|
|
126
|
-
|
|
127
|
-
if "completion" in content:
|
|
128
|
-
return content["completion"] # type: ignore
|
|
129
|
-
elif "error" in content:
|
|
130
|
-
raise Exception(f"{content['error']}")
|
|
131
|
-
else:
|
|
132
|
-
raise Exception(f"No completion found in response: {content}")
|
|
93
|
+
return await get_response_from_mito_server(
|
|
94
|
+
MITO_OPENAI_URL,
|
|
95
|
+
headers,
|
|
96
|
+
data,
|
|
97
|
+
timeout,
|
|
98
|
+
max_retries,
|
|
99
|
+
message_type,
|
|
100
|
+
provider_name="OpenAI"
|
|
101
|
+
)
|
|
133
102
|
|
|
134
103
|
async def stream_ai_completion_from_mito_server(
|
|
135
104
|
last_message_content: Union[str, None],
|
|
@@ -137,8 +106,8 @@ async def stream_ai_completion_from_mito_server(
|
|
|
137
106
|
timeout: int,
|
|
138
107
|
max_retries: int,
|
|
139
108
|
message_type: MessageType,
|
|
140
|
-
reply_fn:
|
|
141
|
-
message_id:
|
|
109
|
+
reply_fn: Callable[[Union[CompletionReply, CompletionStreamChunk]], None],
|
|
110
|
+
message_id: str,
|
|
142
111
|
) -> AsyncGenerator[str, None]:
|
|
143
112
|
"""
|
|
144
113
|
Stream AI completions from the Mito server.
|
|
@@ -158,7 +127,7 @@ async def stream_ai_completion_from_mito_server(
|
|
|
158
127
|
Yields:
|
|
159
128
|
Chunks of text from the streaming response
|
|
160
129
|
"""
|
|
161
|
-
#
|
|
130
|
+
# Prepare request data and headers
|
|
162
131
|
data, headers = _prepare_request_data_and_headers(
|
|
163
132
|
last_message_content,
|
|
164
133
|
ai_completion_data,
|
|
@@ -167,118 +136,36 @@ async def stream_ai_completion_from_mito_server(
|
|
|
167
136
|
message_type
|
|
168
137
|
)
|
|
169
138
|
|
|
170
|
-
#
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
print(f"Error processing streaming chunk: {str(e)}")
|
|
185
|
-
|
|
186
|
-
# ===== STEP 4: Execute the streaming request =====
|
|
187
|
-
fetch_future = None
|
|
188
|
-
try:
|
|
189
|
-
# Use fetch with streaming_callback to handle streaming responses.
|
|
190
|
-
# The streaming_callback is not sent as part of the POST request.
|
|
191
|
-
# It's a parameter for the Tornado AsyncHTTPClient.fetch() method that specifies
|
|
192
|
-
# how to handle incoming data chunks as they arrive from the server.
|
|
193
|
-
# When the server sends data in chunks, this callback function is called each time
|
|
194
|
-
# a new chunk arrives, allowing for immediate processing without waiting for the
|
|
195
|
-
# entire response to complete.
|
|
196
|
-
fetch_future = http_client.fetch(
|
|
197
|
-
MITO_OPENAI_URL,
|
|
198
|
-
method="POST",
|
|
199
|
-
headers=headers,
|
|
200
|
-
body=json.dumps(data),
|
|
201
|
-
request_timeout=http_client_timeout,
|
|
202
|
-
streaming_callback=chunk_callback
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
# Create a task to wait for the fetch to complete
|
|
206
|
-
async def wait_for_fetch() -> None:
|
|
207
|
-
try:
|
|
208
|
-
await fetch_future
|
|
209
|
-
nonlocal fetch_complete
|
|
210
|
-
fetch_complete = True
|
|
211
|
-
print("Fetch completed")
|
|
212
|
-
except Exception as e:
|
|
213
|
-
print(f"Error in fetch: {str(e)}")
|
|
214
|
-
raise
|
|
215
|
-
|
|
216
|
-
# Start the task to wait for fetch completion
|
|
217
|
-
fetch_task = asyncio.create_task(wait_for_fetch())
|
|
218
|
-
|
|
219
|
-
# ===== STEP 5: Yield chunks as they arrive =====
|
|
220
|
-
while not (fetch_complete and chunk_queue.empty()):
|
|
221
|
-
try:
|
|
222
|
-
# Wait for a chunk with a timeout. By setting the timeout, we 1. prevent deadlocks
|
|
223
|
-
# which could happen if fetch_complete has not been set to true yet, and 2. it enables
|
|
224
|
-
# periodic checking if the queue has a new chunk.
|
|
225
|
-
chunk = await asyncio.wait_for(chunk_queue.get(), timeout=0.1)
|
|
226
|
-
|
|
227
|
-
# If reply_fn is provided, send the chunk directly to the frontend
|
|
228
|
-
if reply_fn and message_id:
|
|
229
|
-
reply_fn(CompletionStreamChunk(
|
|
230
|
-
parent_id=message_id,
|
|
231
|
-
chunk=CompletionItem(
|
|
232
|
-
content=chunk,
|
|
233
|
-
isIncomplete=True,
|
|
234
|
-
token=message_id,
|
|
235
|
-
),
|
|
236
|
-
done=False,
|
|
237
|
-
))
|
|
238
|
-
|
|
239
|
-
yield chunk
|
|
240
|
-
except asyncio.TimeoutError:
|
|
241
|
-
# No chunk available within timeout, check if fetch is complete
|
|
242
|
-
if fetch_complete and chunk_queue.empty():
|
|
243
|
-
break
|
|
244
|
-
|
|
245
|
-
# Otherwise continue waiting for chunks
|
|
246
|
-
continue
|
|
247
|
-
|
|
248
|
-
print(f"\nStream completed in {time.time() - start_time:.2f} seconds")
|
|
249
|
-
|
|
250
|
-
# Send a final chunk to indicate completion if reply_fn is provided
|
|
251
|
-
if reply_fn and message_id:
|
|
252
|
-
reply_fn(CompletionStreamChunk(
|
|
253
|
-
parent_id=message_id,
|
|
254
|
-
chunk=CompletionItem(
|
|
255
|
-
content="",
|
|
256
|
-
isIncomplete=False,
|
|
257
|
-
token=message_id,
|
|
258
|
-
),
|
|
259
|
-
done=True,
|
|
260
|
-
))
|
|
261
|
-
except Exception as e:
|
|
262
|
-
print(f"\nStream failed after {time.time() - start_time:.2f} seconds with error: {str(e)}")
|
|
263
|
-
# If an exception occurred, ensure the fetch future is awaited to properly clean up
|
|
264
|
-
if fetch_future:
|
|
265
|
-
try:
|
|
266
|
-
await fetch_future
|
|
267
|
-
except Exception:
|
|
268
|
-
pass
|
|
269
|
-
raise
|
|
270
|
-
finally:
|
|
271
|
-
# ===== STEP 6: Clean up resources =====
|
|
272
|
-
http_client.close()
|
|
139
|
+
# Use the unified streaming function
|
|
140
|
+
async for chunk in stream_response_from_mito_server(
|
|
141
|
+
url=MITO_OPENAI_URL,
|
|
142
|
+
headers=headers,
|
|
143
|
+
data=data,
|
|
144
|
+
timeout=timeout,
|
|
145
|
+
max_retries=max_retries,
|
|
146
|
+
message_type=message_type,
|
|
147
|
+
reply_fn=reply_fn,
|
|
148
|
+
message_id=message_id,
|
|
149
|
+
chunk_processor=None,
|
|
150
|
+
provider_name="OpenAI",
|
|
151
|
+
):
|
|
152
|
+
yield chunk
|
|
273
153
|
|
|
274
154
|
|
|
275
155
|
def get_open_ai_completion_function_params(
|
|
156
|
+
message_type: MessageType,
|
|
276
157
|
model: str,
|
|
277
158
|
messages: List[ChatCompletionMessageParam],
|
|
278
159
|
stream: bool,
|
|
279
160
|
response_format_info: Optional[ResponseFormatInfo] = None,
|
|
280
161
|
) -> Dict[str, Any]:
|
|
281
162
|
|
|
163
|
+
print("MESSAGE TYPE: ", message_type)
|
|
164
|
+
message_requires_fast_model = does_message_require_fast_model(message_type)
|
|
165
|
+
model = FAST_OPENAI_MODEL if message_requires_fast_model else model
|
|
166
|
+
|
|
167
|
+
print(f"model: {model}")
|
|
168
|
+
|
|
282
169
|
completion_function_params = {
|
|
283
170
|
"model": model,
|
|
284
171
|
"stream": stream,
|
|
@@ -309,9 +196,5 @@ def get_open_ai_completion_function_params(
|
|
|
309
196
|
"strict": True
|
|
310
197
|
}
|
|
311
198
|
}
|
|
312
|
-
|
|
313
|
-
# o3-mini will error if we try setting the temperature
|
|
314
|
-
if model == "gpt-4o-mini":
|
|
315
|
-
completion_function_params["temperature"] = 0.0
|
|
316
199
|
|
|
317
200
|
return completion_function_params
|
|
@@ -0,0 +1,49 @@
|
|
|
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 Union
|
|
5
|
+
|
|
6
|
+
from mito_ai.completions.models import MessageType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_model_provider(model: str) -> Union[str, None]:
|
|
10
|
+
"""
|
|
11
|
+
Determine the model type based on the model name prefix
|
|
12
|
+
"""
|
|
13
|
+
if not model:
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
model_lower = model.lower()
|
|
17
|
+
|
|
18
|
+
if model_lower.startswith('claude'):
|
|
19
|
+
return 'claude'
|
|
20
|
+
elif model_lower.startswith('gemini'):
|
|
21
|
+
return 'gemini'
|
|
22
|
+
elif model_lower.startswith('ollama'):
|
|
23
|
+
return 'ollama'
|
|
24
|
+
elif model_lower.startswith('gpt'):
|
|
25
|
+
return 'openai'
|
|
26
|
+
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def does_message_require_fast_model(message_type: MessageType) -> bool:
|
|
31
|
+
"""
|
|
32
|
+
Determines if a message requires the fast model.
|
|
33
|
+
|
|
34
|
+
The fast model is used for messages that are not chat messages.
|
|
35
|
+
For example, inline completions and chat name generation need to be fast
|
|
36
|
+
so they don't slow down the user's experience.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
if message_type in (MessageType.CHAT, MessageType.SMART_DEBUG, MessageType.CODE_EXPLAIN, MessageType.AGENT_EXECUTION, MessageType.AGENT_AUTO_ERROR_FIXUP):
|
|
40
|
+
return False
|
|
41
|
+
elif message_type in (MessageType.INLINE_COMPLETION, MessageType.CHAT_NAME_GENERATION):
|
|
42
|
+
return True
|
|
43
|
+
elif message_type in (MessageType.START_NEW_CHAT, MessageType.FETCH_HISTORY, MessageType.GET_THREADS, MessageType.DELETE_THREAD, MessageType.UPDATE_MODEL_CONFIG):
|
|
44
|
+
# These messages don't use any model, but we add them here for type safety
|
|
45
|
+
return True
|
|
46
|
+
else:
|
|
47
|
+
raise ValueError(f"Invalid message type: {message_type}")
|
|
48
|
+
|
|
49
|
+
|
mito_ai/utils/server_limits.py
CHANGED
|
@@ -28,7 +28,7 @@ free tier, but running AI models is expensive, so we need to limit the usage
|
|
|
28
28
|
or we will no longer be able to provide this free tier.
|
|
29
29
|
"""
|
|
30
30
|
# Monthly chat completions limit for free tier users
|
|
31
|
-
OS_MONTHLY_AI_COMPLETIONS_LIMIT: Final[int] =
|
|
31
|
+
OS_MONTHLY_AI_COMPLETIONS_LIMIT: Final[int] = 150
|
|
32
32
|
|
|
33
33
|
# Monthly autocomplete limit for free tier users
|
|
34
34
|
OS_MONTHLY_AUTOCOMPLETE_LIMIT: Final[int] = 5000
|
mito_ai/utils/telemetry_utils.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
|
-
from typing import Any, Dict, Literal, Optional
|
|
6
|
+
from typing import Any, Dict, Literal, Optional, List
|
|
7
7
|
from mito_ai.utils.version_utils import MITOSHEET_HELPER_PRIVATE, is_pro
|
|
8
8
|
from mito_ai.utils.schema import UJ_AI_MITO_API_NUM_USAGES, UJ_MITOSHEET_TELEMETRY, UJ_STATIC_USER_ID, UJ_USER_EMAIL, UJ_FEEDBACKS_V2
|
|
9
9
|
from mito_ai.utils.db import get_user_field
|
|
@@ -24,6 +24,7 @@ PRINT_LOGS = False
|
|
|
24
24
|
# Constants for logging the success or error of Mito AI
|
|
25
25
|
MITO_AI_COMPLETION_SUCCESS = 'mito_ai_success'
|
|
26
26
|
MITO_AI_COMPLETION_ERROR = 'mito_ai_error'
|
|
27
|
+
MITO_AI_COMPLETION_RETRY = 'mito_ai_retry'
|
|
27
28
|
|
|
28
29
|
# Params
|
|
29
30
|
# - logging the type of key
|
|
@@ -82,7 +83,6 @@ def telemetry_turned_on(key_type: Optional[str] = None) -> bool:
|
|
|
82
83
|
Helper function that tells you if logging is turned on or
|
|
83
84
|
turned off on the entire Mito instance
|
|
84
85
|
"""
|
|
85
|
-
|
|
86
86
|
# If the user is on the Mito server, then they are sending
|
|
87
87
|
# us their information already
|
|
88
88
|
if key_type == 'mito_server_key':
|
|
@@ -104,7 +104,7 @@ def telemetry_turned_on(key_type: Optional[str] = None) -> bool:
|
|
|
104
104
|
|
|
105
105
|
return bool(telemetry)
|
|
106
106
|
|
|
107
|
-
def identify(key_type: Optional[str] = None) -> None:
|
|
107
|
+
def identify(key_type: Optional[str] = None, is_electron: Optional[bool] = None) -> None:
|
|
108
108
|
"""
|
|
109
109
|
Helper function for identifying a user. We just take
|
|
110
110
|
their python version, mito version, and email.
|
|
@@ -125,6 +125,11 @@ def identify(key_type: Optional[str] = None) -> None:
|
|
|
125
125
|
IS_DEV_MODE_PARAM: is_dev_mode(),
|
|
126
126
|
UJ_FEEDBACKS_V2: feedbacks_v2
|
|
127
127
|
}
|
|
128
|
+
|
|
129
|
+
if is_electron is not None:
|
|
130
|
+
# Only update field when we get info from the client. Don't default to False.
|
|
131
|
+
# becase we are only sending this info to the first completion_handler identify call.
|
|
132
|
+
params['is_mito_desktop'] = is_electron
|
|
128
133
|
|
|
129
134
|
if not is_running_test():
|
|
130
135
|
# TODO: If the user is in JupyterLite, we need to do some extra work.
|
|
@@ -174,7 +179,6 @@ def log(
|
|
|
174
179
|
If telemetry is not turned off and we are not running tests,
|
|
175
180
|
we log the ai event
|
|
176
181
|
"""
|
|
177
|
-
|
|
178
182
|
final_params: Dict[str, Any] = params or {}
|
|
179
183
|
|
|
180
184
|
# Then, make sure to add the user email
|
|
@@ -186,7 +190,24 @@ def log(
|
|
|
186
190
|
|
|
187
191
|
if thread_id is not None:
|
|
188
192
|
final_params['thread_id'] = thread_id
|
|
189
|
-
|
|
193
|
+
|
|
194
|
+
# Process parameters that need chunking
|
|
195
|
+
params_to_remove = []
|
|
196
|
+
params_to_add = {}
|
|
197
|
+
|
|
198
|
+
for param_name, param_value in final_params.items():
|
|
199
|
+
if isinstance(param_value, str) and len(param_value) > 250:
|
|
200
|
+
# Mark for removal
|
|
201
|
+
params_to_remove.append(param_name)
|
|
202
|
+
# Get chunked parameters
|
|
203
|
+
chunked_params = chunk_param(param_value, param_name)
|
|
204
|
+
params_to_add.update(chunked_params)
|
|
205
|
+
|
|
206
|
+
# Apply the changes
|
|
207
|
+
for param_name in params_to_remove:
|
|
208
|
+
del final_params[param_name]
|
|
209
|
+
final_params.update(params_to_add)
|
|
210
|
+
|
|
190
211
|
# Finally, do the acutal logging. We do not log anything when tests are
|
|
191
212
|
# running, or if telemetry is turned off
|
|
192
213
|
if not is_running_test() and telemetry_turned_on(key_type):
|
|
@@ -346,3 +367,114 @@ def log_db_connection_error(connection_type: str, error_message: str) -> None:
|
|
|
346
367
|
"error_message": error_message,
|
|
347
368
|
}
|
|
348
369
|
)
|
|
370
|
+
|
|
371
|
+
def log_file_upload_attempt(
|
|
372
|
+
filename: str, file_extension: str, is_chunked: bool, total_chunks: int
|
|
373
|
+
) -> None:
|
|
374
|
+
log(
|
|
375
|
+
"mito_ai_file_upload_attempt",
|
|
376
|
+
params={
|
|
377
|
+
"filename": filename,
|
|
378
|
+
"file_extension": file_extension,
|
|
379
|
+
"is_chunked": is_chunked,
|
|
380
|
+
"total_chunks": total_chunks,
|
|
381
|
+
},
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
def log_file_upload_failure(error: str) -> None:
|
|
385
|
+
log("mito_ai_file_upload_failure", params={"error_message": error})
|
|
386
|
+
|
|
387
|
+
def log_ai_completion_retry(key_type: Literal['mito_server_key', 'user_key'], thread_id: str, message_type: MessageType, error: BaseException) -> None:
|
|
388
|
+
log(MITO_AI_COMPLETION_RETRY, params={KEY_TYPE_PARAM: key_type, "message_type": message_type.value}, thread_id=thread_id, key_type=key_type, error=error)
|
|
389
|
+
|
|
390
|
+
def log_ai_completion_error(
|
|
391
|
+
key_type: Literal['mito_server_key', 'user_key'],
|
|
392
|
+
thread_id: str,
|
|
393
|
+
message_type: MessageType,
|
|
394
|
+
error: BaseException
|
|
395
|
+
) -> None:
|
|
396
|
+
log(MITO_AI_COMPLETION_ERROR, params={KEY_TYPE_PARAM: key_type, "message_type": message_type.value}, thread_id=thread_id, key_type=key_type, error=error)
|
|
397
|
+
|
|
398
|
+
def log_mito_server_free_tier_limit_reached(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType) -> None:
|
|
399
|
+
log(MITO_SERVER_FREE_TIER_LIMIT_REACHED, params={KEY_TYPE_PARAM: key_type, "message_type": message_type.value}, key_type=key_type)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
#################################
|
|
403
|
+
# Streamlit Logs
|
|
404
|
+
#################################
|
|
405
|
+
|
|
406
|
+
###
|
|
407
|
+
# Converting Notebook into Streamlit App code
|
|
408
|
+
###
|
|
409
|
+
|
|
410
|
+
def log_streamlit_app_conversion_success(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, edit_prompt: str) -> None:
|
|
411
|
+
log(
|
|
412
|
+
"mito_ai_streamlit_app_conversion_success",
|
|
413
|
+
key_type=key_type,
|
|
414
|
+
params={
|
|
415
|
+
"edit_prompt": edit_prompt,
|
|
416
|
+
"message_type": message_type.value
|
|
417
|
+
}
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
def log_streamlit_app_validation_retry(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: List[str]) -> None:
|
|
421
|
+
log(
|
|
422
|
+
"mito_ai_streamlit_app_conversion_retry",
|
|
423
|
+
params={
|
|
424
|
+
"error_message": error,
|
|
425
|
+
"message_type": message_type.value
|
|
426
|
+
},
|
|
427
|
+
key_type=key_type
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
def log_streamlit_app_conversion_error(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error_message: str, formatted_traceback: str, edit_prompt: str) -> None:
|
|
431
|
+
log(
|
|
432
|
+
"mito_ai_streamlit_app_conversion_error",
|
|
433
|
+
params={
|
|
434
|
+
"error_message": error_message,
|
|
435
|
+
"traceback": formatted_traceback,
|
|
436
|
+
"edit_prompt": edit_prompt,
|
|
437
|
+
"message_type": message_type.value
|
|
438
|
+
},
|
|
439
|
+
key_type=key_type
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
###
|
|
443
|
+
# Setting up Preview
|
|
444
|
+
###
|
|
445
|
+
|
|
446
|
+
def log_streamlit_app_preview_success(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, edit_prompt: str) -> None:
|
|
447
|
+
log(
|
|
448
|
+
"mito_ai_streamlit_app_preview_success",
|
|
449
|
+
key_type=key_type,
|
|
450
|
+
params={
|
|
451
|
+
"edit_prompt": edit_prompt,
|
|
452
|
+
"message_type": message_type.value
|
|
453
|
+
}
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
def log_streamlit_app_preview_failure(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error_message: str, formatted_traceback: str, edit_prompt: str) -> None:
|
|
457
|
+
log(
|
|
458
|
+
"mito_ai_streamlit_app_preview_failure",
|
|
459
|
+
key_type=key_type,
|
|
460
|
+
params={
|
|
461
|
+
"error_message": error_message,
|
|
462
|
+
"traceback": formatted_traceback,
|
|
463
|
+
"message_type": message_type.value,
|
|
464
|
+
"edit_prompt": edit_prompt
|
|
465
|
+
}
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
###
|
|
469
|
+
# Deploying Streamlit App
|
|
470
|
+
###
|
|
471
|
+
|
|
472
|
+
def log_streamlit_app_deployment_failure(key_type: Literal['mito_server_key', 'user_key'], message_type: MessageType, error: Dict) -> None:
|
|
473
|
+
log(
|
|
474
|
+
"mito_ai_streamlit_app_deployment_failure",
|
|
475
|
+
key_type=key_type,
|
|
476
|
+
params={
|
|
477
|
+
"error": error, # Contains all details in app_deploy.models.AppDeployError class
|
|
478
|
+
"message_type": message_type.value
|
|
479
|
+
}
|
|
480
|
+
)
|