mito-ai 0.1.57__py3-none-any.whl → 0.1.59__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 (92) hide show
  1. mito_ai/__init__.py +19 -22
  2. mito_ai/_version.py +1 -1
  3. mito_ai/anthropic_client.py +24 -14
  4. mito_ai/chart_wizard/handlers.py +78 -17
  5. mito_ai/chart_wizard/urls.py +8 -5
  6. mito_ai/completions/completion_handlers/agent_auto_error_fixup_handler.py +6 -8
  7. mito_ai/completions/completion_handlers/agent_execution_handler.py +6 -8
  8. mito_ai/completions/completion_handlers/chat_completion_handler.py +13 -17
  9. mito_ai/completions/completion_handlers/code_explain_handler.py +13 -17
  10. mito_ai/completions/completion_handlers/completion_handler.py +3 -5
  11. mito_ai/completions/completion_handlers/inline_completer_handler.py +5 -6
  12. mito_ai/completions/completion_handlers/scratchpad_result_handler.py +6 -8
  13. mito_ai/completions/completion_handlers/smart_debug_handler.py +13 -17
  14. mito_ai/completions/completion_handlers/utils.py +3 -7
  15. mito_ai/completions/handlers.py +32 -22
  16. mito_ai/completions/message_history.py +8 -10
  17. mito_ai/completions/prompt_builders/chart_add_field_prompt.py +35 -0
  18. mito_ai/completions/prompt_builders/prompt_constants.py +2 -0
  19. mito_ai/constants.py +31 -2
  20. mito_ai/enterprise/__init__.py +1 -1
  21. mito_ai/enterprise/litellm_client.py +144 -0
  22. mito_ai/enterprise/utils.py +16 -2
  23. mito_ai/log/handlers.py +1 -1
  24. mito_ai/openai_client.py +36 -96
  25. mito_ai/provider_manager.py +420 -0
  26. mito_ai/settings/enterprise_handler.py +26 -0
  27. mito_ai/settings/urls.py +2 -0
  28. mito_ai/streamlit_conversion/agent_utils.py +2 -30
  29. mito_ai/streamlit_conversion/streamlit_agent_handler.py +48 -46
  30. mito_ai/streamlit_preview/handlers.py +6 -3
  31. mito_ai/streamlit_preview/urls.py +5 -3
  32. mito_ai/tests/message_history/test_generate_short_chat_name.py +103 -28
  33. mito_ai/tests/open_ai_utils_test.py +34 -36
  34. mito_ai/tests/providers/test_anthropic_client.py +174 -16
  35. mito_ai/tests/providers/test_azure.py +15 -15
  36. mito_ai/tests/providers/test_capabilities.py +14 -17
  37. mito_ai/tests/providers/test_gemini_client.py +14 -13
  38. mito_ai/tests/providers/test_model_resolution.py +145 -89
  39. mito_ai/tests/providers/test_openai_client.py +209 -13
  40. mito_ai/tests/providers/test_provider_limits.py +5 -5
  41. mito_ai/tests/providers/test_providers.py +229 -51
  42. mito_ai/tests/providers/test_retry_logic.py +13 -22
  43. mito_ai/tests/providers/utils.py +4 -4
  44. mito_ai/tests/streamlit_conversion/test_streamlit_agent_handler.py +57 -85
  45. mito_ai/tests/streamlit_preview/test_streamlit_preview_handler.py +4 -1
  46. mito_ai/tests/test_constants.py +90 -0
  47. mito_ai/tests/test_enterprise_mode.py +217 -0
  48. mito_ai/tests/test_model_utils.py +362 -0
  49. mito_ai/utils/anthropic_utils.py +8 -6
  50. mito_ai/utils/gemini_utils.py +0 -3
  51. mito_ai/utils/litellm_utils.py +84 -0
  52. mito_ai/utils/model_utils.py +257 -0
  53. mito_ai/utils/open_ai_utils.py +29 -41
  54. mito_ai/utils/provider_utils.py +13 -29
  55. mito_ai/utils/telemetry_utils.py +14 -2
  56. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/build_log.json +102 -102
  57. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/package.json +2 -2
  58. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/package.json.orig +1 -1
  59. mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.9d26322f3e78beb2b666.js → mito_ai-0.1.59.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.44c109c7be36fb884d25.js +1059 -144
  60. mito_ai-0.1.59.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.44c109c7be36fb884d25.js.map +1 -0
  61. mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.79c1ea8a3cda73a4cb6f.js → mito_ai-0.1.59.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.f7decebaf69618541e0f.js +17 -17
  62. mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.79c1ea8a3cda73a4cb6f.js.map → mito_ai-0.1.59.data/data/share/jupyter/labextensions/mito_ai/static/remoteEntry.f7decebaf69618541e0f.js.map +1 -1
  63. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.css +78 -78
  64. {mito_ai-0.1.57.dist-info → mito_ai-0.1.59.dist-info}/METADATA +2 -1
  65. {mito_ai-0.1.57.dist-info → mito_ai-0.1.59.dist-info}/RECORD +90 -83
  66. mito_ai/completions/providers.py +0 -284
  67. mito_ai-0.1.57.data/data/share/jupyter/labextensions/mito_ai/static/lib_index_js.9d26322f3e78beb2b666.js.map +0 -1
  68. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/etc/jupyter/jupyter_server_config.d/mito_ai.json +0 -0
  69. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/schemas/mito_ai/toolbar-buttons.json +0 -0
  70. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js +0 -0
  71. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/node_modules_process_browser_js.4b128e94d31a81ebd209.js.map +0 -0
  72. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/style.js +0 -0
  73. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js +0 -0
  74. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/style_index_js.f5d476ac514294615881.js.map +0 -0
  75. {mito_ai-0.1.57.data → mito_ai-0.1.59.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
  76. {mito_ai-0.1.57.data → mito_ai-0.1.59.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
  77. {mito_ai-0.1.57.data → mito_ai-0.1.59.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
  78. {mito_ai-0.1.57.data → mito_ai-0.1.59.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
  79. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js +0 -0
  80. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_dist_esm_index_mjs.6bac1a8c4cc93f15f6b7.js.map +0 -0
  81. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js +0 -0
  82. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_aws-amplify_ui-react_dist_esm_index_mjs.4fcecd65bef9e9847609.js.map +0 -0
  83. {mito_ai-0.1.57.data → mito_ai-0.1.59.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
  84. {mito_ai-0.1.57.data → mito_ai-0.1.59.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
  85. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js +0 -0
  86. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_semver_index_js.3f6754ac5116d47de76b.js.map +0 -0
  87. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js +0 -0
  88. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/static/vendors-node_modules_vscode-diff_dist_index_js.ea55f1f9346638aafbcf.js.map +0 -0
  89. {mito_ai-0.1.57.data → mito_ai-0.1.59.data}/data/share/jupyter/labextensions/mito_ai/themes/mito_ai/index.js +0 -0
  90. {mito_ai-0.1.57.dist-info → mito_ai-0.1.59.dist-info}/WHEEL +0 -0
  91. {mito_ai-0.1.57.dist-info → mito_ai-0.1.59.dist-info}/entry_points.txt +0 -0
  92. {mito_ai-0.1.57.dist-info → mito_ai-0.1.59.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,144 @@
1
+ # Copyright (c) Saga Inc.
2
+ # Distributed under the terms of the The Mito Enterprise license.
3
+
4
+ from typing import Optional, List, Callable, Union, Dict, Any
5
+ from openai.types.chat import ChatCompletionMessageParam
6
+ from mito_ai.completions.models import (
7
+ MessageType,
8
+ ResponseFormatInfo,
9
+ CompletionReply,
10
+ CompletionStreamChunk,
11
+ CompletionItem,
12
+ )
13
+ from mito_ai.utils.litellm_utils import get_litellm_completion_function_params
14
+ from mito_ai.utils.model_utils import strip_router_prefix
15
+ import litellm
16
+
17
+ class LiteLLMClient:
18
+ """
19
+ A client for interacting with LiteLLM server endpoints.
20
+ LiteLLM provides an OpenAI-compatible API, so we use the LiteLLM SDK directly.
21
+ """
22
+
23
+ def __init__(self, api_key: Optional[str], base_url: str, timeout: int = 30, max_retries: int = 1):
24
+ self.api_key = api_key
25
+ self.base_url = base_url
26
+ self.timeout = timeout
27
+ self.max_retries = max_retries
28
+
29
+ async def request_completions(
30
+ self,
31
+ messages: List[ChatCompletionMessageParam],
32
+ model: str, # Should include provider prefix (e.g., "LiteLLM/openai/gpt-4o")
33
+ response_format_info: Optional[ResponseFormatInfo] = None,
34
+ message_type: MessageType = MessageType.CHAT
35
+ ) -> str:
36
+ """
37
+ Request completions from LiteLLM server.
38
+
39
+ Args:
40
+ messages: List of chat messages
41
+ model: Model name with router and provider prefix (e.g., "LiteLLM/openai/gpt-4o")
42
+ response_format_info: Optional response format specification
43
+ message_type: Type of message (chat, agent execution, etc.)
44
+
45
+ Returns:
46
+ The completion text response
47
+ """
48
+ # Strip router prefix if present (LiteLLM/ prefix)
49
+ model_for_litellm = strip_router_prefix(model)
50
+
51
+ # Prepare parameters for LiteLLM
52
+ params = get_litellm_completion_function_params(
53
+ model=model_for_litellm,
54
+ messages=messages,
55
+ api_key=self.api_key,
56
+ api_base=self.base_url,
57
+ timeout=self.timeout,
58
+ stream=False,
59
+ response_format_info=response_format_info,
60
+ )
61
+
62
+ try:
63
+ # Use LiteLLM's acompletion function
64
+ response = await litellm.acompletion(**params)
65
+
66
+ # Extract content from response
67
+ if response and response.choices and len(response.choices) > 0:
68
+ content = response.choices[0].message.content
69
+ return content or ""
70
+ else:
71
+ return ""
72
+ except Exception as e:
73
+ raise Exception(f"LiteLLM completion error: {str(e)}")
74
+
75
+ async def stream_completions(
76
+ self,
77
+ messages: List[ChatCompletionMessageParam],
78
+ model: str,
79
+ message_type: MessageType,
80
+ message_id: str,
81
+ reply_fn: Callable[[Union[CompletionReply, CompletionStreamChunk]], None],
82
+ response_format_info: Optional[ResponseFormatInfo] = None
83
+ ) -> str:
84
+ """
85
+ Stream completions from LiteLLM server.
86
+
87
+ Args:
88
+ messages: List of chat messages
89
+ model: Model name with router and provider prefix (e.g., "LiteLLM/openai/gpt-4o")
90
+ message_type: Type of message (chat, agent execution, etc.)
91
+ message_id: ID of the message being processed
92
+ reply_fn: Function to call with each chunk for streaming replies
93
+ response_format_info: Optional response format specification
94
+
95
+ Returns:
96
+ The accumulated response string
97
+ """
98
+ accumulated_response = ""
99
+
100
+ # Strip router prefix if present (LiteLLM/ prefix)
101
+ model_for_litellm = strip_router_prefix(model)
102
+
103
+ # Prepare parameters for LiteLLM
104
+ params = get_litellm_completion_function_params(
105
+ model=model_for_litellm,
106
+ messages=messages,
107
+ api_key=self.api_key,
108
+ api_base=self.base_url,
109
+ timeout=self.timeout,
110
+ stream=True,
111
+ response_format_info=response_format_info,
112
+ )
113
+
114
+ try:
115
+ # Use LiteLLM's acompletion with stream=True
116
+ # When stream=True, acompletion returns an async iterable after awaiting
117
+ stream = await litellm.acompletion(**params)
118
+
119
+ # Process streaming chunks
120
+ async for chunk in stream:
121
+ if chunk and chunk.choices and len(chunk.choices) > 0:
122
+ delta = chunk.choices[0].delta
123
+ content = delta.content if delta and delta.content else ""
124
+
125
+ if content:
126
+ accumulated_response += content
127
+
128
+ # Check if this is the final chunk
129
+ is_finished = chunk.choices[0].finish_reason is not None
130
+
131
+ # Send chunk to frontend
132
+ reply_fn(CompletionStreamChunk(
133
+ parent_id=message_id,
134
+ chunk=CompletionItem(
135
+ content=content,
136
+ isIncomplete=not is_finished,
137
+ token=message_id,
138
+ ),
139
+ done=is_finished,
140
+ ))
141
+
142
+ return accumulated_response
143
+ except Exception as e:
144
+ raise Exception(f"LiteLLM streaming error: {str(e)}")
@@ -5,11 +5,25 @@
5
5
  # Distributed under the terms of the The Mito Enterprise license.
6
6
 
7
7
  from mito_ai.utils.version_utils import is_enterprise, is_mitosheet_private
8
- from mito_ai.constants import AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_VERSION, AZURE_OPENAI_MODEL
8
+ from mito_ai.constants import (
9
+ AZURE_OPENAI_API_KEY,
10
+ AZURE_OPENAI_ENDPOINT,
11
+ AZURE_OPENAI_API_VERSION,
12
+ AZURE_OPENAI_MODEL,
13
+ ABACUS_BASE_URL,
14
+ ABACUS_MODELS
15
+ )
9
16
 
10
17
  def is_azure_openai_configured() -> bool:
11
18
  """
12
19
  Azure OpenAI is only supported for Mito Enterprise users
13
20
  """
14
21
  is_allowed_to_use_azure = is_enterprise() or is_mitosheet_private()
15
- return all([is_allowed_to_use_azure, AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_VERSION, AZURE_OPENAI_MODEL])
22
+ return all([is_allowed_to_use_azure, AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_VERSION, AZURE_OPENAI_MODEL])
23
+
24
+ def is_abacus_configured() -> bool:
25
+ """
26
+ Abacus AI is only supported for Mito Enterprise users.
27
+ Checks if Abacus AI is configured with base URL and models.
28
+ """
29
+ return all([is_enterprise(), ABACUS_BASE_URL, ABACUS_MODELS])
mito_ai/log/handlers.py CHANGED
@@ -32,7 +32,7 @@ class LogHandler(APIHandler):
32
32
  log_event = data['log_event']
33
33
  params = data.get('params', {})
34
34
 
35
- key_type = MITO_SERVER_KEY if self.key_type == "mito_server_key" else USER_KEY
35
+ key_type = MITO_SERVER_KEY if self.key_type == MITO_SERVER_KEY else USER_KEY
36
36
  log(log_event, params, key_type=key_type)
37
37
 
38
38
 
mito_ai/openai_client.py CHANGED
@@ -7,12 +7,13 @@ from typing import Any, AsyncGenerator, Callable, Dict, List, Optional, Union
7
7
  from mito_ai.utils.mito_server_utils import ProviderCompletionException
8
8
  import openai
9
9
  from openai.types.chat import ChatCompletionMessageParam
10
- from traitlets import Instance, Unicode, default, validate
10
+ from traitlets import Instance, default, validate
11
11
  from traitlets.config import LoggingConfigurable
12
12
 
13
13
  from mito_ai import constants
14
- from mito_ai.enterprise.utils import is_azure_openai_configured
14
+ from mito_ai.enterprise.utils import is_azure_openai_configured, is_abacus_configured
15
15
  from mito_ai.logger import get_logger
16
+ from mito_ai.utils.model_utils import strip_router_prefix
16
17
  from mito_ai.completions.models import (
17
18
  AICapabilities,
18
19
  CompletionError,
@@ -24,28 +25,17 @@ from mito_ai.completions.models import (
24
25
  ResponseFormatInfo,
25
26
  )
26
27
  from mito_ai.utils.open_ai_utils import (
27
- check_mito_server_quota,
28
28
  get_ai_completion_from_mito_server,
29
29
  get_open_ai_completion_function_params,
30
30
  stream_ai_completion_from_mito_server,
31
31
  )
32
- from mito_ai.utils.server_limits import update_mito_server_quota
33
- from mito_ai.utils.telemetry_utils import (
34
- MITO_SERVER_KEY,
35
- USER_KEY,
36
- )
32
+ from mito_ai.utils.server_limits import update_mito_server_quota, check_mito_server_quota
37
33
 
38
34
  OPENAI_MODEL_FALLBACK = "gpt-4.1"
39
35
 
40
36
  class OpenAIClient(LoggingConfigurable):
41
37
  """Provide AI feature through OpenAI services."""
42
38
 
43
- api_key = Unicode(
44
- config=True,
45
- allow_none=True,
46
- help="OpenAI API key. Default value is read from the OPENAI_API_KEY environment variable.",
47
- )
48
-
49
39
  last_error = Instance(
50
40
  CompletionError,
51
41
  allow_none=True,
@@ -65,61 +55,6 @@ This attribute is observed by the websocket provider to push the error to the cl
65
55
  super().__init__(log=get_logger(), **kwargs)
66
56
  self.last_error = None
67
57
  self._async_client: Optional[openai.AsyncOpenAI] = None
68
-
69
- @default("api_key")
70
- def _api_key_default(self) -> Optional[str]:
71
- default_key = constants.OPENAI_API_KEY
72
- return self._validate_api_key(default_key)
73
-
74
- @validate("api_key")
75
- def _validate_api_key(self, api_key: Optional[str]) -> Optional[str]:
76
- if not api_key:
77
- self.log.debug(
78
- "No OpenAI API key provided; following back to Mito server API."
79
- )
80
- return None
81
-
82
- client = openai.OpenAI(api_key=api_key)
83
- try:
84
- # Make an http request to OpenAI to make sure it works
85
- client.models.list()
86
- except openai.AuthenticationError as e:
87
- self.log.warning(
88
- "Invalid OpenAI API key provided.",
89
- exc_info=e,
90
- )
91
- self.last_error = CompletionError.from_exception(
92
- e,
93
- hint="You're missing the OPENAI_API_KEY environment variable. Run the following code in your terminal to set the environment variable and then relaunch the jupyter server `export OPENAI_API_KEY=<your-api-key>`",
94
- )
95
- return None
96
- except openai.PermissionDeniedError as e:
97
- self.log.warning(
98
- "Invalid OpenAI API key provided.",
99
- exc_info=e,
100
- )
101
- self.last_error = CompletionError.from_exception(e)
102
- return None
103
- except openai.InternalServerError as e:
104
- self.log.debug(
105
- "Unable to get OpenAI models due to OpenAI error.", exc_info=e
106
- )
107
- return api_key
108
- except openai.RateLimitError as e:
109
- self.log.debug(
110
- "Unable to get OpenAI models due to rate limit error.", exc_info=e
111
- )
112
- return api_key
113
- except openai.APIConnectionError as e:
114
- self.log.warning(
115
- "Unable to connect to OpenAI API.",
116
- exec_info=e,
117
- )
118
- self.last_error = CompletionError.from_exception(e)
119
- return None
120
- else:
121
- self.log.debug("User OpenAI API key validated.")
122
- return api_key
123
58
 
124
59
  @property
125
60
  def capabilities(self) -> AICapabilities:
@@ -133,7 +68,15 @@ This attribute is observed by the websocket provider to push the error to the cl
133
68
  provider="Azure OpenAI",
134
69
  )
135
70
 
136
- if constants.OLLAMA_MODEL and not self.api_key:
71
+ if is_abacus_configured():
72
+ return AICapabilities(
73
+ configuration={
74
+ "model": "<dynamic>"
75
+ },
76
+ provider="Abacus AI",
77
+ )
78
+
79
+ if constants.OLLAMA_MODEL:
137
80
  return AICapabilities(
138
81
  configuration={
139
82
  "model": constants.OLLAMA_MODEL
@@ -141,14 +84,12 @@ This attribute is observed by the websocket provider to push the error to the cl
141
84
  provider="Ollama",
142
85
  )
143
86
 
144
- if self.api_key:
145
- self._validate_api_key(self.api_key)
146
-
87
+ if constants.OPENAI_API_KEY:
147
88
  return AICapabilities(
148
89
  configuration={
149
- "model": OPENAI_MODEL_FALLBACK,
90
+ "model": "<dynamic>"
150
91
  },
151
- provider="OpenAI (user key)",
92
+ provider="OpenAI",
152
93
  )
153
94
 
154
95
  try:
@@ -169,19 +110,6 @@ This attribute is observed by the websocket provider to push the error to the cl
169
110
  if not self._async_client or self._async_client.is_closed():
170
111
  self._async_client = self._build_openai_client()
171
112
  return self._async_client
172
-
173
-
174
- @property
175
- def key_type(self) -> str:
176
- """Returns the authentication key type being used."""
177
-
178
- if self.api_key:
179
- return USER_KEY
180
-
181
- if constants.OLLAMA_MODEL:
182
- return "ollama"
183
-
184
- return MITO_SERVER_KEY
185
113
 
186
114
  def _build_openai_client(self) -> Optional[Union[openai.AsyncOpenAI, openai.AsyncAzureOpenAI]]:
187
115
  base_url = None
@@ -201,12 +129,16 @@ This attribute is observed by the websocket provider to push the error to the cl
201
129
  timeout=self.timeout,
202
130
  )
203
131
 
204
- elif constants.OLLAMA_MODEL and not self.api_key:
132
+ elif is_abacus_configured():
133
+ base_url = constants.ABACUS_BASE_URL
134
+ llm_api_key = constants.ABACUS_API_KEY
135
+ self.log.debug(f"Using Abacus AI with base URL: {constants.ABACUS_BASE_URL}")
136
+ elif constants.OLLAMA_MODEL:
205
137
  base_url = constants.OLLAMA_BASE_URL
206
138
  llm_api_key = "ollama"
207
139
  self.log.debug(f"Using Ollama with model: {constants.OLLAMA_MODEL}")
208
- elif self.api_key:
209
- llm_api_key = self.api_key
140
+ elif constants.OPENAI_API_KEY:
141
+ llm_api_key = constants.OPENAI_API_KEY
210
142
  self.log.debug("Using OpenAI with user-provided API key")
211
143
  else:
212
144
  self.log.warning("No valid API key or model configuration provided")
@@ -221,17 +153,25 @@ This attribute is observed by the websocket provider to push the error to the cl
221
153
  )
222
154
  return client
223
155
 
224
- def _adjust_model_for_azure_or_ollama(self, model: str) -> str:
156
+ def _adjust_model_for_provider(self, model: str) -> str:
225
157
 
226
158
  # If they have set an Azure OpenAI model, then we always use it
227
159
  if is_azure_openai_configured() and constants.AZURE_OPENAI_MODEL is not None:
228
160
  self.log.debug(f"Resolving to Azure OpenAI model: {constants.AZURE_OPENAI_MODEL}")
161
+ # TODO: We should update Azure so it works the way LiteLLM and Abacus do:
162
+ # when configured, we only show models from Azure in the UI.
229
163
  return constants.AZURE_OPENAI_MODEL
230
164
 
231
165
  # If they have set an Ollama model, then we use it
232
166
  if constants.OLLAMA_MODEL is not None:
233
167
  return constants.OLLAMA_MODEL
234
168
 
169
+ # If using Abacus, strip the "Abacus/" prefix from the model name
170
+ if is_abacus_configured() and model.lower().startswith('abacus/'):
171
+ stripped_model = strip_router_prefix(model)
172
+ self.log.debug(f"Stripping Abacus prefix: {model} -> {stripped_model}")
173
+ return stripped_model
174
+
235
175
  # Otherwise, we use the model they provided
236
176
  return model
237
177
 
@@ -262,11 +202,11 @@ This attribute is observed by the websocket provider to push the error to the cl
262
202
 
263
203
  # Handle other providers as before
264
204
  completion_function_params = get_open_ai_completion_function_params(
265
- message_type, model, messages, False, response_format_info
205
+ model, messages, False, response_format_info
266
206
  )
267
207
 
268
208
  # If they have set an Azure OpenAI or Ollama model, then we use it
269
- completion_function_params["model"] = self._adjust_model_for_azure_or_ollama(completion_function_params["model"])
209
+ completion_function_params["model"] = self._adjust_model_for_provider(completion_function_params["model"])
270
210
 
271
211
  if self._active_async_client is not None:
272
212
  response = await self._active_async_client.chat.completions.create(**completion_function_params)
@@ -313,10 +253,10 @@ This attribute is observed by the websocket provider to push the error to the cl
313
253
 
314
254
  # Handle other providers as before
315
255
  completion_function_params = get_open_ai_completion_function_params(
316
- message_type, model, messages, True, response_format_info
256
+ model, messages, True, response_format_info
317
257
  )
318
258
 
319
- completion_function_params["model"] = self._adjust_model_for_azure_or_ollama(completion_function_params["model"])
259
+ completion_function_params["model"] = self._adjust_model_for_provider(completion_function_params["model"])
320
260
 
321
261
  try:
322
262
  if self._active_async_client is not None: