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,196 @@
1
+ """LLM Models tool using generated API client."""
2
+
3
+ from typing import Optional
4
+
5
+ from .._config import SDKConfig
6
+ from .._api.client import (
7
+ BaseResource,
8
+ AsyncBaseResource,
9
+ SyncModelsLlmModelsAPI,
10
+ ModelsLlmModelsAPI,
11
+ )
12
+ from .._api.generated.models.models__api__llm_models.models import (
13
+ LLMModelList,
14
+ LLMModelDetail,
15
+ PaginatedLLMModelListList,
16
+ ProvidersResponse,
17
+ StatsResponse,
18
+ CostCalculationRequestRequest,
19
+ CostCalculationResponse,
20
+ )
21
+
22
+
23
+ class ModelsResource(BaseResource):
24
+ """LLM Models tool (sync).
25
+
26
+ Uses generated SyncModelsLlmModelsAPI client.
27
+
28
+ Example:
29
+ ```python
30
+ from sdkrouter import SDKRouter
31
+
32
+ client = SDKRouter(api_key="your-api-key")
33
+
34
+ # List all models with pagination
35
+ models = client.models.list(page=1, page_size=20)
36
+ for model in models.results:
37
+ print(f"{model.model_id}: {model.name}")
38
+
39
+ # Get model details
40
+ model = client.models.get("openai/gpt-4o")
41
+ print(f"Context: {model.context_length} tokens")
42
+
43
+ # Calculate cost
44
+ cost = client.models.calculate_cost(
45
+ "openai/gpt-4o",
46
+ input_tokens=1000,
47
+ output_tokens=500
48
+ )
49
+ print(f"Cost: ${cost.total_cost_usd}")
50
+
51
+ # Get providers list
52
+ providers = client.models.providers()
53
+ for p in providers.providers:
54
+ print(f"{p.name}: {p.model_count} models")
55
+
56
+ # Get statistics
57
+ stats = client.models.stats()
58
+ print(f"Total models: {stats.total_models}")
59
+ ```
60
+ """
61
+
62
+ def __init__(self, config: SDKConfig):
63
+ super().__init__(config)
64
+ self._api = SyncModelsLlmModelsAPI(self._http_client)
65
+
66
+ def list(
67
+ self,
68
+ *,
69
+ page: int = 1,
70
+ page_size: int = 20,
71
+ ) -> PaginatedLLMModelListList:
72
+ """
73
+ List available LLM models with pagination.
74
+
75
+ Args:
76
+ page: Page number (1-based)
77
+ page_size: Number of results per page
78
+
79
+ Returns:
80
+ Paginated list of LLM models.
81
+ """
82
+ return self._api.list(page=page, page_size=page_size)
83
+
84
+ def get(self, model_id: str) -> LLMModelDetail:
85
+ """
86
+ Get detailed information about a specific model.
87
+
88
+ Args:
89
+ model_id: Model identifier (e.g., 'openai/gpt-4o')
90
+
91
+ Returns:
92
+ Model details including pricing, capabilities, etc.
93
+ """
94
+ return self._api.retrieve(model_id)
95
+
96
+ def calculate_cost(
97
+ self,
98
+ model_id: str,
99
+ *,
100
+ input_tokens: int,
101
+ output_tokens: int,
102
+ ) -> CostCalculationResponse:
103
+ """
104
+ Calculate cost for a request.
105
+
106
+ Args:
107
+ model_id: Model identifier
108
+ input_tokens: Number of input tokens
109
+ output_tokens: Number of output tokens
110
+
111
+ Returns:
112
+ Cost calculation result.
113
+ """
114
+ request = CostCalculationRequestRequest(
115
+ input_tokens=input_tokens,
116
+ output_tokens=output_tokens,
117
+ )
118
+ return self._api.calculate_cost_create(model_id, request)
119
+
120
+ def providers(self) -> ProvidersResponse:
121
+ """
122
+ Get list of available providers.
123
+
124
+ Returns:
125
+ List of providers with model counts.
126
+ """
127
+ return self._api.providers_retrieve()
128
+
129
+ def stats(self) -> StatsResponse:
130
+ """
131
+ Get model statistics.
132
+
133
+ Returns:
134
+ Statistics about available models.
135
+ """
136
+ return self._api.stats_retrieve()
137
+
138
+
139
+ class AsyncModelsResource(AsyncBaseResource):
140
+ """LLM Models tool (async).
141
+
142
+ Uses generated ModelsLlmModelsAPI client.
143
+ """
144
+
145
+ def __init__(self, config: SDKConfig):
146
+ super().__init__(config)
147
+ self._api = ModelsLlmModelsAPI(self._http_client)
148
+
149
+ async def list(
150
+ self,
151
+ *,
152
+ page: int = 1,
153
+ page_size: int = 20,
154
+ ) -> PaginatedLLMModelListList:
155
+ """List available LLM models with pagination."""
156
+ return await self._api.list(page=page, page_size=page_size)
157
+
158
+ async def get(self, model_id: str) -> LLMModelDetail:
159
+ """Get detailed information about a specific model."""
160
+ return await self._api.retrieve(model_id)
161
+
162
+ async def calculate_cost(
163
+ self,
164
+ model_id: str,
165
+ *,
166
+ input_tokens: int,
167
+ output_tokens: int,
168
+ ) -> CostCalculationResponse:
169
+ """Calculate cost for a request."""
170
+ request = CostCalculationRequestRequest(
171
+ input_tokens=input_tokens,
172
+ output_tokens=output_tokens,
173
+ )
174
+ return await self._api.calculate_cost_create(model_id, request)
175
+
176
+ async def providers(self) -> ProvidersResponse:
177
+ """Get list of available providers."""
178
+ return await self._api.providers_retrieve()
179
+
180
+ async def stats(self) -> StatsResponse:
181
+ """Get model statistics."""
182
+ return await self._api.stats_retrieve()
183
+
184
+
185
+ __all__ = [
186
+ "ModelsResource",
187
+ "AsyncModelsResource",
188
+ # Models
189
+ "LLMModelList",
190
+ "LLMModelDetail",
191
+ "PaginatedLLMModelListList",
192
+ "ProvidersResponse",
193
+ "StatsResponse",
194
+ "CostCalculationRequestRequest",
195
+ "CostCalculationResponse",
196
+ ]
@@ -0,0 +1,165 @@
1
+ """URL shortening tool using generated API client."""
2
+
3
+ from datetime import datetime
4
+ from typing import Optional
5
+
6
+ import httpx
7
+
8
+ from .._config import SDKConfig
9
+ from .._api.client import (
10
+ BaseResource,
11
+ AsyncBaseResource,
12
+ SyncShortlinksShortlinksAPI,
13
+ ShortlinksShortlinksAPI,
14
+ )
15
+ from .._api.generated.shortlinks.shortlinks__api__shortlinks.models import (
16
+ ShortLinkList,
17
+ ShortLinkDetail,
18
+ ShortLinkCreate,
19
+ ShortLinkCreateRequest,
20
+ PaginatedShortLinkListList,
21
+ ShortLinkStats,
22
+ )
23
+
24
+
25
+ class ShortlinksResource(BaseResource):
26
+ """URL shortening tool (sync).
27
+
28
+ Uses generated SyncShortlinksShortlinksAPI client.
29
+ """
30
+
31
+ def __init__(self, config: SDKConfig):
32
+ super().__init__(config)
33
+ self._api = SyncShortlinksShortlinksAPI(self._http_client)
34
+
35
+ def create(
36
+ self,
37
+ target_url: str,
38
+ *,
39
+ custom_slug: Optional[str] = None,
40
+ expires_at: Optional[datetime] = None,
41
+ max_hits: Optional[int] = None,
42
+ metadata: Optional[dict] = None,
43
+ ) -> ShortLinkCreate:
44
+ """
45
+ Create a short link.
46
+
47
+ Args:
48
+ target_url: URL to redirect to
49
+ custom_slug: Custom slug (optional)
50
+ expires_at: Expiration datetime (optional)
51
+ max_hits: Max redirects (optional)
52
+ metadata: Additional metadata
53
+
54
+ Returns:
55
+ ShortLinkCreate with link info.
56
+ """
57
+ # Build request with only non-None values
58
+ kwargs = {"target_url": target_url}
59
+ if custom_slug is not None:
60
+ kwargs["custom_slug"] = custom_slug
61
+ if expires_at is not None:
62
+ kwargs["expires_at"] = expires_at.isoformat()
63
+ if max_hits is not None:
64
+ kwargs["max_hits"] = max_hits
65
+ if metadata is not None:
66
+ kwargs["metadata"] = metadata
67
+ request = ShortLinkCreateRequest(**kwargs)
68
+ return self._api.create(request)
69
+
70
+ def get(self, code: str) -> ShortLinkDetail:
71
+ """Get link details by code."""
72
+ return self._api.retrieve(code)
73
+
74
+ def list(
75
+ self,
76
+ *,
77
+ page: int = 1,
78
+ page_size: int = 20,
79
+ ) -> PaginatedShortLinkListList:
80
+ """List short links."""
81
+ return self._api.list(page=page, page_size=page_size)
82
+
83
+ def delete(self, code: str) -> bool:
84
+ """Deactivate a short link."""
85
+ try:
86
+ self._api.destroy(code)
87
+ return True
88
+ except httpx.HTTPStatusError:
89
+ return False
90
+
91
+ def stats(self) -> ShortLinkStats:
92
+ """Get link statistics."""
93
+ return self._api.stats_retrieve()
94
+
95
+
96
+ class AsyncShortlinksResource(AsyncBaseResource):
97
+ """URL shortening tool (async).
98
+
99
+ Uses generated ShortlinksShortlinksAPI client.
100
+ """
101
+
102
+ def __init__(self, config: SDKConfig):
103
+ super().__init__(config)
104
+ self._api = ShortlinksShortlinksAPI(self._http_client)
105
+
106
+ async def create(
107
+ self,
108
+ target_url: str,
109
+ *,
110
+ custom_slug: Optional[str] = None,
111
+ expires_at: Optional[datetime] = None,
112
+ max_hits: Optional[int] = None,
113
+ metadata: Optional[dict] = None,
114
+ ) -> ShortLinkCreate:
115
+ """Create a short link."""
116
+ # Build request with only non-None values
117
+ kwargs = {"target_url": target_url}
118
+ if custom_slug is not None:
119
+ kwargs["custom_slug"] = custom_slug
120
+ if expires_at is not None:
121
+ kwargs["expires_at"] = expires_at.isoformat()
122
+ if max_hits is not None:
123
+ kwargs["max_hits"] = max_hits
124
+ if metadata is not None:
125
+ kwargs["metadata"] = metadata
126
+ request = ShortLinkCreateRequest(**kwargs)
127
+ return await self._api.create(request)
128
+
129
+ async def get(self, code: str) -> ShortLinkDetail:
130
+ """Get link details by code."""
131
+ return await self._api.retrieve(code)
132
+
133
+ async def list(
134
+ self,
135
+ *,
136
+ page: int = 1,
137
+ page_size: int = 20,
138
+ ) -> PaginatedShortLinkListList:
139
+ """List short links."""
140
+ return await self._api.list(page=page, page_size=page_size)
141
+
142
+ async def delete(self, code: str) -> bool:
143
+ """Deactivate a short link."""
144
+ try:
145
+ await self._api.destroy(code)
146
+ return True
147
+ except httpx.HTTPStatusError:
148
+ return False
149
+
150
+ async def stats(self) -> ShortLinkStats:
151
+ """Get link statistics."""
152
+ return await self._api.stats_retrieve()
153
+
154
+
155
+ __all__ = [
156
+ "ShortlinksResource",
157
+ "AsyncShortlinksResource",
158
+ # Models
159
+ "ShortLinkList",
160
+ "ShortLinkDetail",
161
+ "ShortLinkCreate",
162
+ "ShortLinkCreateRequest",
163
+ "PaginatedShortLinkListList",
164
+ "ShortLinkStats",
165
+ ]
@@ -0,0 +1,173 @@
1
+ """Vision analysis tool using generated API client."""
2
+
3
+ from typing import Optional
4
+
5
+ from .._config import SDKConfig
6
+ from .._api.client import (
7
+ BaseResource,
8
+ AsyncBaseResource,
9
+ SyncVisionVisionAPI,
10
+ VisionVisionAPI,
11
+ )
12
+ from .._api.generated.vision.vision__api__vision.models import (
13
+ VisionAnalyzeRequestRequest,
14
+ VisionAnalyzeResponse,
15
+ OCRRequestRequest,
16
+ OCRResponse,
17
+ VisionModelsResponse,
18
+ )
19
+ from .._api.generated.vision.enums import (
20
+ OCRRequestRequestMode,
21
+ VisionAnalyzeRequestRequestModelQuality,
22
+ )
23
+
24
+
25
+ class VisionResource(BaseResource):
26
+ """Vision analysis tool (sync).
27
+
28
+ Uses generated SyncVisionVisionAPI client.
29
+ """
30
+
31
+ def __init__(self, config: SDKConfig):
32
+ super().__init__(config)
33
+ self._api = SyncVisionVisionAPI(self._http_client)
34
+
35
+ def analyze(
36
+ self,
37
+ *,
38
+ image: Optional[str] = None,
39
+ image_url: Optional[str] = None,
40
+ prompt: Optional[str] = None,
41
+ model: Optional[str] = None,
42
+ model_quality: Optional[VisionAnalyzeRequestRequestModelQuality] = None,
43
+ fetch_image: Optional[bool] = None,
44
+ max_tokens: Optional[int] = None,
45
+ ) -> VisionAnalyzeResponse:
46
+ """Analyze an image with vision model."""
47
+ # Build request with only non-None values
48
+ kwargs = {}
49
+ if image is not None:
50
+ kwargs["image"] = image
51
+ if image_url is not None:
52
+ kwargs["image_url"] = image_url
53
+ if prompt is not None:
54
+ kwargs["prompt"] = prompt
55
+ if model is not None:
56
+ kwargs["model"] = model
57
+ if model_quality is not None:
58
+ kwargs["model_quality"] = model_quality
59
+ if fetch_image is not None:
60
+ kwargs["fetch_image"] = fetch_image
61
+ if max_tokens is not None:
62
+ kwargs["max_tokens"] = max_tokens
63
+ request = VisionAnalyzeRequestRequest(**kwargs)
64
+ return self._api.analyze_create(request)
65
+
66
+ def ocr(
67
+ self,
68
+ *,
69
+ image: Optional[str] = None,
70
+ image_url: Optional[str] = None,
71
+ mode: Optional[OCRRequestRequestMode] = None,
72
+ language_hint: Optional[str] = None,
73
+ ) -> OCRResponse:
74
+ """Extract text from image using OCR."""
75
+ # Build request with only non-None values
76
+ kwargs = {}
77
+ if image is not None:
78
+ kwargs["image"] = image
79
+ if image_url is not None:
80
+ kwargs["image_url"] = image_url
81
+ if mode is not None:
82
+ kwargs["mode"] = mode
83
+ if language_hint is not None:
84
+ kwargs["language_hint"] = language_hint
85
+ request = OCRRequestRequest(**kwargs)
86
+ return self._api.ocr_create(request)
87
+
88
+ def models(self) -> VisionModelsResponse:
89
+ """Get supported vision models."""
90
+ return self._api.models_retrieve()
91
+
92
+
93
+ class AsyncVisionResource(AsyncBaseResource):
94
+ """Vision analysis tool (async).
95
+
96
+ Uses generated VisionVisionAPI client.
97
+ """
98
+
99
+ def __init__(self, config: SDKConfig):
100
+ super().__init__(config)
101
+ self._api = VisionVisionAPI(self._http_client)
102
+
103
+ async def analyze(
104
+ self,
105
+ *,
106
+ image: Optional[str] = None,
107
+ image_url: Optional[str] = None,
108
+ prompt: Optional[str] = None,
109
+ model: Optional[str] = None,
110
+ model_quality: Optional[VisionAnalyzeRequestRequestModelQuality] = None,
111
+ fetch_image: Optional[bool] = None,
112
+ max_tokens: Optional[int] = None,
113
+ ) -> VisionAnalyzeResponse:
114
+ """Analyze an image with vision model."""
115
+ # Build request with only non-None values
116
+ kwargs = {}
117
+ if image is not None:
118
+ kwargs["image"] = image
119
+ if image_url is not None:
120
+ kwargs["image_url"] = image_url
121
+ if prompt is not None:
122
+ kwargs["prompt"] = prompt
123
+ if model is not None:
124
+ kwargs["model"] = model
125
+ if model_quality is not None:
126
+ kwargs["model_quality"] = model_quality
127
+ if fetch_image is not None:
128
+ kwargs["fetch_image"] = fetch_image
129
+ if max_tokens is not None:
130
+ kwargs["max_tokens"] = max_tokens
131
+ request = VisionAnalyzeRequestRequest(**kwargs)
132
+ return await self._api.analyze_create(request)
133
+
134
+ async def ocr(
135
+ self,
136
+ *,
137
+ image: Optional[str] = None,
138
+ image_url: Optional[str] = None,
139
+ mode: Optional[OCRRequestRequestMode] = None,
140
+ language_hint: Optional[str] = None,
141
+ ) -> OCRResponse:
142
+ """Extract text from image using OCR."""
143
+ # Build request with only non-None values
144
+ kwargs = {}
145
+ if image is not None:
146
+ kwargs["image"] = image
147
+ if image_url is not None:
148
+ kwargs["image_url"] = image_url
149
+ if mode is not None:
150
+ kwargs["mode"] = mode
151
+ if language_hint is not None:
152
+ kwargs["language_hint"] = language_hint
153
+ request = OCRRequestRequest(**kwargs)
154
+ return await self._api.ocr_create(request)
155
+
156
+ async def models(self) -> VisionModelsResponse:
157
+ """Get supported vision models."""
158
+ return await self._api.models_retrieve()
159
+
160
+
161
+ __all__ = [
162
+ "VisionResource",
163
+ "AsyncVisionResource",
164
+ # Models
165
+ "VisionAnalyzeRequestRequest",
166
+ "VisionAnalyzeResponse",
167
+ "OCRRequestRequest",
168
+ "OCRResponse",
169
+ "VisionModelsResponse",
170
+ # Enums
171
+ "OCRRequestRequestMode",
172
+ "VisionAnalyzeRequestRequestModelQuality",
173
+ ]
@@ -0,0 +1,27 @@
1
+ """SDK utilities for token counting, cost calculation, and more."""
2
+
3
+ from .tokens import (
4
+ TokenCounter,
5
+ count_tokens,
6
+ count_messages_tokens,
7
+ estimate_image_tokens,
8
+ get_optimal_detail_mode,
9
+ )
10
+ from .parsing import (
11
+ ResponseFormatT,
12
+ to_strict_json_schema,
13
+ type_to_response_format,
14
+ )
15
+
16
+ __all__ = [
17
+ # Token utilities
18
+ "TokenCounter",
19
+ "count_tokens",
20
+ "count_messages_tokens",
21
+ "estimate_image_tokens",
22
+ "get_optimal_detail_mode",
23
+ # Parsing utilities
24
+ "ResponseFormatT",
25
+ "to_strict_json_schema",
26
+ "type_to_response_format",
27
+ ]
@@ -0,0 +1,109 @@
1
+ """Utilities for structured output parsing with Pydantic models."""
2
+
3
+ from typing import Any, Type, TypeVar
4
+
5
+ from pydantic import BaseModel
6
+
7
+ ResponseFormatT = TypeVar("ResponseFormatT", bound=BaseModel)
8
+
9
+
10
+ def to_strict_json_schema(model: Type[BaseModel]) -> dict[str, Any]:
11
+ """
12
+ Convert Pydantic model to strict JSON Schema for OpenAI API.
13
+
14
+ Strict mode requires:
15
+ - additionalProperties: false at all levels
16
+ - All properties must be in required array
17
+
18
+ Args:
19
+ model: Pydantic model class
20
+
21
+ Returns:
22
+ JSON Schema dict compatible with OpenAI strict mode
23
+ """
24
+ schema = model.model_json_schema()
25
+ return _ensure_strict_schema(schema)
26
+
27
+
28
+ def _ensure_strict_schema(schema: dict[str, Any]) -> dict[str, Any]:
29
+ """
30
+ Make schema strict (additionalProperties: false, all required).
31
+
32
+ Recursively processes nested objects and $defs.
33
+
34
+ Args:
35
+ schema: JSON Schema dict
36
+
37
+ Returns:
38
+ Modified schema with strict mode settings
39
+ """
40
+ schema = schema.copy()
41
+
42
+ # Process $defs (referenced schemas)
43
+ if "$defs" in schema:
44
+ new_defs = {}
45
+ for name, definition in schema["$defs"].items():
46
+ new_defs[name] = _ensure_strict_schema(definition)
47
+ schema["$defs"] = new_defs
48
+
49
+ # Set additionalProperties: false for objects
50
+ if schema.get("type") == "object":
51
+ schema["additionalProperties"] = False
52
+
53
+ # Make all properties required
54
+ if "properties" in schema:
55
+ schema["required"] = list(schema["properties"].keys())
56
+
57
+ # Process nested properties
58
+ new_props = {}
59
+ for prop_name, prop_schema in schema["properties"].items():
60
+ new_props[prop_name] = _ensure_strict_schema(prop_schema)
61
+ schema["properties"] = new_props
62
+
63
+ # Process array items
64
+ if schema.get("type") == "array" and "items" in schema:
65
+ schema["items"] = _ensure_strict_schema(schema["items"])
66
+
67
+ # Process anyOf/oneOf/allOf
68
+ for key in ("anyOf", "oneOf", "allOf"):
69
+ if key in schema:
70
+ schema[key] = [_ensure_strict_schema(item) for item in schema[key]]
71
+
72
+ return schema
73
+
74
+
75
+ def type_to_response_format(response_format: Type[BaseModel]) -> dict[str, Any]:
76
+ """
77
+ Convert Pydantic model to OpenAI response_format parameter.
78
+
79
+ Args:
80
+ response_format: Pydantic model class for response parsing
81
+
82
+ Returns:
83
+ Dict compatible with OpenAI response_format parameter
84
+
85
+ Example:
86
+ >>> class Answer(BaseModel):
87
+ ... text: str
88
+ ... confidence: float
89
+ >>> fmt = type_to_response_format(Answer)
90
+ >>> fmt["type"]
91
+ 'json_schema'
92
+ >>> fmt["json_schema"]["strict"]
93
+ True
94
+ """
95
+ return {
96
+ "type": "json_schema",
97
+ "json_schema": {
98
+ "name": response_format.__name__,
99
+ "schema": to_strict_json_schema(response_format),
100
+ "strict": True,
101
+ },
102
+ }
103
+
104
+
105
+ __all__ = [
106
+ "ResponseFormatT",
107
+ "to_strict_json_schema",
108
+ "type_to_response_format",
109
+ ]