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,133 @@
1
+ from __future__ import annotations
2
+
3
+ import httpx
4
+
5
+ from .models import *
6
+
7
+
8
+ class CdnCdnAPI:
9
+ """API endpoints for Cdn."""
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[PaginatedCDNFileListList]:
16
+ """
17
+ ViewSet for CDN file management. Endpoints: - GET /api/cdn/ - List
18
+ user's files - POST /api/cdn/ - Upload a new file - GET /api/cdn/{uuid}/
19
+ - Get file details - DELETE /api/cdn/{uuid}/ - Delete file - GET
20
+ /api/cdn/stats/ - Get storage statistics
21
+ """
22
+ url = "/api/cdn/"
23
+ 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})
24
+ if not response.is_success:
25
+ try:
26
+ error_body = response.json()
27
+ except Exception:
28
+ error_body = response.text
29
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
30
+ return PaginatedCDNFileListList.model_validate(response.json())
31
+
32
+
33
+ async def create(self, data: CDNFileUploadRequest) -> CDNFileUpload:
34
+ """
35
+ Upload a new file or download from URL.
36
+ """
37
+ url = "/api/cdn/"
38
+ response = await self._client.post(url, json=data.model_dump(exclude_unset=True))
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 CDNFileUpload.model_validate(response.json())
46
+
47
+
48
+ async def retrieve(self, uuid: str) -> CDNFileDetail:
49
+ """
50
+ ViewSet for CDN file management. Endpoints: - GET /api/cdn/ - List
51
+ user's files - POST /api/cdn/ - Upload a new file - GET /api/cdn/{uuid}/
52
+ - Get file details - DELETE /api/cdn/{uuid}/ - Delete file - GET
53
+ /api/cdn/stats/ - Get storage statistics
54
+ """
55
+ url = f"/api/cdn/{uuid}/"
56
+ response = await self._client.get(url)
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 CDNFileDetail.model_validate(response.json())
64
+
65
+
66
+ async def update(self, uuid: str) -> CDNFileList:
67
+ """
68
+ ViewSet for CDN file management. Endpoints: - GET /api/cdn/ - List
69
+ user's files - POST /api/cdn/ - Upload a new file - GET /api/cdn/{uuid}/
70
+ - Get file details - DELETE /api/cdn/{uuid}/ - Delete file - GET
71
+ /api/cdn/stats/ - Get storage statistics
72
+ """
73
+ url = f"/api/cdn/{uuid}/"
74
+ response = await self._client.put(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 CDNFileList.model_validate(response.json())
82
+
83
+
84
+ async def partial_update(self, uuid: str) -> CDNFileList:
85
+ """
86
+ ViewSet for CDN file management. Endpoints: - GET /api/cdn/ - List
87
+ user's files - POST /api/cdn/ - Upload a new file - GET /api/cdn/{uuid}/
88
+ - Get file details - DELETE /api/cdn/{uuid}/ - Delete file - GET
89
+ /api/cdn/stats/ - Get storage statistics
90
+ """
91
+ url = f"/api/cdn/{uuid}/"
92
+ response = await self._client.patch(url)
93
+ if not response.is_success:
94
+ try:
95
+ error_body = response.json()
96
+ except Exception:
97
+ error_body = response.text
98
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
99
+ return CDNFileList.model_validate(response.json())
100
+
101
+
102
+ async def destroy(self, uuid: str) -> None:
103
+ """
104
+ Delete a file.
105
+ """
106
+ url = f"/api/cdn/{uuid}/"
107
+ response = await self._client.delete(url)
108
+ if not response.is_success:
109
+ try:
110
+ error_body = response.json()
111
+ except Exception:
112
+ error_body = response.text
113
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
114
+ return None
115
+
116
+
117
+ async def stats_retrieve(self) -> CDNStats:
118
+ """
119
+ Get storage statistics
120
+
121
+ Get CDN storage statistics for the current user.
122
+ """
123
+ url = "/api/cdn/stats/"
124
+ response = await self._client.get(url)
125
+ if not response.is_success:
126
+ try:
127
+ error_body = response.json()
128
+ except Exception:
129
+ error_body = response.text
130
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
131
+ return CDNStats.model_validate(response.json())
132
+
133
+
@@ -0,0 +1,163 @@
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
+
10
+ class CDNFileList(BaseModel):
11
+ """
12
+ Serializer for listing CDN files.
13
+
14
+ Response model (includes read-only fields).
15
+ """
16
+
17
+ model_config = ConfigDict(
18
+ validate_assignment=True,
19
+ extra="allow",
20
+ frozen=False,
21
+ )
22
+
23
+ id: Any = ...
24
+ uuid: Any = Field(description='Unique file identifier for URLs')
25
+ short_code: Any = Field(description='Short code for URLs')
26
+ filename: Any = Field(description='Original filename')
27
+ content_type: Any = Field(description='MIME type of the file')
28
+ size_bytes: int = Field(description='File size in bytes')
29
+ is_public: bool = Field(description='Whether file is publicly accessible')
30
+ is_expired: bool = ...
31
+ is_image: bool = ...
32
+ access_count: int = Field(description='Number of times the file was accessed')
33
+ expires_at: Any | None = Field(None, description='When the file expires (null = never)')
34
+ url: Any = ...
35
+ short_url: Any = ...
36
+ created_at: Any = ...
37
+
38
+
39
+
40
+ class PaginatedCDNFileListList(BaseModel):
41
+ """
42
+
43
+ Response model (includes read-only fields).
44
+ """
45
+
46
+ model_config = ConfigDict(
47
+ validate_assignment=True,
48
+ extra="allow",
49
+ frozen=False,
50
+ )
51
+
52
+ count: int = Field(description='Total number of items across all pages')
53
+ page: int = Field(description='Current page number (1-based)')
54
+ pages: int = Field(description='Total number of pages')
55
+ page_size: int = Field(description='Number of items per page')
56
+ has_next: bool = Field(description='Whether there is a next page')
57
+ has_previous: bool = Field(description='Whether there is a previous page')
58
+ next_page: int | None = Field(None, description='Next page number (null if no next page)')
59
+ previous_page: int | None = Field(None, description='Previous page number (null if no previous page)')
60
+ results: list[CDNFileList] = Field(description='Array of items for current page')
61
+
62
+
63
+
64
+ class CDNFileUploadRequest(BaseModel):
65
+ """
66
+ Serializer for file upload request. Supports two modes: - file: Direct file
67
+ upload - url: Server downloads from URL
68
+
69
+ Request model (no read-only fields).
70
+ """
71
+
72
+ model_config = ConfigDict(
73
+ validate_assignment=True,
74
+ extra="allow",
75
+ frozen=False,
76
+ )
77
+
78
+ file: str | None = None
79
+ url: str | None = Field(None, description='URL to download file from (alternative to file upload)', min_length=1)
80
+ filename: str | None = Field(None, description='Filename (optional, extracted from URL if not provided)', min_length=1)
81
+ ttl: str | None = Field(None, description='TTL (e.g., 30d, 7d, 24h, 0=forever)', min_length=1)
82
+ is_public: bool | None = None
83
+ metadata: dict[str, Any] | None = None
84
+
85
+
86
+
87
+ class CDNFileUpload(BaseModel):
88
+ """
89
+ Serializer for file upload request. Supports two modes: - file: Direct file
90
+ upload - url: Server downloads from URL
91
+
92
+ Response model (includes read-only fields).
93
+ """
94
+
95
+ model_config = ConfigDict(
96
+ validate_assignment=True,
97
+ extra="allow",
98
+ frozen=False,
99
+ )
100
+
101
+ file: str | None = None
102
+ url: str | None = Field(None, description='URL to download file from (alternative to file upload)')
103
+ filename: str | None = Field(None, description='Filename (optional, extracted from URL if not provided)')
104
+ ttl: str | None = Field(None, description='TTL (e.g., 30d, 7d, 24h, 0=forever)')
105
+ is_public: bool | None = None
106
+ metadata: dict[str, Any] | None = None
107
+
108
+
109
+
110
+ class CDNFileDetail(BaseModel):
111
+ """
112
+ Serializer for CDN file details.
113
+
114
+ Response model (includes read-only fields).
115
+ """
116
+
117
+ model_config = ConfigDict(
118
+ validate_assignment=True,
119
+ extra="allow",
120
+ frozen=False,
121
+ )
122
+
123
+ id: Any = ...
124
+ uuid: Any = Field(description='Unique file identifier for URLs')
125
+ short_code: Any = Field(description='Short code for URLs')
126
+ filename: Any = Field(description='Original filename')
127
+ content_type: Any = Field(description='MIME type of the file')
128
+ size_bytes: int = Field(description='File size in bytes')
129
+ is_public: bool = Field(description='Whether file is publicly accessible')
130
+ is_expired: bool = ...
131
+ is_image: bool = ...
132
+ access_count: int = Field(description='Number of times the file was accessed')
133
+ expires_at: Any | None = Field(None, description='When the file expires (null = never)')
134
+ url: Any = ...
135
+ short_url: Any = ...
136
+ created_at: Any = ...
137
+ checksum_sha256: str = Field(description='SHA256 checksum for deduplication', max_length=64)
138
+ storage_backend: str | None = Field(None, description='Storage backend (local, s3, etc.)', max_length=50)
139
+ last_accessed_at: str | None = Field(None, description='Last time the file was accessed')
140
+ metadata: dict[str, Any] | None = Field(None, description='Additional file metadata')
141
+ updated_at: Any = ...
142
+
143
+
144
+
145
+ class CDNStats(BaseModel):
146
+ """
147
+ Serializer for CDN storage statistics.
148
+
149
+ Response model (includes read-only fields).
150
+ """
151
+
152
+ model_config = ConfigDict(
153
+ validate_assignment=True,
154
+ extra="allow",
155
+ frozen=False,
156
+ )
157
+
158
+ total_files: int = Field(description='Total number of files')
159
+ total_size_bytes: int = Field(description='Total size in bytes')
160
+ total_access_count: int = Field(description='Total access count')
161
+
162
+
163
+
@@ -0,0 +1,132 @@
1
+ from __future__ import annotations
2
+
3
+ import httpx
4
+
5
+ from .models import *
6
+
7
+
8
+ class SyncCdnCdnAPI:
9
+ """Synchronous API endpoints for Cdn."""
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, page: int | None = None, page_size: int | None = None) -> list[PaginatedCDNFileListList]:
16
+ """
17
+ ViewSet for CDN file management. Endpoints: - GET /api/cdn/ - List
18
+ user's files - POST /api/cdn/ - Upload a new file - GET /api/cdn/{uuid}/
19
+ - Get file details - DELETE /api/cdn/{uuid}/ - Delete file - GET
20
+ /api/cdn/stats/ - Get storage statistics
21
+ """
22
+ url = "/api/cdn/"
23
+ response = 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})
24
+ if not response.is_success:
25
+ try:
26
+ error_body = response.json()
27
+ except Exception:
28
+ error_body = response.text
29
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
30
+ return PaginatedCDNFileListList.model_validate(response.json())
31
+
32
+
33
+ def create(self, data: CDNFileUploadRequest) -> CDNFileUpload:
34
+ """
35
+ Upload a new file or download from URL.
36
+ """
37
+ url = "/api/cdn/"
38
+ response = self._client.post(url, json=data.model_dump(exclude_unset=True))
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 CDNFileUpload.model_validate(response.json())
46
+
47
+
48
+ def retrieve(self, uuid: str) -> CDNFileDetail:
49
+ """
50
+ ViewSet for CDN file management. Endpoints: - GET /api/cdn/ - List
51
+ user's files - POST /api/cdn/ - Upload a new file - GET /api/cdn/{uuid}/
52
+ - Get file details - DELETE /api/cdn/{uuid}/ - Delete file - GET
53
+ /api/cdn/stats/ - Get storage statistics
54
+ """
55
+ url = f"/api/cdn/{uuid}/"
56
+ response = self._client.get(url)
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 CDNFileDetail.model_validate(response.json())
64
+
65
+
66
+ def update(self, uuid: str) -> CDNFileList:
67
+ """
68
+ ViewSet for CDN file management. Endpoints: - GET /api/cdn/ - List
69
+ user's files - POST /api/cdn/ - Upload a new file - GET /api/cdn/{uuid}/
70
+ - Get file details - DELETE /api/cdn/{uuid}/ - Delete file - GET
71
+ /api/cdn/stats/ - Get storage statistics
72
+ """
73
+ url = f"/api/cdn/{uuid}/"
74
+ response = self._client.put(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 CDNFileList.model_validate(response.json())
82
+
83
+
84
+ def partial_update(self, uuid: str) -> CDNFileList:
85
+ """
86
+ ViewSet for CDN file management. Endpoints: - GET /api/cdn/ - List
87
+ user's files - POST /api/cdn/ - Upload a new file - GET /api/cdn/{uuid}/
88
+ - Get file details - DELETE /api/cdn/{uuid}/ - Delete file - GET
89
+ /api/cdn/stats/ - Get storage statistics
90
+ """
91
+ url = f"/api/cdn/{uuid}/"
92
+ response = self._client.patch(url)
93
+ if not response.is_success:
94
+ try:
95
+ error_body = response.json()
96
+ except Exception:
97
+ error_body = response.text
98
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
99
+ return CDNFileList.model_validate(response.json())
100
+
101
+
102
+ def destroy(self, uuid: str) -> None:
103
+ """
104
+ Delete a file.
105
+ """
106
+ url = f"/api/cdn/{uuid}/"
107
+ response = self._client.delete(url)
108
+ if not response.is_success:
109
+ try:
110
+ error_body = response.json()
111
+ except Exception:
112
+ error_body = response.text
113
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
114
+
115
+
116
+ def stats_retrieve(self) -> CDNStats:
117
+ """
118
+ Get storage statistics
119
+
120
+ Get CDN storage statistics for the current user.
121
+ """
122
+ url = "/api/cdn/stats/"
123
+ response = self._client.get(url)
124
+ if not response.is_success:
125
+ try:
126
+ error_body = response.json()
127
+ except Exception:
128
+ error_body = response.text
129
+ raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
130
+ return CDNStats.model_validate(response.json())
131
+
132
+
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+ import httpx
6
+
7
+ from .cdn__api__cdn import CdnCdnAPI
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.cdn_cdn = CdnCdnAPI(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()