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.
- sdkrouter/__init__.py +110 -0
- sdkrouter/_api/__init__.py +28 -0
- sdkrouter/_api/client.py +204 -0
- sdkrouter/_api/generated/__init__.py +21 -0
- sdkrouter/_api/generated/cdn/__init__.py +209 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/__init__.py +7 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/client.py +133 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/models.py +163 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/sync_client.py +132 -0
- sdkrouter/_api/generated/cdn/client.py +75 -0
- sdkrouter/_api/generated/cdn/logger.py +256 -0
- sdkrouter/_api/generated/cdn/pyproject.toml +55 -0
- sdkrouter/_api/generated/cdn/retry.py +272 -0
- sdkrouter/_api/generated/cdn/sync_client.py +58 -0
- sdkrouter/_api/generated/cleaner/__init__.py +212 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/__init__.py +7 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/client.py +83 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/models.py +117 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/sync_client.py +82 -0
- sdkrouter/_api/generated/cleaner/client.py +75 -0
- sdkrouter/_api/generated/cleaner/enums.py +55 -0
- sdkrouter/_api/generated/cleaner/logger.py +256 -0
- sdkrouter/_api/generated/cleaner/pyproject.toml +55 -0
- sdkrouter/_api/generated/cleaner/retry.py +272 -0
- sdkrouter/_api/generated/cleaner/sync_client.py +58 -0
- sdkrouter/_api/generated/keys/__init__.py +212 -0
- sdkrouter/_api/generated/keys/client.py +75 -0
- sdkrouter/_api/generated/keys/enums.py +64 -0
- sdkrouter/_api/generated/keys/keys__api__keys/__init__.py +7 -0
- sdkrouter/_api/generated/keys/keys__api__keys/client.py +150 -0
- sdkrouter/_api/generated/keys/keys__api__keys/models.py +152 -0
- sdkrouter/_api/generated/keys/keys__api__keys/sync_client.py +149 -0
- sdkrouter/_api/generated/keys/logger.py +256 -0
- sdkrouter/_api/generated/keys/pyproject.toml +55 -0
- sdkrouter/_api/generated/keys/retry.py +272 -0
- sdkrouter/_api/generated/keys/sync_client.py +58 -0
- sdkrouter/_api/generated/models/__init__.py +209 -0
- sdkrouter/_api/generated/models/client.py +75 -0
- sdkrouter/_api/generated/models/logger.py +256 -0
- sdkrouter/_api/generated/models/models__api__llm_models/__init__.py +7 -0
- sdkrouter/_api/generated/models/models__api__llm_models/client.py +99 -0
- sdkrouter/_api/generated/models/models__api__llm_models/models.py +206 -0
- sdkrouter/_api/generated/models/models__api__llm_models/sync_client.py +99 -0
- sdkrouter/_api/generated/models/pyproject.toml +55 -0
- sdkrouter/_api/generated/models/retry.py +272 -0
- sdkrouter/_api/generated/models/sync_client.py +58 -0
- sdkrouter/_api/generated/shortlinks/__init__.py +209 -0
- sdkrouter/_api/generated/shortlinks/client.py +75 -0
- sdkrouter/_api/generated/shortlinks/logger.py +256 -0
- sdkrouter/_api/generated/shortlinks/pyproject.toml +55 -0
- sdkrouter/_api/generated/shortlinks/retry.py +272 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/__init__.py +7 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/client.py +137 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/models.py +153 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/sync_client.py +136 -0
- sdkrouter/_api/generated/shortlinks/sync_client.py +58 -0
- sdkrouter/_api/generated/vision/__init__.py +212 -0
- sdkrouter/_api/generated/vision/client.py +75 -0
- sdkrouter/_api/generated/vision/enums.py +40 -0
- sdkrouter/_api/generated/vision/logger.py +256 -0
- sdkrouter/_api/generated/vision/pyproject.toml +55 -0
- sdkrouter/_api/generated/vision/retry.py +272 -0
- sdkrouter/_api/generated/vision/sync_client.py +58 -0
- sdkrouter/_api/generated/vision/vision__api__vision/__init__.py +7 -0
- sdkrouter/_api/generated/vision/vision__api__vision/client.py +65 -0
- sdkrouter/_api/generated/vision/vision__api__vision/models.py +138 -0
- sdkrouter/_api/generated/vision/vision__api__vision/sync_client.py +65 -0
- sdkrouter/_client.py +432 -0
- sdkrouter/_config.py +74 -0
- sdkrouter/_constants.py +21 -0
- sdkrouter/_internal/__init__.py +1 -0
- sdkrouter/_types/__init__.py +30 -0
- sdkrouter/_types/cdn.py +27 -0
- sdkrouter/_types/models.py +26 -0
- sdkrouter/_types/ocr.py +24 -0
- sdkrouter/_types/parsed.py +101 -0
- sdkrouter/_types/shortlinks.py +27 -0
- sdkrouter/_types/vision.py +29 -0
- sdkrouter/_version.py +3 -0
- sdkrouter/helpers/__init__.py +13 -0
- sdkrouter/helpers/formatting.py +15 -0
- sdkrouter/helpers/html.py +100 -0
- sdkrouter/helpers/json_cleaner.py +53 -0
- sdkrouter/tools/__init__.py +129 -0
- sdkrouter/tools/cdn.py +285 -0
- sdkrouter/tools/cleaner.py +186 -0
- sdkrouter/tools/keys.py +215 -0
- sdkrouter/tools/models.py +196 -0
- sdkrouter/tools/shortlinks.py +165 -0
- sdkrouter/tools/vision.py +173 -0
- sdkrouter/utils/__init__.py +27 -0
- sdkrouter/utils/parsing.py +109 -0
- sdkrouter/utils/tokens.py +375 -0
- sdkrouter-0.1.1.dist-info/METADATA +411 -0
- sdkrouter-0.1.1.dist-info/RECORD +96 -0
- 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 .cleaner__api__cleaner import CleanerCleanerAPI
|
|
37
|
+
from . import enums
|
|
38
|
+
# NOTE: Individual enum imports commented out due to invalid Python syntax with dotted names
|
|
39
|
+
# from .enums import CleanRequestRequest.output_format, CleaningRequestDetail.status, CleaningRequestList.status
|
|
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 cleaner_cleaner(self) -> CleanerCleanerAPI:
|
|
116
|
+
"""Access cleaner endpoints."""
|
|
117
|
+
return self._client.cleaner_cleaner
|
|
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,83 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from .models import *
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CleanerCleanerAPI:
|
|
9
|
+
"""API endpoints for Cleaner."""
|
|
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) -> None:
|
|
16
|
+
"""
|
|
17
|
+
List cleaning requests
|
|
18
|
+
|
|
19
|
+
List user's cleaning requests.
|
|
20
|
+
"""
|
|
21
|
+
url = "/api/cleaner/"
|
|
22
|
+
response = await self._client.get(url)
|
|
23
|
+
if not response.is_success:
|
|
24
|
+
try:
|
|
25
|
+
error_body = response.json()
|
|
26
|
+
except Exception:
|
|
27
|
+
error_body = response.text
|
|
28
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def retrieve(self, id: str) -> CleaningRequestDetail:
|
|
33
|
+
"""
|
|
34
|
+
Get cleaning request details
|
|
35
|
+
|
|
36
|
+
Get cleaning request details.
|
|
37
|
+
"""
|
|
38
|
+
url = f"/api/cleaner/{id}/"
|
|
39
|
+
response = await self._client.get(url)
|
|
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 CleaningRequestDetail.model_validate(response.json())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def clean_create(self, data: CleanRequestRequest) -> CleanResponse:
|
|
50
|
+
"""
|
|
51
|
+
Clean HTML file
|
|
52
|
+
|
|
53
|
+
Clean HTML file. Accepts multipart/form-data with HTML file. Returns
|
|
54
|
+
cleaned HTML with statistics.
|
|
55
|
+
"""
|
|
56
|
+
url = "/api/cleaner/clean/"
|
|
57
|
+
response = await self._client.post(url, json=data.model_dump(exclude_unset=True))
|
|
58
|
+
if not response.is_success:
|
|
59
|
+
try:
|
|
60
|
+
error_body = response.json()
|
|
61
|
+
except Exception:
|
|
62
|
+
error_body = response.text
|
|
63
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
64
|
+
return CleanResponse.model_validate(response.json())
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def stats_retrieve(self) -> CleaningStats:
|
|
68
|
+
"""
|
|
69
|
+
Get cleaning statistics
|
|
70
|
+
|
|
71
|
+
Get cleaning statistics.
|
|
72
|
+
"""
|
|
73
|
+
url = "/api/cleaner/stats/"
|
|
74
|
+
response = await self._client.get(url)
|
|
75
|
+
if not response.is_success:
|
|
76
|
+
try:
|
|
77
|
+
error_body = response.json()
|
|
78
|
+
except Exception:
|
|
79
|
+
error_body = response.text
|
|
80
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
81
|
+
return CleaningStats.model_validate(response.json())
|
|
82
|
+
|
|
83
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
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 CleanRequestRequestOutputFormat, CleaningRequestDetailStatus
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CleaningRequestDetail(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
Detail serializer for cleaning request.
|
|
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
|
+
uuid: Any = Field(description='Unique request identifier')
|
|
26
|
+
original_filename: Any = Field(description='Original filename')
|
|
27
|
+
original_size_bytes: int = Field(description='Original HTML size in bytes')
|
|
28
|
+
cleaned_html: Any = Field(description='Cleaned HTML content')
|
|
29
|
+
cleaned_size_bytes: int = Field(description='Cleaned HTML size in bytes')
|
|
30
|
+
output_format: Any = Field(description='Output format: html, markdown, aom, xtree')
|
|
31
|
+
config: dict[str, Any] = Field(description='CleanerConfig used for this request')
|
|
32
|
+
stats: dict[str, Any] = Field(description='Cleaning statistics (elements removed, etc.)')
|
|
33
|
+
status: CleaningRequestDetailStatus = Field(description='* `pending` - Pending\n* `processing` - Processing\n* `success` - Success\n* `error` - Error')
|
|
34
|
+
error_message: Any = Field(description='Error message if request failed')
|
|
35
|
+
duration_ms: int | None = Field(None, description='Processing duration in milliseconds')
|
|
36
|
+
compression_ratio: float = ...
|
|
37
|
+
size_reduction_percent: float = ...
|
|
38
|
+
created_at: Any = ...
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class CleanRequestRequest(BaseModel):
|
|
43
|
+
"""
|
|
44
|
+
Request serializer for HTML cleaning (multipart/form-data).
|
|
45
|
+
|
|
46
|
+
Request model (no read-only fields).
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
model_config = ConfigDict(
|
|
50
|
+
validate_assignment=True,
|
|
51
|
+
extra="allow",
|
|
52
|
+
frozen=False,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
file: str = Field(description='HTML file to clean')
|
|
56
|
+
output_format: CleanRequestRequestOutputFormat | None = Field(None, description='* `html` - html\n* `markdown` - markdown\n* `aom` - aom\n* `xtree` - xtree')
|
|
57
|
+
max_tokens: int | None = Field(None, ge=100, le=100000)
|
|
58
|
+
remove_scripts: bool | None = None
|
|
59
|
+
remove_styles: bool | None = None
|
|
60
|
+
remove_comments: bool | None = None
|
|
61
|
+
remove_hidden: bool | None = None
|
|
62
|
+
filter_classes: bool | None = None
|
|
63
|
+
class_threshold: float | None = Field(None, ge=0.0, le=1.0)
|
|
64
|
+
try_hydration: bool | None = None
|
|
65
|
+
preserve_selectors: list[str] | None = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CleanResponse(BaseModel):
|
|
70
|
+
"""
|
|
71
|
+
Response serializer for cleaned HTML.
|
|
72
|
+
|
|
73
|
+
Response model (includes read-only fields).
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
model_config = ConfigDict(
|
|
77
|
+
validate_assignment=True,
|
|
78
|
+
extra="allow",
|
|
79
|
+
frozen=False,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
request_uuid: str = ...
|
|
83
|
+
cleaned_html: str = ...
|
|
84
|
+
original_size: int = ...
|
|
85
|
+
cleaned_size: int = ...
|
|
86
|
+
compression_ratio: float = ...
|
|
87
|
+
size_reduction_percent: float = ...
|
|
88
|
+
duration_ms: int = ...
|
|
89
|
+
output_format: str = ...
|
|
90
|
+
stats: dict[str, Any] = ...
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class CleaningStats(BaseModel):
|
|
95
|
+
"""
|
|
96
|
+
Statistics serializer.
|
|
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
|
+
total_requests: int = ...
|
|
108
|
+
successful_requests: int = ...
|
|
109
|
+
failed_requests: int = ...
|
|
110
|
+
total_original_bytes: int | None = None
|
|
111
|
+
total_cleaned_bytes: int | None = None
|
|
112
|
+
avg_duration_ms: float | None = None
|
|
113
|
+
overall_compression_ratio: float | None = None
|
|
114
|
+
overall_size_reduction_percent: float | None = None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from .models import *
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SyncCleanerCleanerAPI:
|
|
9
|
+
"""Synchronous API endpoints for Cleaner."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, client: httpx.Client):
|
|
12
|
+
"""Initialize sync sub-client with shared httpx client."""
|
|
13
|
+
self._client = client
|
|
14
|
+
|
|
15
|
+
def list(self) -> None:
|
|
16
|
+
"""
|
|
17
|
+
List cleaning requests
|
|
18
|
+
|
|
19
|
+
List user's cleaning requests.
|
|
20
|
+
"""
|
|
21
|
+
url = "/api/cleaner/"
|
|
22
|
+
response = self._client.get(url)
|
|
23
|
+
if not response.is_success:
|
|
24
|
+
try:
|
|
25
|
+
error_body = response.json()
|
|
26
|
+
except Exception:
|
|
27
|
+
error_body = response.text
|
|
28
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def retrieve(self, id: str) -> CleaningRequestDetail:
|
|
32
|
+
"""
|
|
33
|
+
Get cleaning request details
|
|
34
|
+
|
|
35
|
+
Get cleaning request details.
|
|
36
|
+
"""
|
|
37
|
+
url = f"/api/cleaner/{id}/"
|
|
38
|
+
response = self._client.get(url)
|
|
39
|
+
if not response.is_success:
|
|
40
|
+
try:
|
|
41
|
+
error_body = response.json()
|
|
42
|
+
except Exception:
|
|
43
|
+
error_body = response.text
|
|
44
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
45
|
+
return CleaningRequestDetail.model_validate(response.json())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def clean_create(self, data: CleanRequestRequest) -> CleanResponse:
|
|
49
|
+
"""
|
|
50
|
+
Clean HTML file
|
|
51
|
+
|
|
52
|
+
Clean HTML file. Accepts multipart/form-data with HTML file. Returns
|
|
53
|
+
cleaned HTML with statistics.
|
|
54
|
+
"""
|
|
55
|
+
url = "/api/cleaner/clean/"
|
|
56
|
+
response = self._client.post(url, json=data.model_dump(exclude_unset=True))
|
|
57
|
+
if not response.is_success:
|
|
58
|
+
try:
|
|
59
|
+
error_body = response.json()
|
|
60
|
+
except Exception:
|
|
61
|
+
error_body = response.text
|
|
62
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
63
|
+
return CleanResponse.model_validate(response.json())
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def stats_retrieve(self) -> CleaningStats:
|
|
67
|
+
"""
|
|
68
|
+
Get cleaning statistics
|
|
69
|
+
|
|
70
|
+
Get cleaning statistics.
|
|
71
|
+
"""
|
|
72
|
+
url = "/api/cleaner/stats/"
|
|
73
|
+
response = self._client.get(url)
|
|
74
|
+
if not response.is_success:
|
|
75
|
+
try:
|
|
76
|
+
error_body = response.json()
|
|
77
|
+
except Exception:
|
|
78
|
+
error_body = response.text
|
|
79
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
80
|
+
return CleaningStats.model_validate(response.json())
|
|
81
|
+
|
|
82
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from .cleaner__api__cleaner import CleanerCleanerAPI
|
|
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.cleaner_cleaner = CleanerCleanerAPI(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,55 @@
|
|
|
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 CleanRequestRequestOutputFormat(StrEnum):
|
|
12
|
+
"""
|
|
13
|
+
* `html` - html
|
|
14
|
+
* `markdown` - markdown
|
|
15
|
+
* `aom` - aom
|
|
16
|
+
* `xtree` - xtree
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
HTML = "html"
|
|
20
|
+
MARKDOWN = "markdown"
|
|
21
|
+
AOM = "aom"
|
|
22
|
+
XTREE = "xtree"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CleaningRequestDetailStatus(StrEnum):
|
|
27
|
+
"""
|
|
28
|
+
* `pending` - Pending
|
|
29
|
+
* `processing` - Processing
|
|
30
|
+
* `success` - Success
|
|
31
|
+
* `error` - Error
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
PENDING = "pending"
|
|
35
|
+
PROCESSING = "processing"
|
|
36
|
+
SUCCESS = "success"
|
|
37
|
+
ERROR = "error"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CleaningRequestListStatus(StrEnum):
|
|
42
|
+
"""
|
|
43
|
+
* `pending` - Pending
|
|
44
|
+
* `processing` - Processing
|
|
45
|
+
* `success` - Success
|
|
46
|
+
* `error` - Error
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
PENDING = "pending"
|
|
50
|
+
PROCESSING = "processing"
|
|
51
|
+
SUCCESS = "success"
|
|
52
|
+
ERROR = "error"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|