sdkrouter 0.1.1__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 (96) hide show
  1. sdkrouter/__init__.py +110 -0
  2. sdkrouter/_api/__init__.py +28 -0
  3. sdkrouter/_api/client.py +204 -0
  4. sdkrouter/_api/generated/__init__.py +21 -0
  5. sdkrouter/_api/generated/cdn/__init__.py +209 -0
  6. sdkrouter/_api/generated/cdn/cdn__api__cdn/__init__.py +7 -0
  7. sdkrouter/_api/generated/cdn/cdn__api__cdn/client.py +133 -0
  8. sdkrouter/_api/generated/cdn/cdn__api__cdn/models.py +163 -0
  9. sdkrouter/_api/generated/cdn/cdn__api__cdn/sync_client.py +132 -0
  10. sdkrouter/_api/generated/cdn/client.py +75 -0
  11. sdkrouter/_api/generated/cdn/logger.py +256 -0
  12. sdkrouter/_api/generated/cdn/pyproject.toml +55 -0
  13. sdkrouter/_api/generated/cdn/retry.py +272 -0
  14. sdkrouter/_api/generated/cdn/sync_client.py +58 -0
  15. sdkrouter/_api/generated/cleaner/__init__.py +212 -0
  16. sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/__init__.py +7 -0
  17. sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/client.py +83 -0
  18. sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/models.py +117 -0
  19. sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/sync_client.py +82 -0
  20. sdkrouter/_api/generated/cleaner/client.py +75 -0
  21. sdkrouter/_api/generated/cleaner/enums.py +55 -0
  22. sdkrouter/_api/generated/cleaner/logger.py +256 -0
  23. sdkrouter/_api/generated/cleaner/pyproject.toml +55 -0
  24. sdkrouter/_api/generated/cleaner/retry.py +272 -0
  25. sdkrouter/_api/generated/cleaner/sync_client.py +58 -0
  26. sdkrouter/_api/generated/keys/__init__.py +212 -0
  27. sdkrouter/_api/generated/keys/client.py +75 -0
  28. sdkrouter/_api/generated/keys/enums.py +64 -0
  29. sdkrouter/_api/generated/keys/keys__api__keys/__init__.py +7 -0
  30. sdkrouter/_api/generated/keys/keys__api__keys/client.py +150 -0
  31. sdkrouter/_api/generated/keys/keys__api__keys/models.py +152 -0
  32. sdkrouter/_api/generated/keys/keys__api__keys/sync_client.py +149 -0
  33. sdkrouter/_api/generated/keys/logger.py +256 -0
  34. sdkrouter/_api/generated/keys/pyproject.toml +55 -0
  35. sdkrouter/_api/generated/keys/retry.py +272 -0
  36. sdkrouter/_api/generated/keys/sync_client.py +58 -0
  37. sdkrouter/_api/generated/models/__init__.py +209 -0
  38. sdkrouter/_api/generated/models/client.py +75 -0
  39. sdkrouter/_api/generated/models/logger.py +256 -0
  40. sdkrouter/_api/generated/models/models__api__llm_models/__init__.py +7 -0
  41. sdkrouter/_api/generated/models/models__api__llm_models/client.py +99 -0
  42. sdkrouter/_api/generated/models/models__api__llm_models/models.py +206 -0
  43. sdkrouter/_api/generated/models/models__api__llm_models/sync_client.py +99 -0
  44. sdkrouter/_api/generated/models/pyproject.toml +55 -0
  45. sdkrouter/_api/generated/models/retry.py +272 -0
  46. sdkrouter/_api/generated/models/sync_client.py +58 -0
  47. sdkrouter/_api/generated/shortlinks/__init__.py +209 -0
  48. sdkrouter/_api/generated/shortlinks/client.py +75 -0
  49. sdkrouter/_api/generated/shortlinks/logger.py +256 -0
  50. sdkrouter/_api/generated/shortlinks/pyproject.toml +55 -0
  51. sdkrouter/_api/generated/shortlinks/retry.py +272 -0
  52. sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/__init__.py +7 -0
  53. sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/client.py +137 -0
  54. sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/models.py +153 -0
  55. sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/sync_client.py +136 -0
  56. sdkrouter/_api/generated/shortlinks/sync_client.py +58 -0
  57. sdkrouter/_api/generated/vision/__init__.py +212 -0
  58. sdkrouter/_api/generated/vision/client.py +75 -0
  59. sdkrouter/_api/generated/vision/enums.py +40 -0
  60. sdkrouter/_api/generated/vision/logger.py +256 -0
  61. sdkrouter/_api/generated/vision/pyproject.toml +55 -0
  62. sdkrouter/_api/generated/vision/retry.py +272 -0
  63. sdkrouter/_api/generated/vision/sync_client.py +58 -0
  64. sdkrouter/_api/generated/vision/vision__api__vision/__init__.py +7 -0
  65. sdkrouter/_api/generated/vision/vision__api__vision/client.py +65 -0
  66. sdkrouter/_api/generated/vision/vision__api__vision/models.py +138 -0
  67. sdkrouter/_api/generated/vision/vision__api__vision/sync_client.py +65 -0
  68. sdkrouter/_client.py +432 -0
  69. sdkrouter/_config.py +74 -0
  70. sdkrouter/_constants.py +21 -0
  71. sdkrouter/_internal/__init__.py +1 -0
  72. sdkrouter/_types/__init__.py +30 -0
  73. sdkrouter/_types/cdn.py +27 -0
  74. sdkrouter/_types/models.py +26 -0
  75. sdkrouter/_types/ocr.py +24 -0
  76. sdkrouter/_types/parsed.py +101 -0
  77. sdkrouter/_types/shortlinks.py +27 -0
  78. sdkrouter/_types/vision.py +29 -0
  79. sdkrouter/_version.py +3 -0
  80. sdkrouter/helpers/__init__.py +13 -0
  81. sdkrouter/helpers/formatting.py +15 -0
  82. sdkrouter/helpers/html.py +100 -0
  83. sdkrouter/helpers/json_cleaner.py +53 -0
  84. sdkrouter/tools/__init__.py +129 -0
  85. sdkrouter/tools/cdn.py +285 -0
  86. sdkrouter/tools/cleaner.py +186 -0
  87. sdkrouter/tools/keys.py +215 -0
  88. sdkrouter/tools/models.py +196 -0
  89. sdkrouter/tools/shortlinks.py +165 -0
  90. sdkrouter/tools/vision.py +173 -0
  91. sdkrouter/utils/__init__.py +27 -0
  92. sdkrouter/utils/parsing.py +109 -0
  93. sdkrouter/utils/tokens.py +375 -0
  94. sdkrouter-0.1.1.dist-info/METADATA +411 -0
  95. sdkrouter-0.1.1.dist-info/RECORD +96 -0
  96. sdkrouter-0.1.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,212 @@
