mbxai 0.5.22__tar.gz → 0.5.24__tar.gz

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 (25) hide show
  1. {mbxai-0.5.22 → mbxai-0.5.24}/.vscode/PythonImportHelper-v2-Completion.json +46 -4
  2. {mbxai-0.5.22 → mbxai-0.5.24}/PKG-INFO +1 -1
  3. {mbxai-0.5.22 → mbxai-0.5.24}/pyproject.toml +1 -1
  4. {mbxai-0.5.22 → mbxai-0.5.24}/setup.py +1 -1
  5. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/__init__.py +1 -1
  6. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/mcp/server.py +1 -1
  7. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/openrouter/client.py +107 -53
  8. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/openrouter/config.py +13 -1
  9. {mbxai-0.5.22 → mbxai-0.5.24}/uv.lock +7 -7
  10. {mbxai-0.5.22 → mbxai-0.5.24}/LICENSE +0 -0
  11. {mbxai-0.5.22 → mbxai-0.5.24}/README.md +0 -0
  12. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/core.py +0 -0
  13. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/mcp/__init__.py +0 -0
  14. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/mcp/client.py +0 -0
  15. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/mcp/example.py +0 -0
  16. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/openrouter/__init__.py +0 -0
  17. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/openrouter/models.py +0 -0
  18. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/tools/__init__.py +0 -0
  19. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/tools/client.py +0 -0
  20. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/tools/example.py +0 -0
  21. {mbxai-0.5.22 → mbxai-0.5.24}/src/mbxai/tools/types.py +0 -0
  22. {mbxai-0.5.22 → mbxai-0.5.24}/tests/test_core.py +0 -0
  23. {mbxai-0.5.22 → mbxai-0.5.24}/tests/test_mcp.py +0 -0
  24. {mbxai-0.5.22 → mbxai-0.5.24}/tests/test_openrouter.py +0 -0
  25. {mbxai-0.5.22 → mbxai-0.5.24}/tests/test_tools.py +0 -0
@@ -131,6 +131,14 @@
131
131
  "detail": "typing",
132
132
  "documentation": {}
133
133
  },
134
+ {
135
+ "label": "Optional",
136
+ "importPath": "typing",
137
+ "description": "typing",
138
+ "isExtraImport": true,
139
+ "detail": "typing",
140
+ "documentation": {}
141
+ },
134
142
  {
135
143
  "label": "ClassVar",
136
144
  "importPath": "typing",
@@ -286,6 +294,14 @@
286
294
  "detail": "pydantic",
287
295
  "documentation": {}
288
296
  },
297
+ {
298
+ "label": "Field",
299
+ "importPath": "pydantic",
300
+ "description": "pydantic",
301
+ "isExtraImport": true,
302
+ "detail": "pydantic",
303
+ "documentation": {}
304
+ },
289
305
  {
290
306
  "label": "BaseModel",
291
307
  "importPath": "pydantic",
@@ -422,6 +438,23 @@
422
438
  "detail": "openai",
423
439
  "documentation": {}
424
440
  },
441
+ {
442
+ "label": "time",
443
+ "kind": 6,
444
+ "isExtraImport": true,
445
+ "importPath": "time",
446
+ "description": "time",
447
+ "detail": "time",
448
+ "documentation": {}
449
+ },
450
+ {
451
+ "label": "wraps",
452
+ "importPath": "functools",
453
+ "description": "functools",
454
+ "isExtraImport": true,
455
+ "detail": "functools",
456
+ "documentation": {}
457
+ },
425
458
  {
426
459
  "label": "Enum",
427
460
  "importPath": "enum",
@@ -817,7 +850,7 @@
817
850
  "kind": 6,
818
851
  "importPath": "src.mbxai.openrouter.client",
819
852
  "description": "src.mbxai.openrouter.client",
820
- "peekOfCode": "class OpenRouterError(Exception):\n \"\"\"Base exception for OpenRouter client errors.\"\"\"\n pass\nclass OpenRouterConnectionError(OpenRouterError):\n \"\"\"Raised when there is a connection error.\"\"\"\n pass\nclass OpenRouterAPIError(OpenRouterError):\n \"\"\"Raised when the API returns an error.\"\"\"\n pass\nclass OpenRouterClient:",
853
+ "peekOfCode": "class OpenRouterError(Exception):\n \"\"\"Base exception for OpenRouter client errors.\"\"\"\n pass\nclass OpenRouterConnectionError(OpenRouterError):\n \"\"\"Raised when there is a connection error.\"\"\"\n pass\nclass OpenRouterAPIError(OpenRouterError):\n \"\"\"Raised when the API returns an error.\"\"\"\n pass\ndef with_retry(max_retries: int = 3, initial_delay: float = 1.0, max_delay: float = 10.0):",
821
854
  "detail": "src.mbxai.openrouter.client",
822
855
  "documentation": {}
823
856
  },
