keycycle 0.2.2__tar.gz → 0.2.3__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 (48) hide show
  1. {keycycle-0.2.2 → keycycle-0.2.3}/PKG-INFO +1 -1
  2. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/key_rotation/rotating_mixin.py +18 -10
  3. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle.egg-info/PKG-INFO +1 -1
  4. {keycycle-0.2.2 → keycycle-0.2.3}/pyproject.toml +1 -1
  5. {keycycle-0.2.2 → keycycle-0.2.3}/MANIFEST.in +0 -0
  6. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/__init__.py +0 -0
  7. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/adapters/__init__.py +0 -0
  8. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/adapters/generic_adapter.py +0 -0
  9. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/adapters/openai_adapter.py +0 -0
  10. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/__init__.py +0 -0
  11. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/constants.py +0 -0
  12. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/dataclasses.py +0 -0
  13. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/enums.py +0 -0
  14. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/loader.py +0 -0
  15. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/log_config.py +0 -0
  16. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/models/cerebras.yaml +0 -0
  17. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/models/cohere.yaml +0 -0
  18. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/models/gemini.yaml +0 -0
  19. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/models/groq.yaml +0 -0
  20. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/models/openrouter.yaml +0 -0
  21. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/models/openrouter_models.yaml +0 -0
  22. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/config/models.py +0 -0
  23. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/core/__init__.py +0 -0
  24. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/core/backoff.py +0 -0
  25. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/core/exceptions.py +0 -0
  26. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/core/utils.py +0 -0
  27. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/key_rotation/__init__.py +0 -0
  28. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/key_rotation/rotation_manager.py +0 -0
  29. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/legacy_multi_provider_wrapper.py +0 -0
  30. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/multi_client_wrapper.py +0 -0
  31. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/py.typed +0 -0
  32. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/usage/__init__.py +0 -0
  33. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/usage/db_logic.py +0 -0
  34. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/usage/usage_logger.py +0 -0
  35. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle/utils.py +0 -0
  36. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle.egg-info/SOURCES.txt +0 -0
  37. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle.egg-info/dependency_links.txt +0 -0
  38. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle.egg-info/requires.txt +0 -0
  39. {keycycle-0.2.2 → keycycle-0.2.3}/keycycle.egg-info/top_level.txt +0 -0
  40. {keycycle-0.2.2 → keycycle-0.2.3}/setup.cfg +0 -0
  41. {keycycle-0.2.2 → keycycle-0.2.3}/tests/test_api_key_pinning.py +0 -0
  42. {keycycle-0.2.2 → keycycle-0.2.3}/tests/test_api_key_retrieval.py +0 -0
  43. {keycycle-0.2.2 → keycycle-0.2.3}/tests/test_cerebras_direct.py +0 -0
  44. {keycycle-0.2.2 → keycycle-0.2.3}/tests/test_custom_key_limits.py +0 -0
  45. {keycycle-0.2.2 → keycycle-0.2.3}/tests/test_multi_client_wrapper.py +0 -0
  46. {keycycle-0.2.2 → keycycle-0.2.3}/tests/test_rate_limit_logic.py +0 -0
  47. {keycycle-0.2.2 → keycycle-0.2.3}/tests/test_specific_key_retrieval.py +0 -0
  48. {keycycle-0.2.2 → keycycle-0.2.3}/tests/test_streams_recording.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: keycycle
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A thread-safe key rotation and rate limiting manager for API keys.
5
5
  Author-email: William Jin <jinwilliam.jin@gmail.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -14,7 +14,17 @@ from ..config.constants import (
14
14
  )
15
15
  from ..core.utils import is_rate_limit_error, is_temporary_rate_limit_error, get_key_suffix
16
16
  from ..core.backoff import ExponentialBackoff, BackoffConfig
