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,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
|
+
]
|