@@ -826,7 +859,7 @@
826
859
  "kind": 6,
827
860
  "importPath": "src.mbxai.openrouter.client",
828
861
  "description": "src.mbxai.openrouter.client",
829
- "peekOfCode": "class OpenRouterConnectionError(OpenRouterError):\n \"\"\"Raised when there is a connection error.\"\"\"\n pass\nclass OpenRouterAPIError(OpenRouterError):\n \"\"\"Raised when the API returns an error.\"\"\"\n pass\nclass OpenRouterClient:\n \"\"\"Client for interacting with the OpenRouter API.\"\"\"\n def __init__(\n self,",
862
+ "peekOfCode": "class OpenRouterConnectionError(OpenRouterError):\n \"\"\"Raised when there is a connection error.\"\"\"\n pass\nclass OpenRouterAPIError(OpenRouterError):\n \"\"\"Raised when the API returns an error.\"\"\"\n pass\ndef with_retry(max_retries: int = 3, initial_delay: float = 1.0, max_delay: float = 10.0):\n \"\"\"Decorator to add retry logic to a function.\n Args:\n max_retries: Maximum number of retry attempts",
830
863
  "detail": "src.mbxai.openrouter.client",
831
864
  "documentation": {}
832
865
  },
@@ -835,7 +868,7 @@
835
868
  "kind": 6,
836
869
  "importPath": "src.mbxai.openrouter.client",
837
870
  "description": "src.mbxai.openrouter.client",
838
- "peekOfCode": "class OpenRouterAPIError(OpenRouterError):\n \"\"\"Raised when the API returns an error.\"\"\"\n pass\nclass OpenRouterClient:\n \"\"\"Client for interacting with the OpenRouter API.\"\"\"\n def __init__(\n self,\n token: str,\n model: Union[str, OpenRouterModel] = OpenRouterModel.GPT4_TURBO,\n base_url: Optional[str] = None,",
871
+ "peekOfCode": "class OpenRouterAPIError(OpenRouterError):\n \"\"\"Raised when the API returns an error.\"\"\"\n pass\ndef with_retry(max_retries: int = 3, initial_delay: float = 1.0, max_delay: float = 10.0):\n \"\"\"Decorator to add retry logic to a function.\n Args:\n max_retries: Maximum number of retry attempts\n initial_delay: Initial delay between retries in seconds\n max_delay: Maximum delay between retries in seconds\n \"\"\"",
839
872
  "detail": "src.mbxai.openrouter.client",
840
873
  "documentation": {}
841
874
  },
@@ -844,7 +877,16 @@
844
877
  "kind": 6,
845
878
  "importPath": "src.mbxai.openrouter.client",
846
879
  "description": "src.mbxai.openrouter.client",
847
- "peekOfCode": "class OpenRouterClient:\n \"\"\"Client for interacting with the OpenRouter API.\"\"\"\n def __init__(\n self,\n token: str,\n model: Union[str, OpenRouterModel] = OpenRouterModel.GPT4_TURBO,\n base_url: Optional[str] = None,\n default_headers: Optional[dict[str, str]] = None,\n ) -> None:\n \"\"\"Initialize the OpenRouter client.",
880
+ "peekOfCode": "class OpenRouterClient:\n \"\"\"Client for interacting with the OpenRouter API.\"\"\"\n def __init__(\n self,\n token: str,\n model: Union[str, OpenRouterModel] = OpenRouterModel.GPT4_TURBO,\n base_url: Optional[str] = None,\n default_headers: Optional[dict[str, str]] = None,\n max_retries: int = 3,\n retry_initial_delay: float = 1.0,",
881
+ "detail": "src.mbxai.openrouter.client",
882
+ "documentation": {}
883
+ },
884
+ {
885
+ "label": "with_retry",
886
+ "kind": 2,
887
+ "importPath": "src.mbxai.openrouter.client",
888
+ "description": "src.mbxai.openrouter.client",
889
+ "peekOfCode": "def with_retry(max_retries: int = 3, initial_delay: float = 1.0, max_delay: float = 10.0):\n \"\"\"Decorator to add retry logic to a function.\n Args:\n max_retries: Maximum number of retry attempts\n initial_delay: Initial delay between retries in seconds\n max_delay: Maximum delay between retries in seconds\n \"\"\"\n def decorator(func):\n @wraps(func)\n async def async_wrapper(*args, **kwargs):",
848
890
  "detail": "src.mbxai.openrouter.client",
849
891
  "documentation": {}
850
892
  },
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mbxai
3
- Version: 0.5.22
3
+ Version: 0.5.24
4
4
  Summary: MBX AI SDK