1
+ # Auto-generated by DjangoCFG - see CLAUDE.md
2
+ """
3
+ SDKRouter API - API Client with JWT Management
4
+
5
+ Usage:
6
+ >>> from api import API
7
+ >>>
8
+ >>> api = API('https://api.example.com')
9
+ >>>
10
+ >>> # Set JWT token
11
+ >>> api.set_token('your-jwt-token', 'refresh-token')
12
+ >>>
13
+ >>> # Use API
14
+ >>> async with api:
15
+ ... posts = await api.posts.list()
16
+ ... user = await api.users.retrieve(1)
17
+ >>>
18
+ >>> # Check authentication
19
+ >>> if api.is_authenticated():
20
+ ... # ...
21
+ >>>
22
+ >>> # Get OpenAPI schema path
23
+ >>> schema_path = api.get_schema_path()
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import threading
29
+ from typing import Any
30
+
31
+ import httpx
32
+
33
+ from .client import APIClient
34
+ from .logger import LoggerConfig
35
+ from .retry import RetryConfig
36
+ from .keys__api__keys import KeysKeysAPI
37
+ from . import enums
38
+ # NOTE: Individual enum imports commented out due to invalid Python syntax with dotted names
39
+ # from .enums import APIKeyCreate.permission, APIKeyCreateRequest.permission, APIKeyDetail.permission, APIKeyList.permission
40
+
41
+ TOKEN_KEY = "auth_token"
42
+ REFRESH_TOKEN_KEY = "refresh_token"
43
+
44
+ # Auto-generated by DjangoCFG - see CLAUDE.md
45
+ class API:
46
+ """
47
+ API Client wrapper with JWT token management.
48
+
49
+ This class provides:
50
+ - Thread-safe JWT token storage
51
+ - Automatic Authorization header injection
52
+ - Context manager support for async operations
53
+ - Optional retry and logging configuration
54
+
55
+ Example:
56
+ >>> api = API('https://api.example.com')
57
+ >>> api.set_token('jwt-token')
58
+ >>> async with api:
59
+ ... users = await api.users.list()
60
+ >>>
61
+ >>> # With retry and logging
62
+ >>> api = API(
63
+ ... 'https://api.example.com',
64
+ ... retry_config=RetryConfig(max_attempts=5),
65
+ ... logger_config=LoggerConfig(enabled=True)
66
+ ... )
67
+ """
68
+
69
+ def __init__(
70
+ self,
71
+ base_url: str,
72
+ logger_config: LoggerConfig | None = None,
73
+ retry_config: RetryConfig | None = None,
74
+ **kwargs: Any
75
+ ):
76
+ """
77
+ Initialize API client.
78
+
79
+ Args:
80
+ base_url: Base API URL (e.g., 'https://api.example.com')
81
+ logger_config: Logger configuration (None to disable logging)
82
+ retry_config: Retry configuration (None to disable retry)
83
+ **kwargs: Additional httpx.AsyncClient kwargs
84
+ """
85
+ self.base_url = base_url.rstrip('/')
86
+ self._kwargs = kwargs
87
+ self._logger_config = logger_config
88
+ self._retry_config = retry_config
89
+ self._token: str | None = None
90
+ self._refresh_token: str | None = None
91
+ self._lock = threading.Lock()
92
+ self._client: APIClient | None = None
93
+ self._init_clients()
94
+
95
+ def _init_clients(self) -> None:
96
+ """Initialize API client with current token."""
97
+ # Create httpx client with auth header if token exists
98
+ headers = {}
99
+ if self._token:
100
+ headers['Authorization'] = f'Bearer {self._token}'
101
+
102
+ kwargs = {**self._kwargs}
103
+ if headers:
104
+ kwargs['headers'] = headers
105
+
106
+ # Create new APIClient
107
+ self._client = APIClient(
108
+ self.base_url,
109
+ logger_config=self._logger_config,
110
+ retry_config=self._retry_config,
111
+ **kwargs
112
+ )
113
+
114
+ @property
115
+ def keys_keys(self) -> KeysKeysAPI:
116
+ """Access keys endpoints."""
117
+ return self._client.keys_keys
118
+
119
+ def get_token(self) -> str | None:
120
+ """Get current JWT token."""
121
+ with self._lock:
122
+ return self._token
123
+
124
+ def get_refresh_token(self) -> str | None:
125
+ """Get current refresh token."""
126
+ with self._lock:
127
+ return self._refresh_token
128
+
129
+ def set_token(self, token: str, refresh_token: str | None = None) -> None:
130
+ """
131
+ Set JWT token and refresh token.
132
+
133
+ Args:
134
+ token: JWT access token
135
+ refresh_token: JWT refresh token (optional)
136
+ """
137
+ with self._lock:
138
+ self._token = token
139
+ if refresh_token:
140
+ self._refresh_token = refresh_token
141
+
142
+ # Reinitialize clients with new token
143
+ self._init_clients()
144
+
145
+ def clear_tokens(self) -> None:
146
+ """Clear all tokens."""
147
+ with self._lock:
148
+ self._token = None
149
+ self._refresh_token = None
150
+
151
+ # Reinitialize clients without token
152
+ self._init_clients()
153
+
154
+ def is_authenticated(self) -> bool:
155
+ """Check if user is authenticated."""
156
+ return self.get_token() is not None
157
+
158
+ def set_base_url(self, url: str) -> None:
159
+ """
160
+ Update base URL and reinitialize clients.
161
+
162
+ Args:
163
+ url: New base URL
164
+ """
165
+ self.base_url = url.rstrip('/')
166
+ self._init_clients()
167
+
168
+ def get_base_url(self) -> str:
169
+ """Get current base URL."""
170
+ return self.base_url
171
+
172
+ def get_schema_path(self) -> str:
173
+ """
174
+ Get OpenAPI schema path.
175
+
176
+ Returns:
177
+ Path to the OpenAPI schema JSON file
178
+
179
+ Note:
180
+ The OpenAPI schema is available in the schema.json file.
181
+ You can load it dynamically using:
182
+ ```python
183
+ import json
184
+ from pathlib import Path
185
+
186
+ schema_path = Path(__file__).parent / 'schema.json'
187
+ with open(schema_path) as f:
188
+ schema = json.load(f)
189
+ ```
190
+ """
191
+ return './schema.json'
192
+
193
+ async def __aenter__(self) -> 'API':
194
+ """Async context manager entry."""
195
+ if self._client:
196
+ await self._client.__aenter__()
197
+ return self
198
+
199
+ async def __aexit__(self, *args: Any) -> None:
200
+ """Async context manager exit."""
201
+ if self._client:
202
+ await self._client.__aexit__(*args)
203
+
204
+ async def close(self) -> None:
205
+ """Close HTTP client."""
206
+ if self._client:
207
+ await self._client.close()
208
+
209
+ __all__ = [
210
+ "API",
211
+ "APIClient",
212
+ ]
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+ import httpx
6
+
7
+ from .keys__api__keys import KeysKeysAPI
8
+ from .logger import APILogger, LoggerConfig
9
+ from .retry import RetryConfig, RetryAsyncClient
10
+
11
+
12
+ class APIClient:
13
+ """
14
+ Async API client for SDKRouter API.
15
+
16
+ Usage:
17
+ >>> async with APIClient(base_url='https://api.example.com') as client:
18
+ ... users = await client.users.list()
19
+ ... post = await client.posts.create(data=new_post)
20
+ >>>
21
+ >>> # With retry configuration
22
+ >>> retry_config = RetryConfig(max_attempts=5, min_wait=2.0)
23
+ >>> async with APIClient(base_url='https://api.example.com', retry_config=retry_config) as client:
24
+ ... users = await client.users.list()
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ base_url: str,
30
+ logger_config: Optional[LoggerConfig] = None,
31
+ retry_config: Optional[RetryConfig] = None,
32
+ **kwargs: Any,
33
+ ):
34
+ """
35
+ Initialize API client.
36
+
37
+ Args:
38
+ base_url: Base API URL (e.g., 'https://api.example.com')
39
+ logger_config: Logger configuration (None to disable logging)
40
+ retry_config: Retry configuration (None to disable retry)
41
+ **kwargs: Additional httpx.AsyncClient kwargs
42
+ """
43
+ self.base_url = base_url.rstrip('/')
44
+
45
+ # Create HTTP client with or without retry
46
+ if retry_config is not None:
47
+ self._client = RetryAsyncClient(
48
+ base_url=self.base_url,
49
+ retry_config=retry_config,
50
+ **kwargs,
51
+ )
52
+ else:
53
+ self._client = httpx.AsyncClient(
54
+ base_url=self.base_url,
55
+ **kwargs,
56
+ )
57
+
58
+ # Initialize logger
59
+ self.logger: Optional[APILogger] = None
60
+ if logger_config is not None:
61
+ self.logger = APILogger(logger_config)
62
+
63
+ # Initialize sub-clients
64
+ self.keys_keys = KeysKeysAPI(self._client)
65
+
66
+ async def __aenter__(self) -> 'APIClient':
67
+ await self._client.__aenter__()
68
+ return self
69
+
70
+ async def __aexit__(self, *args: Any) -> None:
71
+ await self._client.__aexit__(*args)
72
+
73
+ async def close(self) -> None:
74
+ """Close HTTP client."""
75
+ await self._client.aclose()
@@ -0,0 +1,64 @@
1
+ # Auto-generated by DjangoCFG - see CLAUDE.md
2
+ from enum import IntEnum, Enum
3
+
4
+ # Python 3.10 compatibility: StrEnum was added in Python 3.11
5
+ # Use str + Enum instead for backward compatibility
6
+ class StrEnum(str, Enum):
7
+ """String Enum for Python 3.10+ compatibility"""
8
+ pass
9
+
10
+
11
+ class APIKeyCreatePermission(StrEnum):
12
+ """
13
+ * `read` - Read Only
14
+ * `write` - Read & Write
15
+ * `admin` - Admin
16
+ """
17
+
18
+ READ = "read"
19
+ WRITE = "write"
20
+ ADMIN = "admin"
21
+
22
+
23
+
24
+ class APIKeyCreateRequestPermission(StrEnum):
25
+ """
26
+ * `read` - Read Only
27
+ * `write` - Read & Write
28
+ * `admin` - Admin
29
+ """
30
+
31
+ READ = "read"
32
+ WRITE = "write"
33
+ ADMIN = "admin"
34
+
35
+
36
+
37
+ class APIKeyDetailPermission(StrEnum):
38
+ """
39
+ Permission level for this key
40
+ * `read` - Read Only
41
+ * `write` - Read & Write
42
+ * `admin` - Admin
43
+ """
44
+
45
+ READ = "read"
46
+ WRITE = "write"
47
+ ADMIN = "admin"
48
+
49
+
50
+
51
+ class APIKeyListPermission(StrEnum):
52
+ """
53
+ Permission level for this key
54
+ * `read` - Read Only
55
+ * `write` - Read & Write
56
+ * `admin` - Admin
57
+ """
58
+
59
+ READ = "read"
60
+ WRITE = "write"
61
+ ADMIN = "admin"
62
+
63
+
64
+
@@ -0,0 +1,7 @@
1
+ # Auto-generated by DjangoCFG - see CLAUDE.md
2
+ from .client import KeysKeysAPI
3
+ from .models import *
4
+
5
+ __all__ = [
6
+ "KeysKeysAPI",
7
+ ]
@@ -0,0 +1,150 @@
1
+ from __future__ import annotations
2
+
3
+ import httpx
4
+
5
+ from .models import *
6
+
7
+
8
+ class KeysKeysAPI:
9
+ """API endpoints for Keys."""
10
+
11
+ def __init__(self, client: httpx.AsyncClient):
12
+ """Initialize sub-client with shared httpx client."""
13
+ self._client = client
14
+
15
+ async def list(self, page: int | None = None, page_size: int | None = None) -> list[PaginatedAPIKeyListList]:
16
+ """
17
+ ViewSet for API key management. Endpoints: - GET /api/keys/ - List
18
+ user's API keys - POST /api/keys/ - Create a new API key - GET
19
+ /api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
20
+ Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
21
+ /api/keys/{id}/reactivate/ - Reactivate API key
22
+ """
23
+ url = "/api/keys/"
24
+ response = await self._client.get(url, params={"page": page if page is not None else None, "page_size": page_size if page_size is not None else None})
25
+ if not response.is_success:
26
+ try:
27
+ error_body = response.json()
28
+ except Exception:
29
+ error_body = response.text
30
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
31
+ return PaginatedAPIKeyListList.model_validate(response.json())
32
+
33
+
34
+ async def create(self, data: APIKeyCreateRequest) -> APIKeyCreate:
35
+ """
36
+ Create a new API key.
37
+ """
38
+ url = "/api/keys/"
39
+ response = await self._client.post(url, json=data.model_dump(exclude_unset=True))
40
+ if not response.is_success:
41
+ try:
42
+ error_body = response.json()
43
+ except Exception:
44
+ error_body = response.text
45
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
46
+ return APIKeyCreate.model_validate(response.json())
47
+
48
+
49
+ async def retrieve(self, id: str) -> APIKeyDetail:
50
+ """
51
+ ViewSet for API key management. Endpoints: - GET /api/keys/ - List
52
+ user's API keys - POST /api/keys/ - Create a new API key - GET
53
+ /api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
54
+ Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
55
+ /api/keys/{id}/reactivate/ - Reactivate API key
56
+ """
57
+ url = f"/api/keys/{id}/"
58
+ response = await self._client.get(url)
59
+ if not response.is_success:
60
+ try:
61
+ error_body = response.json()
62
+ except Exception:
63
+ error_body = response.text
64
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
65
+ return APIKeyDetail.model_validate(response.json())
66
+
67
+
68
+ async def update(self, id: str) -> APIKeyList:
69
+ """
70
+ ViewSet for API key management. Endpoints: - GET /api/keys/ - List
71
+ user's API keys - POST /api/keys/ - Create a new API key - GET
72
+ /api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
73
+ Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
74
+ /api/keys/{id}/reactivate/ - Reactivate API key
75
+ """
76
+ url = f"/api/keys/{id}/"
77
+ response = await self._client.put(url)
78
+ if not response.is_success:
79
+ try:
80
+ error_body = response.json()
81
+ except Exception:
82
+ error_body = response.text
83
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
84
+ return APIKeyList.model_validate(response.json())
85
+
86
+
87
+ async def partial_update(self, id: str) -> APIKeyList:
88
+ """
89
+ ViewSet for API key management. Endpoints: - GET /api/keys/ - List
90
+ user's API keys - POST /api/keys/ - Create a new API key - GET
91
+ /api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
92
+ Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
93
+ /api/keys/{id}/reactivate/ - Reactivate API key
94
+ """
95
+ url = f"/api/keys/{id}/"
96
+ response = await self._client.patch(url)
97
+ if not response.is_success:
98
+ try:
99
+ error_body = response.json()
100
+ except Exception:
101
+ error_body = response.text
102
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
103
+ return APIKeyList.model_validate(response.json())
104
+
105
+
106
+ async def destroy(self, id: str) -> None:
107
+ """
108
+ Deactivate an API key (soft delete).
109
+ """
110
+ url = f"/api/keys/{id}/"
111
+ response = await self._client.delete(url)
112
+ if not response.is_success:
113
+ try:
114
+ error_body = response.json()
115
+ except Exception:
116
+ error_body = response.text
117
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
118
+ return None
119
+
120
+
121
+ async def reactivate_create(self, id: str) -> APIKeyList:
122
+ """
123
+ Reactivate a deactivated API key.
124
+ """
125
+ url = f"/api/keys/{id}/reactivate/"
126
+ response = await self._client.post(url)
127
+ if not response.is_success:
128
+ try:
129
+ error_body = response.json()
130
+ except Exception:
131
+ error_body = response.text
132
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
133
+ return APIKeyList.model_validate(response.json())
134
+
135
+
136
+ async def rotate_create(self, id: str) -> APIKeyList:
137
+ """
138
+ Rotate an API key (generate new key).
139
+ """
140
+ url = f"/api/keys/{id}/rotate/"
141
+ response = await self._client.post(url)
142
+ if not response.is_success:
143
+ try:
144
+ error_body = response.json()
145
+ except Exception:
146
+ error_body = response.text
147
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
148
+ return APIKeyList.model_validate(response.json())
149
+
150
+
@@ -0,0 +1,152 @@
1
+ # Auto-generated by DjangoCFG - see CLAUDE.md
2
+ from __future__ import annotations
3
+
4
+ from datetime import datetime
5
+ from typing import Any
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field
8
+
9
+ from ..enums import APIKeyCreatePermission, APIKeyCreateRequestPermission, APIKeyDetailPermission, APIKeyListPermission
10
+
11
+
12
+ class APIKeyList(BaseModel):
13
+ """
14
+ Serializer for listing API keys (no sensitive data).
15
+
16
+ Response model (includes read-only fields).
17
+ """
18
+
19
+ model_config = ConfigDict(
20
+ validate_assignment=True,
21
+ extra="allow",
22
+ frozen=False,
23
+ )
24
+
25
+ id: Any = ...
26
+ name: Any = Field(description='Human-readable name for the key')
27
+ key_prefix: Any = Field(description="Visible prefix for identification (e.g., 'sk_live_abc...')")
28
+ permission: APIKeyListPermission = Field(description='Permission level for this key\n\n* `read` - Read Only\n* `write` - Read & Write\n* `admin` - Admin')
29
+ is_active: bool = Field(description='Whether this key can be used')
30
+ is_expired: bool = ...
31
+ is_valid: bool = ...
32
+ is_quota_exceeded: bool = ...
33
+ rate_limit_rpm: int | None = Field(None, description='Max requests per minute (null = unlimited)')
34
+ rate_limit_rpd: int | None = Field(None, description='Max requests per day (null = unlimited)')
35
+ quota_monthly_usd: Any | None = Field(None, description='Monthly spending limit in USD (null = unlimited)', pattern='^-?\\d{0,8}(?:\\.\\d{0,2})?$')
36
+ quota_used_usd: Any = Field(description='Amount spent this month in USD', pattern='^-?\\d{0,4}(?:\\.\\d{0,6})?$')
37
+ last_used_at: Any | None = Field(None, description='Last time this key was used')
38
+ total_requests: int = Field(description='Total number of requests made with this key')
39
+ total_cost_usd: Any = Field(description='Total cost of all requests in USD', pattern='^-?\\d{0,6}(?:\\.\\d{0,6})?$')
40
+ expires_at: Any | None = Field(None, description='When this key expires (null = never)')
41
+ created_at: Any = ...
42
+ updated_at: Any = ...
43
+
44
+
45
+
46
+ class PaginatedAPIKeyListList(BaseModel):
47
+ """
48
+
49
+ Response model (includes read-only fields).
50
+ """
51
+
52
+ model_config = ConfigDict(
53
+ validate_assignment=True,
54
+ extra="allow",
55
+ frozen=False,
56
+ )
57
+
58
+ count: int = Field(description='Total number of items across all pages')
59
+ page: int = Field(description='Current page number (1-based)')
60
+ pages: int = Field(description='Total number of pages')
61
+ page_size: int = Field(description='Number of items per page')
62
+ has_next: bool = Field(description='Whether there is a next page')
63
+ has_previous: bool = Field(description='Whether there is a previous page')
64
+ next_page: int | None = Field(None, description='Next page number (null if no next page)')
65
+ previous_page: int | None = Field(None, description='Previous page number (null if no previous page)')
66
+ results: list[APIKeyList] = Field(description='Array of items for current page')
67
+
68
+
69
+
70
+ class APIKeyCreateRequest(BaseModel):
71
+ """
72
+ Serializer for creating new API keys.
73
+
74
+ Request model (no read-only fields).
75
+ """
76
+
77
+ model_config = ConfigDict(
78
+ validate_assignment=True,
79
+ extra="allow",
80
+ frozen=False,
81
+ )
82
+
83
+ name: str = Field(min_length=1, max_length=100)
84
+ permission: APIKeyCreateRequestPermission | None = Field(None, description='* `read` - Read Only\n* `write` - Read & Write\n* `admin` - Admin')
85
+ rate_limit_rpm: int | None = Field(None, ge=1)
86
+ rate_limit_rpd: int | None = Field(None, ge=1)
87
+ quota_monthly_usd: str | None = Field(None, pattern='^-?\\d{0,8}(?:\\.\\d{0,2})?$')
88
+ expires_at: str | None = None
89
+ metadata: dict[str, Any] | None = None
90
+ is_test: bool | None = None
91
+
92
+
93
+
94
+ class APIKeyCreate(BaseModel):
95
+ """
96
+ Serializer for creating new API keys.
97
+
98
+ Response model (includes read-only fields).
99
+ """
100
+
101
+ model_config = ConfigDict(
102
+ validate_assignment=True,
103
+ extra="allow",
104
+ frozen=False,
105
+ )
106
+
107
+ name: str = Field(max_length=100)
108
+ permission: APIKeyCreatePermission | None = Field(None, description='* `read` - Read Only\n* `write` - Read & Write\n* `admin` - Admin')
109
+ rate_limit_rpm: int | None = Field(None, ge=1)
110
+ rate_limit_rpd: int | None = Field(None, ge=1)
111
+ quota_monthly_usd: str | None = Field(None, pattern='^-?\\d{0,8}(?:\\.\\d{0,2})?$')
112
+ expires_at: str | None = None
113
+ metadata: dict[str, Any] | None = None
114
+ is_test: bool | None = None
115
+
116
+
117
+
118
+ class APIKeyDetail(BaseModel):
119
+ """
120
+ Serializer for API key details.
121
+
122
+ Response model (includes read-only fields).
123
+ """
124
+
125
+ model_config = ConfigDict(
126
+ validate_assignment=True,
127
+ extra="allow",
128
+ frozen=False,
129
+ )
130
+
131
+ id: Any = ...
132
+ name: Any = Field(description='Human-readable name for the key')
133
+ key_prefix: Any = Field(description="Visible prefix for identification (e.g., 'sk_live_abc...')")
134
+ permission: APIKeyDetailPermission = Field(description='Permission level for this key\n\n* `read` - Read Only\n* `write` - Read & Write\n* `admin` - Admin')
135
+ is_active: bool = Field(description='Whether this key can be used')
136
+ is_expired: bool = ...
137
+ is_valid: bool = ...
138
+ is_quota_exceeded: bool = ...
139
+ rate_limit_rpm: int | None = Field(None, description='Max requests per minute (null = unlimited)')
140
+ rate_limit_rpd: int | None = Field(None, description='Max requests per day (null = unlimited)')
141
+ quota_monthly_usd: Any | None = Field(None, description='Monthly spending limit in USD (null = unlimited)', pattern='^-?\\d{0,8}(?:\\.\\d{0,2})?$')
142
+ quota_used_usd: Any = Field(description='Amount spent this month in USD', pattern='^-?\\d{0,4}(?:\\.\\d{0,6})?$')
143
+ last_used_at: Any | None = Field(None, description='Last time this key was used')
144
+ total_requests: int = Field(description='Total number of requests made with this key')
145
+ total_cost_usd: Any = Field(description='Total cost of all requests in USD', pattern='^-?\\d{0,6}(?:\\.\\d{0,6})?$')
146
+ expires_at: Any | None = Field(None, description='When this key expires (null = never)')
147
+ created_at: Any = ...
148
+ updated_at: Any = ...
149
+ metadata: dict[str, Any] | None = Field(None, description='Additional metadata (e.g., allowed IPs, allowed models)')
150
+
151
+
152
+