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,101 @@
1
+ """Parsed chat completion types for structured output."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Generic, Optional, TypeVar
5
+
6
+ ContentType = TypeVar("ContentType")
7
+
8
+
9
+ @dataclass
10
+ class ParsedMessage(Generic[ContentType]):
11
+ """Message with parsed content from structured output."""
12
+
13
+ role: str
14
+ content: Optional[str] = None
15
+ parsed: Optional[ContentType] = None
16
+ refusal: Optional[str] = None
17
+ tool_calls: Optional[list[Any]] = None
18
+ function_call: Optional[Any] = None
19
+
20
+ @classmethod
21
+ def from_message(
22
+ cls,
23
+ message: Any,
24
+ parsed_content: Optional[ContentType] = None,
25
+ ) -> "ParsedMessage[ContentType]":
26
+ """Create ParsedMessage from raw ChatCompletionMessage."""
27
+ return cls(
28
+ role=getattr(message, "role", "assistant"),
29
+ content=getattr(message, "content", None),
30
+ parsed=parsed_content,
31
+ refusal=getattr(message, "refusal", None),
32
+ tool_calls=getattr(message, "tool_calls", None),
33
+ function_call=getattr(message, "function_call", None),
34
+ )
35
+
36
+
37
+ @dataclass
38
+ class ParsedChoice(Generic[ContentType]):
39
+ """Choice with parsed message."""
40
+
41
+ index: int
42
+ message: ParsedMessage[ContentType]
43
+ finish_reason: Optional[str] = None
44
+ logprobs: Optional[Any] = None
45
+
46
+ @classmethod
47
+ def from_choice(
48
+ cls,
49
+ choice: Any,
50
+ parsed_content: Optional[ContentType] = None,
51
+ ) -> "ParsedChoice[ContentType]":
52
+ """Create ParsedChoice from raw Choice."""
53
+ return cls(
54
+ index=getattr(choice, "index", 0),
55
+ message=ParsedMessage.from_message(
56
+ getattr(choice, "message", None),
57
+ parsed_content=parsed_content,
58
+ ),
59
+ finish_reason=getattr(choice, "finish_reason", None),
60
+ logprobs=getattr(choice, "logprobs", None),
61
+ )
62
+
63
+
64
+ @dataclass
65
+ class ParsedChatCompletion(Generic[ContentType]):
66
+ """ChatCompletion with parsed content from structured output."""
67
+
68
+ id: str
69
+ choices: list[ParsedChoice[ContentType]]
70
+ created: int
71
+ model: str
72
+ object: str = "chat.completion"
73
+ system_fingerprint: Optional[str] = None
74
+ usage: Optional[Any] = None
75
+ service_tier: Optional[str] = None
76
+
77
+ @classmethod
78
+ def from_completion(
79
+ cls,
80
+ completion: Any,
81
+ parsed_choices: list[ParsedChoice[ContentType]],
82
+ ) -> "ParsedChatCompletion[ContentType]":
83
+ """Create ParsedChatCompletion from raw ChatCompletion."""
84
+ return cls(
85
+ id=getattr(completion, "id", ""),
86
+ choices=parsed_choices,
87
+ created=getattr(completion, "created", 0),
88
+ model=getattr(completion, "model", ""),
89
+ object=getattr(completion, "object", "chat.completion"),
90
+ system_fingerprint=getattr(completion, "system_fingerprint", None),
91
+ usage=getattr(completion, "usage", None),
92
+ service_tier=getattr(completion, "service_tier", None),
93
+ )
94
+
95
+
96
+ __all__ = [
97
+ "ContentType",
98
+ "ParsedMessage",
99
+ "ParsedChoice",
100
+ "ParsedChatCompletion",
101
+ ]
@@ -0,0 +1,27 @@
1
+ """Shortlinks types."""
2
+
3
+ from datetime import datetime
4
+ from typing import Optional
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class ShortLinkCreateRequest(BaseModel):
10
+ """Request for creating a short link."""
11
+
12
+ url: str = Field(description="Target URL to shorten")
13
+ custom_slug: Optional[str] = Field(default=None, description="Custom short code")
14
+ ttl: Optional[str] = Field(default=None, description="Time to live")
15
+ max_hits: Optional[int] = Field(default=None, description="Max redirects allowed")
16
+
17
+
18
+ class ShortLink(BaseModel):
19
+ """Short link information."""
20
+
21
+ code: str = Field(description="Short code")
22
+ short_url: str = Field(description="Full short URL")
23
+ target_url: str = Field(description="Original target URL")
24
+ created_at: datetime = Field(description="Creation time")
25
+ expires_at: Optional[datetime] = Field(default=None, description="Expiration time")
26
+ hit_count: int = Field(default=0, description="Number of redirects")
27
+ is_active: bool = Field(default=True, description="Whether link is active")
@@ -0,0 +1,29 @@
1
+ """Vision types."""
2
+
3
+ from typing import Literal, Optional
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class VisionAnalyzeRequest(BaseModel):
9
+ """Request for vision analysis."""
10
+
11
+ image: Optional[str] = Field(default=None, description="Base64 encoded image")
12
+ image_url: Optional[str] = Field(default=None, description="URL of image to analyze")
13
+ prompt: Optional[str] = Field(default=None, description="Custom analysis prompt")
14
+ model: Optional[str] = Field(default=None, description="Vision model to use")
15
+ model_quality: Literal["fast", "balanced", "best"] = Field(
16
+ default="balanced", description="Quality preset for model selection"
17
+ )
18
+ fetch_image: bool = Field(default=True, description="Whether to fetch and store image")
19
+
20
+
21
+ class VisionAnalyzeResponse(BaseModel):
22
+ """Response from vision analysis."""
23
+
24
+ extracted_text: str = Field(description="Text extracted from the image")
25
+ description: str = Field(description="Description of the image content")
26
+ language: str = Field(description="Detected language")
27
+ model: str = Field(description="Model used for analysis")
28
+ cost_usd: float = Field(description="Cost of the analysis in USD")
29
+ cached: bool = Field(default=False, description="Whether result was from cache")
sdkrouter/_version.py ADDED
@@ -0,0 +1,3 @@
1
+ """Version information."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,13 @@
1
+ """SDK helpers."""
2
+
3
+ from sdkrouter.helpers.formatting import json_to_toon
4
+ from sdkrouter.helpers.json_cleaner import JsonCleaner
5
+ from sdkrouter.helpers.html import html_to_text, extract_links, extract_images
6
+
7
+ __all__ = [
8
+ "json_to_toon",
9
+ "JsonCleaner",
10
+ "html_to_text",
11
+ "extract_links",
12
+ "extract_images",
13
+ ]
@@ -0,0 +1,15 @@
1
+ """JSON formatting utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from toon_python import encode as toon_encode
6
+
7
+
8
+ def json_to_toon(data: dict | list) -> str:
9
+ """
10
+ Convert JSON to TOON format (saves 30-50% tokens).
11
+
12
+ Example:
13
+ {"name": "Alice", "age": 25} → "name: Alice\\nage: 25"
14
+ """
15
+ return toon_encode(data)
@@ -0,0 +1,100 @@
1
+ """HTML utilities for text extraction."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from bs4 import BeautifulSoup
6
+
7
+
8
+ def html_to_text(html: str, separator: str = "\n") -> str:
9
+ """
10
+ Convert HTML to plain text.
11
+
12
+ Args:
13
+ html: HTML string
14
+ separator: Separator between text blocks (default: newline)
15
+
16
+ Returns:
17
+ Plain text with separator between blocks.
18
+
19
+ Usage:
20
+ text = html_to_text("<div><p>Hello</p><p>World</p></div>")
21
+ # "Hello\nWorld"
22
+ """
23
+ if not html:
24
+ return ""
25
+
26
+ soup = BeautifulSoup(html, "lxml")
27
+
28
+ # Remove script and style elements
29
+ for element in soup(["script", "style", "noscript"]):
30
+ element.decompose()
31
+
32
+ # Get text, handling whitespace
33
+ text = soup.get_text(separator=separator, strip=True)
34
+
35
+ # Clean up multiple separators
36
+ if separator == "\n":
37
+ lines = [line.strip() for line in text.split("\n") if line.strip()]
38
+ return "\n".join(lines)
39
+
40
+ return text
41
+
42
+
43
+ def extract_links(html: str, base_url: str = "") -> list[dict]:
44
+ """
45
+ Extract all links from HTML.
46
+
47
+ Args:
48
+ html: HTML string
49
+ base_url: Base URL to resolve relative links
50
+
51
+ Returns:
52
+ List of dicts with 'url', 'text' keys.
53
+ """
54
+ if not html:
55
+ return []
56
+
57
+ soup = BeautifulSoup(html, "lxml")
58
+ links = []
59
+
60
+ for a in soup.find_all("a", href=True):
61
+ href = a["href"]
62
+ if base_url and not href.startswith(("http://", "https://", "//")):
63
+ href = f"{base_url.rstrip('/')}/{href.lstrip('/')}"
64
+ elif href.startswith("//"):
65
+ href = f"https:{href}"
66
+
67
+ links.append({
68
+ "url": href,
69
+ "text": a.get_text(strip=True),
70
+ })
71
+
72
+ return links
73
+
74
+
75
+ def extract_images(html: str, base_url: str = "") -> list[str]:
76
+ """
77
+ Extract all image URLs from HTML.
78
+
79
+ Args:
80
+ html: HTML string
81
+ base_url: Base URL to resolve relative links
82
+
83
+ Returns:
84
+ List of image URLs.
85
+ """
86
+ if not html:
87
+ return []
88
+
89
+ soup = BeautifulSoup(html, "lxml")
90
+ images = []
91
+
92
+ for img in soup.find_all("img", src=True):
93
+ src = img["src"]
94
+ if base_url and not src.startswith(("http://", "https://", "//")):
95
+ src = f"{base_url.rstrip('/')}/{src.lstrip('/')}"
96
+ elif src.startswith("//"):
97
+ src = f"https:{src}"
98
+ images.append(src)
99
+
100
+ return images
@@ -0,0 +1,53 @@
1
+ """JSON cleaning utilities for LLM consumption."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from toon_python import encode as toon_encode
8
+
9
+
10
+ class JsonCleaner:
11
+ """
12
+ Clean JSON and convert to TOON format for LLM.
13
+
14
+ Usage:
15
+ cleaner = JsonCleaner(noise_keys={"Photos", "images"})
16
+ toon_text = cleaner.to_toon(data)
17
+ """
18
+
19
+ DEFAULT_NOISE_KEYS = frozenset({
20
+ "statusItemTypes",
21
+ "attributes",
22
+ "ordering",
23
+ "updatedDate",
24
+ })
25
+
26
+ def __init__(self, noise_keys: set[str] | None = None):
27
+ self.noise_keys = self.DEFAULT_NOISE_KEYS | (noise_keys or set())
28
+
29
+ def compact(self, data: Any) -> Any:
30
+ """Remove nulls, empty values, noise keys. Simplify {code,title}->title."""
31
+ def _process(obj: Any) -> Any:
32
+ if isinstance(obj, dict):
33
+ # Simplify {code, title} pattern
34
+ keys = set(obj.keys())
35
+ if keys <= {"code", "title", "description"} and "title" in keys:
36
+ return obj.get("title") or obj.get("code")
37
+ # Clean dict
38
+ result = {}
39
+ for k, v in obj.items():
40
+ if k in self.noise_keys:
41
+ continue
42
+ cleaned = _process(v)
43
+ if cleaned is not None and cleaned != "" and cleaned != [] and cleaned != {}:
44
+ result[k] = cleaned
45
+ return result
46
+ elif isinstance(obj, list):
47
+ return [_process(item) for item in obj if _process(item) not in (None, "", [], {})]
48
+ return obj
49
+ return _process(data)
50
+
51
+ def to_toon(self, data: Any) -> str:
52
+ """Compact and convert to TOON format."""
53
+ return toon_encode(self.compact(data))
@@ -0,0 +1,129 @@
1
+ """SDK Tools - wrapper resources using generated API clients."""
2
+
3
+ from .vision import (
4
+ VisionResource,
5
+ AsyncVisionResource,
6
+ VisionAnalyzeRequestRequest,
7
+ VisionAnalyzeResponse,
8
+ OCRRequestRequest,
9
+ OCRResponse,
10
+ VisionModelsResponse,
11
+ OCRRequestRequestMode,
12
+ VisionAnalyzeRequestRequestModelQuality,
13
+ )
14
+ from .cdn import (
15
+ CDNResource,
16
+ AsyncCDNResource,
17
+ CDNUploadResponse,
18
+ CDNFileList,
19
+ CDNFileDetail,
20
+ CDNFileUpload,
21
+ CDNFileUploadRequest,
22
+ PaginatedCDNFileListList,
23
+ )
24
+ from .shortlinks import (
25
+ ShortlinksResource,
26
+ AsyncShortlinksResource,
27
+ ShortLinkList,
28
+ ShortLinkDetail,
29
+ ShortLinkCreate,
30
+ ShortLinkCreateRequest,
31
+ PaginatedShortLinkListList,
32
+ )
33
+ from .keys import (
34
+ KeysResource,
35
+ AsyncKeysResource,
36
+ APIKeyList,
37
+ APIKeyDetail,
38
+ APIKeyCreate,
39
+ APIKeyCreateRequest,
40
+ PaginatedAPIKeyListList,
41
+ APIKeyCreatePermission,
42
+ APIKeyCreateRequestPermission,
43
+ APIKeyDetailPermission,
44
+ APIKeyListPermission,
45
+ )
46
+ from .cleaner import (
47
+ CleanerResource,
48
+ AsyncCleanerResource,
49
+ CleanRequestRequest,
50
+ CleanResponse,
51
+ CleaningRequestDetail,
52
+ CleaningStats,
53
+ CleanRequestRequestOutputFormat,
54
+ CleaningRequestDetailStatus,
55
+ CleaningRequestListStatus,
56
+ )
57
+ from .models import (
58
+ ModelsResource,
59
+ AsyncModelsResource,
60
+ LLMModelList,
61
+ LLMModelDetail,
62
+ PaginatedLLMModelListList,
63
+ ProvidersResponse,
64
+ StatsResponse,
65
+ CostCalculationRequestRequest,
66
+ CostCalculationResponse,
67
+ )
68
+
69
+ __all__ = [
70
+ # Vision
71
+ "VisionResource",
72
+ "AsyncVisionResource",
73
+ "VisionAnalyzeRequestRequest",
74
+ "VisionAnalyzeResponse",
75
+ "OCRRequestRequest",
76
+ "OCRResponse",
77
+ "VisionModelsResponse",
78
+ "OCRRequestRequestMode",
79
+ "VisionAnalyzeRequestRequestModelQuality",
80
+ # CDN
81
+ "CDNResource",
82
+ "AsyncCDNResource",
83
+ "CDNUploadResponse",
84
+ "CDNFileList",
85
+ "CDNFileDetail",
86
+ "CDNFileUpload",
87
+ "CDNFileUploadRequest",
88
+ "PaginatedCDNFileListList",
89
+ # Shortlinks
90
+ "ShortlinksResource",
91
+ "AsyncShortlinksResource",
92
+ "ShortLinkList",
93
+ "ShortLinkDetail",
94
+ "ShortLinkCreate",
95
+ "ShortLinkCreateRequest",
96
+ "PaginatedShortLinkListList",
97
+ # Keys
98
+ "KeysResource",
99
+ "AsyncKeysResource",
100
+ "APIKeyList",
101
+ "APIKeyDetail",
102
+ "APIKeyCreate",
103
+ "APIKeyCreateRequest",
104
+ "PaginatedAPIKeyListList",
105
+ "APIKeyCreatePermission",
106
+ "APIKeyCreateRequestPermission",
107
+ "APIKeyDetailPermission",
108
+ "APIKeyListPermission",
109
+ # Cleaner
110
+ "CleanerResource",
111
+ "AsyncCleanerResource",
112
+ "CleanRequestRequest",
113
+ "CleanResponse",
114
+ "CleaningRequestDetail",
115
+ "CleaningStats",
116
+ "CleanRequestRequestOutputFormat",
117
+ "CleaningRequestDetailStatus",
118
+ "CleaningRequestListStatus",
119
+ # Models
120
+ "ModelsResource",
121
+ "AsyncModelsResource",
122
+ "LLMModelList",
123
+ "LLMModelDetail",
124
+ "PaginatedLLMModelListList",
125
+ "ProvidersResponse",
126
+ "StatsResponse",
127
+ "CostCalculationRequestRequest",
128
+ "CostCalculationResponse",
129
+ ]