5
5
  Project-URL: Homepage, https://www.mibexx.de
6
6
  Project-URL: Documentation, https://www.mibexx.de
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mbxai"
7
- version = "0.5.22"
7
+ version = "0.5.24"
8
8
  authors = [
9
9
  { name = "MBX AI" }
10
10
  ]
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="mbxai",
5
- version="0.5.22",
5
+ version="0.5.24",
6
6
  author="MBX AI",
7
7
  description="MBX AI SDK",
8
8
  long_description=open("README.md").read(),
@@ -2,4 +2,4 @@
2
2
  MBX AI package.
3
3
  """
4
4
 
5
- __version__ = "0.5.22"
5
+ __version__ = "0.5.24"
@@ -31,7 +31,7 @@ class MCPServer:
31
31
  self.app = FastAPI(
32
32
  title=self.name,
33
33
  description=self.description,
34
- version="0.5.22",
34
+ version="0.5.24",
35
35
  )
36
36
 
37
37
  # Initialize MCP server
@@ -4,10 +4,12 @@ OpenRouter client implementation.
4
4
 
5
5
  from typing import Any, Optional, Union
6
6
  from openai import OpenAI, OpenAIError
7
- from pydantic import BaseModel, TypeAdapter
7
+ from pydantic import BaseModel, TypeAdapter, Field
8
8
  from .models import OpenRouterModel, OpenRouterModelRegistry
9
9
  from .config import OpenRouterConfig
10
10
  import logging
11
+ import time
12
+ from functools import wraps
11
13
 
12
14
  logger = logging.getLogger(__name__)
13
15
 
@@ -27,6 +29,65 @@ class OpenRouterAPIError(OpenRouterError):
27
29
  pass
28
30
 
29
31
 
32
+ def with_retry(max_retries: int = 3, initial_delay: float = 1.0, max_delay: float = 10.0):
33
+ """Decorator to add retry logic to a function.
34
+
35
+ Args:
36
+ max_retries: Maximum number of retry attempts
37
+ initial_delay: Initial delay between retries in seconds
38
+ max_delay: Maximum delay between retries in seconds
39
+ """
40
+ def decorator(func):
41
+ @wraps(func)
42
+ async def async_wrapper(*args, **kwargs):
43
+ last_error = None
44
+ delay = initial_delay
45
+
46
+ for attempt in range(max_retries + 1):
47
+ try:
48
+ return await func(*args, **kwargs)
49
+ except (OpenRouterConnectionError, OpenRouterAPIError) as e:
50
+ last_error = e
51
+ if attempt < max_retries:
52
+ logger.warning(f"Attempt {attempt + 1} failed: {str(e)}. Retrying in {delay:.1f}s...")
53
+ await asyncio.sleep(delay)
54
+ delay = min(delay * 2, max_delay)
55
+ else:
56
+ logger.error(f"All {max_retries + 1} attempts failed")
57
+ raise last_error
58
+ except Exception as e:
59
+ # Don't retry other types of errors
60
+ raise e
61
+
62
+ raise last_error
63
+
64
+ @wraps(func)
65
+ def sync_wrapper(*args, **kwargs):
66
+ last_error = None
67
+ delay = initial_delay
68
+
69
+ for attempt in range(max_retries + 1):
70
+ try:
71
+ return func(*args, **kwargs)
72
+ except (OpenRouterConnectionError, OpenRouterAPIError) as e:
73
+ last_error = e
74
+ if attempt < max_retries:
75
+ logger.warning(f"Attempt {attempt + 1} failed: {str(e)}. Retrying in {delay:.1f}s...")
76
+ time.sleep(delay)
77
+ delay = min(delay * 2, max_delay)
78
+ else:
79
+ logger.error(f"All {max_retries + 1} attempts failed")
80
+ raise last_error
81
+ except Exception as e:
82
+ # Don't retry other types of errors
83
+ raise e
84
+
85
+ raise last_error
86
+
87
+ return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper
88
+ return decorator
89
+
90
+
30
91
  class OpenRouterClient:
31
92
  """Client for interacting with the OpenRouter API."""
32
93
 
@@ -36,6 +97,9 @@ class OpenRouterClient:
36
97
  model: Union[str, OpenRouterModel] = OpenRouterModel.GPT4_TURBO,
37
98
  base_url: Optional[str] = None,
38
99
  default_headers: Optional[dict[str, str]] = None,
100
+ max_retries: int = 3,
101
+ retry_initial_delay: float = 1.0,
102
+ retry_max_delay: float = 10.0,
39
103
  ) -> None:
40
104
  """Initialize the OpenRouter client.