17
- from agno.models.response import ModelResponse
17
+ if TYPE_CHECKING:
18
+ from agno.models.response import ModelResponse
19
+
20
+
21
+ class _UsageResponse:
22
+ """Minimal response holder for usage recording when agno is not available."""
23
+ __slots__ = ('response_usage',)
24
+
25
+ def __init__(self, usage=None):
26
+ self.response_usage = usage
27
+
18
28
 
19
29
  class RotatingCredentialsMixin:
20
30
  """
@@ -100,7 +110,7 @@ class RotatingCredentialsMixin:
100
110
  def _get_retry_limit(self) -> int:
101
111
  return min(self._max_retries, len(self.wrapper.manager.keys) - 1)
102
112
 
103
- def _record_usage(self, key_obj: KeyUsage, response: Optional[ModelResponse]):
113
+ def _record_usage(self, key_obj: KeyUsage, response: Optional["ModelResponse"]):
104
114
  """
105
115
  Extracts usage from the response and reports it to the manager.
106
116
  Falls back to estimated_tokens if response is None or usage is missing.
@@ -132,7 +142,7 @@ class RotatingCredentialsMixin:
132
142
  multiplier=TEMP_RATE_LIMIT_MULTIPLIER,
133
143
  ))
134
144
 
135
- def invoke(self, *args, **kwargs) -> ModelResponse:
145
+ def invoke(self, *args, **kwargs) -> "ModelResponse":
136
146
  limit = self._get_retry_limit()
137
147
 
138
148
  for attempt in range(limit + 1):
@@ -178,7 +188,7 @@ class RotatingCredentialsMixin:
178
188
  continue
179
189
  raise
180
190
 
181
- async def ainvoke(self, *args, **kwargs) -> ModelResponse:
191
+ async def ainvoke(self, *args, **kwargs) -> "ModelResponse":
182
192
  limit = self._get_retry_limit()
183
193
 
184
194
  for attempt in range(limit + 1):
@@ -224,7 +234,7 @@ class RotatingCredentialsMixin:
224
234
  continue
225
235
  raise
226
236
 
227
- def invoke_stream(self, *args, **kwargs) -> Iterator[ModelResponse]:
237
+ def invoke_stream(self, *args, **kwargs) -> Iterator["ModelResponse"]:
228
238
  limit = self._get_retry_limit()
229
239
 
230
240
  for attempt in range(limit + 1):
@@ -242,8 +252,7 @@ class RotatingCredentialsMixin:
242
252
  final_usage = chunk.response_usage
243
253
  yield chunk
244
254
  if final_usage:
245
- dummy_response = ModelResponse()
246
- dummy_response.response_usage = final_usage
255
+ dummy_response = _UsageResponse(final_usage)
247
256
  self._record_usage(key_usage, dummy_response)
248
257
  else:
249
258
  # If the stream didn't return usage data, fallback to estimation
@@ -280,7 +289,7 @@ class RotatingCredentialsMixin:
280
289
  continue
281
290
  raise
282
291
 
283
- async def ainvoke_stream(self, *args, **kwargs) -> AsyncIterator[ModelResponse]:
292
+ async def ainvoke_stream(self, *args, **kwargs) -> AsyncIterator["ModelResponse"]:
284
293
  limit = self._get_retry_limit()
285
294
 
286
295
  for attempt in range(limit + 1):
@@ -301,8 +310,7 @@ class RotatingCredentialsMixin:
301
310
 
302
311
  # Stream completed successfully. Record usage.
303
312
  if final_usage:
304
- dummy_response = ModelResponse()
305
- dummy_response.response_usage = final_usage
313
+ dummy_response = _UsageResponse(final_usage)
306
314
  self._record_usage(key_usage, dummy_response)
307
315
  else:
308
316
  self._record_usage(key_usage, None)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: keycycle
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A thread-safe key rotation and rate limiting manager for API keys.
5
5
  Author-email: William Jin <jinwilliam.jin@gmail.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "keycycle"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  authors = [
9
9
  { name="William Jin", email="jinwilliam.jin@gmail.com" },
10
10
  ]
File without changes
File without changes
File without changes
File without changes
File without changes