41
105
 
@@ -44,6 +108,9 @@ class OpenRouterClient:
44
108
  model: The model to use (default: GPT4_TURBO)
45
109
  base_url: Optional custom base URL for the API
46
110
  default_headers: Optional default headers for API requests
111
+ max_retries: Maximum number of retry attempts (default: 3)
112
+ retry_initial_delay: Initial delay between retries in seconds (default: 1.0)
113
+ retry_max_delay: Maximum delay between retries in seconds (default: 10.0)
47
114
 
48
115
  Raises:
49
116
  OpenRouterError: If initialization fails
@@ -56,7 +123,10 @@ class OpenRouterClient:
56
123
  default_headers=default_headers or {
57
124
  "HTTP-Referer": "https://github.com/mibexx/mbxai",
58
125
  "X-Title": "MBX AI",
59
- }
126
+ },
127
+ max_retries=max_retries,
128
+ retry_initial_delay=retry_initial_delay,
129
+ retry_max_delay=retry_max_delay,
60
130
  )
61
131
 
62
132
  self._client = OpenAI(
@@ -86,6 +156,10 @@ class OpenRouterClient:
86
156
  raise OpenRouterAPIError(f"API error during {operation}: {error_msg}")
87
157
  elif "Connection" in error_msg:
88
158
  raise OpenRouterConnectionError(f"Connection error during {operation}: {error_msg}")
159
+ elif "Expecting value" in error_msg and "line" in error_msg:
160
+ # This is a JSON parsing error, likely due to a truncated or malformed response
161
+ logger.error("JSON parsing error detected. This might be due to a large response or network issues.")
162
+ raise OpenRouterAPIError(f"Response parsing error during {operation}. The response might be too large or malformed.")
89
163
  else:
90
164
  raise OpenRouterError(f"Error during {operation}: {error_msg}")
91
165
 
@@ -111,80 +185,59 @@ class OpenRouterClient:
111
185
  """
112
186
  self.model = value
113
187
 
188
+ @with_retry()
114
189
  def chat_completion(
115
190
  self,
116
191
  messages: list[dict[str, Any]],
117
192
  *,
118
- model: Optional[Union[str, OpenRouterModel]] = None,
193
+ model: str | None = None,
119
194
  stream: bool = False,
120
195
  **kwargs: Any,
121
196
  ) -> Any:
122
- """Create a chat completion.
123
-
124
- Args:
125
- messages: list of messages
126
- model: Optional model override
127
- stream: Whether to stream the response
128
- **kwargs: Additional parameters
129
-
130
- Returns:
131
- Completion response
132
-
133
- Raises:
134
- OpenRouterConnectionError: For connection issues
135
- OpenRouterAPIError: For API errors
136
- OpenRouterError: For other errors
137
- """
197
+ """Get a chat completion from OpenRouter."""
138
198
  try:
139
- # Remove any incompatible parameters
140
- kwargs.pop("parse", None) # Remove parse parameter if present
199
+ # Log the request details
200
+ logger.info(f"Sending chat completion request to OpenRouter with model: {model or self.model}")
201
+ logger.info(f"Message count: {len(messages)}")
141
202
 
142
- logger.debug(f"Making chat completion request with model {model or self.model}")
143
- logger.debug(f"Request messages: {messages}")
144
- logger.debug(f"Request kwargs: {kwargs}")
203
+ # Calculate total message size for logging
204
+ total_size = sum(len(str(msg)) for msg in messages)
205
+ logger.info(f"Total message size: {total_size} bytes")
145
206
 
146
207
  response = self._client.chat.completions.create(
147
- model=str(model or self.model),
148
208
  messages=messages,
209
+ model=model or self.model,
149
210
  stream=stream,
150
211
  **kwargs,
151
212
  )
152
213
 
153
- # Log response structure for debugging
154
- logger.debug(f"Response type: {type(response)}")
155
- logger.debug(f"Response attributes: {dir(response)}")
156
- logger.debug(f"Raw response: {response}")
157
-
158
- # Handle streaming response differently
159
- if stream:
160
- return response
161
-
162
- # Validate response structure
163
- if response is None:
164
- logger.error("Received None response from API")
165
- raise OpenRouterError("Received None response from API")
166
-
167
- if not hasattr(response, 'choices'):
168
- logger.error(f"Invalid response: missing choices attribute. Response: {response}")
169
- raise OpenRouterError("Invalid response: missing choices attribute")
170
-
171
- if not response.choices:
172
- logger.error(f"Invalid response: empty choices list. Response: {response}")
173
- raise OpenRouterError("Invalid response: empty choices list")
174
-
175
- if not hasattr(response.choices[0], 'message'):
176
- logger.error(f"Invalid response: missing message in first choice. Response: {response}")
177
- raise OpenRouterError("Invalid response: missing message in first choice")
214
+ # Log response details
215
+ logger.info("Received response from OpenRouter")
216
+ if hasattr(response, 'choices') and response.choices:
217
+ logger.info(f"Response has {len(response.choices)} choices")
218
+ if hasattr(response.choices[0], 'message'):
219
+ content = response.choices[0].message.content
220
+ content_length = len(content) if content else 0
221
+ logger.info(f"First choice has message with content length: {content_length}")
222
+ if content_length > 1000000: # Log warning for very large responses
223
+ logger.warning(f"Response content is very large ({content_length} bytes)")
178
224
 
179
- logger.debug(f"Response message: {response.choices[0].message}")
180
225
  return response
181
226
 
182
227
  except Exception as e:
183
228
  logger.error(f"Error in chat completion: {str(e)}")
184
- if isinstance(e, OpenRouterError):
185
- raise
229
+ if hasattr(e, 'response') and e.response is not None:
230
+ logger.error(f"Response status: {e.response.status_code}")
231
+ logger.error(f"Response headers: {e.response.headers}")
232
+ try:
233
+ content = e.response.text
234
+ logger.error(f"Response content length: {len(content)} bytes")
235
+ logger.error(f"Response content preview: {content[:1000]}...")
236
+ except:
237
+ logger.error("Could not read response content")
186
238
  self._handle_api_error("chat completion", e)
187
239
 
240
+ @with_retry()
188
241
  def chat_completion_parse(
189
242
  self,
190
243
  messages: list[dict[str, Any]],
@@ -251,6 +304,7 @@ class OpenRouterClient:
251
304
  except Exception as e:
252
305
  self._handle_api_error("chat completion parse", e)
253
306
 
307
+ @with_retry()
254
308
  def embeddings(
255
309
  self,
256
310
  input: Union[str, list[str]],
@@ -1,5 +1,5 @@
1
1
  from pydantic import BaseModel, Field, field_validator
2
- from typing import Union
2
+ from typing import Union, Optional
3
3
  from .models import OpenRouterModel, OpenRouterModelRegistry
4
4
 
5
5
  class OpenRouterConfig(BaseModel):
@@ -21,6 +21,18 @@ class OpenRouterConfig(BaseModel):
21
21
  },
22
22
  description="Default headers to include in all requests"
23
23
  )
24
+ max_retries: int = Field(
25
+ default=3,
26
+ description="Maximum number of retry attempts"
27
+ )
28
+ retry_initial_delay: float = Field(
29
+ default=1.0,
30
+ description="Initial delay between retries in seconds"
31
+ )
32
+ retry_max_delay: float = Field(
33
+ default=10.0,
34
+ description="Maximum delay between retries in seconds"
35
+ )
24
36
 
25
37
  @field_validator("token")
26
38
  def validate_token(cls, v: str) -> str:
@@ -292,11 +292,11 @@ wheels = [
292
292
 
293
293
  [[package]]
294
294
  name = "httpx-sse"
295
- version = "0.5.22"
295
+ version = "0.5.24"
296
296
  source = { registry = "https://pypi.org/simple" }
297
- sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.22.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
297
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.24.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
298
298
  wheels = [
299
- { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.22-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
299
+ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.24-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
300
300
  ]
301
301
 
302
302
  [[package]]
@@ -446,7 +446,7 @@ wheels = [
446
446
 
447
447
  [[package]]
448
448
  name = "mbxai"
449
- version = "0.5.22"
449
+ version = "0.5.24"
450
450
  source = { editable = "." }
451
451
  dependencies = [
452
452
  { name = "fastapi" },
@@ -980,14 +980,14 @@ wheels = [
980
980
 
981
981
  [[package]]
982
982
  name = "typing-inspection"
983
- version = "0.5.22"
983
+ version = "0.5.24"
984
984
  source = { registry = "https://pypi.org/simple" }
985
985
  dependencies = [
986
986
  { name = "typing-extensions" },
987
987
  ]
988
- sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.5.22.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
988
+ sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.5.24.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
989
989
  wheels = [
990
- { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.22-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
990
+ { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.24-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
991
991
  ]
992
992
 
993
993
  [[package]]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes