openrouter 0.0.19__py3-none-any.whl → 0.0.22__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 (61) hide show
  1. openrouter/_version.py +3 -3
  2. openrouter/analytics.py +2 -0
  3. openrouter/api_keys.py +24 -4
  4. openrouter/basesdk.py +6 -0
  5. openrouter/chat.py +154 -14
  6. openrouter/completions.py +2 -0
  7. openrouter/components/__init__.py +156 -61
  8. openrouter/components/_schema0.py +93 -0
  9. openrouter/components/chatcompletionfinishreason.py +17 -0
  10. openrouter/components/chatgenerationparams.py +397 -13
  11. openrouter/components/chatmessagecontentitem.py +1 -1
  12. openrouter/components/chatmessagecontentitemaudio.py +6 -25
  13. openrouter/components/chatmessagecontentitemcachecontrol.py +32 -0
  14. openrouter/components/chatmessagecontentitemtext.py +9 -2
  15. openrouter/components/chatmessagecontentitemvideo.py +9 -5
  16. openrouter/components/chatresponsechoice.py +1 -67
  17. openrouter/components/chatstreamingchoice.py +72 -0
  18. openrouter/components/chatstreamingresponsechunk.py +1 -1
  19. openrouter/components/completionchoice.py +18 -5
  20. openrouter/components/completioncreateparams.py +10 -10
  21. openrouter/components/completionresponse.py +3 -0
  22. openrouter/components/message.py +10 -9
  23. openrouter/components/openairesponsesannotation.py +11 -4
  24. openrouter/components/openairesponsesreasoningeffort.py +1 -0
  25. openrouter/components/openresponsesnonstreamingresponse.py +13 -10
  26. openrouter/components/openresponsesrequest.py +155 -81
  27. openrouter/components/openresponsesstreamevent.py +110 -39
  28. openrouter/components/outputmessage.py +10 -4
  29. openrouter/components/providername.py +8 -1
  30. openrouter/components/publicendpoint.py +49 -49
  31. openrouter/components/publicpricing.py +49 -49
  32. openrouter/components/responseformattextconfig.py +9 -7
  33. openrouter/components/responsesoutputitem.py +12 -10
  34. openrouter/components/responsesoutputmessage.py +10 -5
  35. openrouter/credits.py +4 -0
  36. openrouter/embeddings.py +4 -0
  37. openrouter/endpoints.py +4 -0
  38. openrouter/generations.py +2 -0
  39. openrouter/models/__init__.py +3 -0
  40. openrouter/models/internal/__init__.py +54 -0
  41. openrouter/models/internal/globals.py +41 -0
  42. openrouter/models_.py +6 -0
  43. openrouter/oauth.py +4 -0
  44. openrouter/operations/__init__.py +10 -1
  45. openrouter/operations/createembeddings.py +44 -24
  46. openrouter/operations/getcredits.py +19 -0
  47. openrouter/operations/getparameters.py +8 -1
  48. openrouter/operations/updatekeys.py +2 -2
  49. openrouter/parameters.py +2 -0
  50. openrouter/providers.py +2 -0
  51. openrouter/responses.py +88 -36
  52. openrouter/sdk.py +13 -0
  53. openrouter/sdkconfiguration.py +2 -0
  54. openrouter/utils/forms.py +21 -10
  55. openrouter/utils/queryparams.py +14 -2
  56. openrouter/utils/retries.py +69 -5
  57. {openrouter-0.0.19.dist-info → openrouter-0.0.22.dist-info}/METADATA +1 -1
  58. {openrouter-0.0.19.dist-info → openrouter-0.0.22.dist-info}/RECORD +61 -54
  59. {openrouter-0.0.19.dist-info → openrouter-0.0.22.dist-info}/WHEEL +0 -0
  60. {openrouter-0.0.19.dist-info → openrouter-0.0.22.dist-info}/licenses/LICENSE +0 -0
  61. {openrouter-0.0.19.dist-info → openrouter-0.0.22.dist-info}/top_level.txt +0 -0
@@ -8,74 +8,74 @@ from openrouter.types import BaseModel, Nullable, UNSET_SENTINEL, UnrecognizedSt
8
8
  from openrouter.utils import validate_open_enum
9
9
  from pydantic import model_serializer
10
10
  from pydantic.functional_validators import PlainValidator
11
- from typing import Any, List, Literal, Optional, Union
11
+ from typing import List, Literal, Optional, Union
12
12
  from typing_extensions import Annotated, NotRequired, TypedDict
13
13
 
14
14
 
15
15
  class PricingTypedDict(TypedDict):
16
- prompt: Any
17
- r"""A value in string or number format that is a large number"""
18
- completion: Any
19
- r"""A value in string or number format that is a large number"""
20
- request: NotRequired[Any]
21
- r"""A value in string or number format that is a large number"""
22
- image: NotRequired[Any]
23
- r"""A value in string or number format that is a large number"""
24
- image_token: NotRequired[Any]
25
- r"""A value in string or number format that is a large number"""
26
- image_output: NotRequired[Any]
27
- r"""A value in string or number format that is a large number"""
28
- audio: NotRequired[Any]
29
- r"""A value in string or number format that is a large number"""
30
- input_audio_cache: NotRequired[Any]
31
- r"""A value in string or number format that is a large number"""
32
- web_search: NotRequired[Any]
33
- r"""A value in string or number format that is a large number"""
34
- internal_reasoning: NotRequired[Any]
35
- r"""A value in string or number format that is a large number"""
36
- input_cache_read: NotRequired[Any]
37
- r"""A value in string or number format that is a large number"""
38
- input_cache_write: NotRequired[Any]
39
- r"""A value in string or number format that is a large number"""
16
+ prompt: str
17
+ r"""A value in string format that is a large number"""
18
+ completion: str
19
+ r"""A value in string format that is a large number"""
20
+ request: NotRequired[str]
21
+ r"""A value in string format that is a large number"""
22
+ image: NotRequired[str]
23
+ r"""A value in string format that is a large number"""
24
+ image_token: NotRequired[str]
25
+ r"""A value in string format that is a large number"""
26
+ image_output: NotRequired[str]
27
+ r"""A value in string format that is a large number"""
28
+ audio: NotRequired[str]
29
+ r"""A value in string format that is a large number"""
30
+ input_audio_cache: NotRequired[str]
31
+ r"""A value in string format that is a large number"""
32
+ web_search: NotRequired[str]
33
+ r"""A value in string format that is a large number"""
34
+ internal_reasoning: NotRequired[str]
35
+ r"""A value in string format that is a large number"""
36
+ input_cache_read: NotRequired[str]
37
+ r"""A value in string format that is a large number"""
38
+ input_cache_write: NotRequired[str]
39
+ r"""A value in string format that is a large number"""
40
40
  discount: NotRequired[float]
41
41
 
42
42
 
43
43
  class Pricing(BaseModel):
44
- prompt: Any
45
- r"""A value in string or number format that is a large number"""
44
+ prompt: str
45
+ r"""A value in string format that is a large number"""
46
46
 
47
- completion: Any
48
- r"""A value in string or number format that is a large number"""
47
+ completion: str
48
+ r"""A value in string format that is a large number"""
49
49
 
50
- request: Optional[Any] = None
51
- r"""A value in string or number format that is a large number"""
50
+ request: Optional[str] = None
51
+ r"""A value in string format that is a large number"""
52
52
 
53
- image: Optional[Any] = None
54
- r"""A value in string or number format that is a large number"""
53
+ image: Optional[str] = None
54
+ r"""A value in string format that is a large number"""
55
55
 
56
- image_token: Optional[Any] = None
57
- r"""A value in string or number format that is a large number"""
56
+ image_token: Optional[str] = None
57
+ r"""A value in string format that is a large number"""
58
58
 
59
- image_output: Optional[Any] = None
60
- r"""A value in string or number format that is a large number"""
59
+ image_output: Optional[str] = None
60
+ r"""A value in string format that is a large number"""
61
61
 
62
- audio: Optional[Any] = None
63
- r"""A value in string or number format that is a large number"""
62
+ audio: Optional[str] = None
63
+ r"""A value in string format that is a large number"""
64
64
 
65
- input_audio_cache: Optional[Any] = None
66
- r"""A value in string or number format that is a large number"""
65
+ input_audio_cache: Optional[str] = None
66
+ r"""A value in string format that is a large number"""
67
67
 
68
- web_search: Optional[Any] = None
69
- r"""A value in string or number format that is a large number"""
68
+ web_search: Optional[str] = None
69
+ r"""A value in string format that is a large number"""
70
70
 
71
- internal_reasoning: Optional[Any] = None
72
- r"""A value in string or number format that is a large number"""
71
+ internal_reasoning: Optional[str] = None
72
+ r"""A value in string format that is a large number"""
73
73
 
74
- input_cache_read: Optional[Any] = None
75
- r"""A value in string or number format that is a large number"""
74
+ input_cache_read: Optional[str] = None
75
+ r"""A value in string format that is a large number"""
76
76
 
77
- input_cache_write: Optional[Any] = None
78
- r"""A value in string or number format that is a large number"""
77
+ input_cache_write: Optional[str] = None
78
+ r"""A value in string format that is a large number"""
79
79
 
80
80
  discount: Optional[float] = None
81
81
 
@@ -2,77 +2,77 @@
2
2
 
3
3
  from __future__ import annotations
4
4
  from openrouter.types import BaseModel
5
- from typing import Any, Optional
5
+ from typing import Optional
6
6
  from typing_extensions import NotRequired, TypedDict
7
7
 
8
8
 
9
9
  class PublicPricingTypedDict(TypedDict):
10
10
  r"""Pricing information for the model"""
11
11
 
12
- prompt: Any
13
- r"""A value in string or number format that is a large number"""
14
- completion: Any
15
- r"""A value in string or number format that is a large number"""
16
- request: NotRequired[Any]
17
- r"""A value in string or number format that is a large number"""
18
- image: NotRequired[Any]
19
- r"""A value in string or number format that is a large number"""
20
- image_token: NotRequired[Any]
21
- r"""A value in string or number format that is a large number"""
22
- image_output: NotRequired[Any]
23
- r"""A value in string or number format that is a large number"""
24
- audio: NotRequired[Any]
25
- r"""A value in string or number format that is a large number"""
26
- input_audio_cache: NotRequired[Any]
27
- r"""A value in string or number format that is a large number"""
28
- web_search: NotRequired[Any]
29
- r"""A value in string or number format that is a large number"""
30
- internal_reasoning: NotRequired[Any]
31
- r"""A value in string or number format that is a large number"""
32
- input_cache_read: NotRequired[Any]
33
- r"""A value in string or number format that is a large number"""
34
- input_cache_write: NotRequired[Any]
35
- r"""A value in string or number format that is a large number"""
12
+ prompt: str
13
+ r"""A value in string format that is a large number"""
14
+ completion: str
15
+ r"""A value in string format that is a large number"""
16
+ request: NotRequired[str]
17
+ r"""A value in string format that is a large number"""
18
+ image: NotRequired[str]
19
+ r"""A value in string format that is a large number"""
20
+ image_token: NotRequired[str]
21
+ r"""A value in string format that is a large number"""
22
+ image_output: NotRequired[str]
23
+ r"""A value in string format that is a large number"""
24
+ audio: NotRequired[str]
25
+ r"""A value in string format that is a large number"""
26
+ input_audio_cache: NotRequired[str]
27
+ r"""A value in string format that is a large number"""
28
+ web_search: NotRequired[str]
29
+ r"""A value in string format that is a large number"""
30
+ internal_reasoning: NotRequired[str]
31
+ r"""A value in string format that is a large number"""
32
+ input_cache_read: NotRequired[str]
33
+ r"""A value in string format that is a large number"""
34
+ input_cache_write: NotRequired[str]
35
+ r"""A value in string format that is a large number"""
36
36
  discount: NotRequired[float]
37
37
 
38
38
 
39
39
  class PublicPricing(BaseModel):
40
40
  r"""Pricing information for the model"""
41
41
 
42
- prompt: Any
43
- r"""A value in string or number format that is a large number"""
42
+ prompt: str
43
+ r"""A value in string format that is a large number"""
44
44
 
45
- completion: Any
46
- r"""A value in string or number format that is a large number"""
45
+ completion: str
46
+ r"""A value in string format that is a large number"""
47
47
 
48
- request: Optional[Any] = None
49
- r"""A value in string or number format that is a large number"""
48
+ request: Optional[str] = None
49
+ r"""A value in string format that is a large number"""
50
50
 
51
- image: Optional[Any] = None
52
- r"""A value in string or number format that is a large number"""
51
+ image: Optional[str] = None
52
+ r"""A value in string format that is a large number"""
53
53
 
54
- image_token: Optional[Any] = None
55
- r"""A value in string or number format that is a large number"""
54
+ image_token: Optional[str] = None
55
+ r"""A value in string format that is a large number"""
56
56
 
57
- image_output: Optional[Any] = None
58
- r"""A value in string or number format that is a large number"""
57
+ image_output: Optional[str] = None
58
+ r"""A value in string format that is a large number"""
59
59
 
60
- audio: Optional[Any] = None
61
- r"""A value in string or number format that is a large number"""
60
+ audio: Optional[str] = None
61
+ r"""A value in string format that is a large number"""
62
62
 
63
- input_audio_cache: Optional[Any] = None
64
- r"""A value in string or number format that is a large number"""
63
+ input_audio_cache: Optional[str] = None
64
+ r"""A value in string format that is a large number"""
65
65
 
66
- web_search: Optional[Any] = None
67
- r"""A value in string or number format that is a large number"""
66
+ web_search: Optional[str] = None
67
+ r"""A value in string format that is a large number"""
68
68
 
69
- internal_reasoning: Optional[Any] = None
70
- r"""A value in string or number format that is a large number"""
69
+ internal_reasoning: Optional[str] = None
70
+ r"""A value in string format that is a large number"""
71
71
 
72
- input_cache_read: Optional[Any] = None
73
- r"""A value in string or number format that is a large number"""
72
+ input_cache_read: Optional[str] = None
73
+ r"""A value in string format that is a large number"""
74
74
 
75
- input_cache_write: Optional[Any] = None
76
- r"""A value in string or number format that is a large number"""
75
+ input_cache_write: Optional[str] = None
76
+ r"""A value in string format that is a large number"""
77
77
 
78
78
  discount: Optional[float] = None
@@ -10,8 +10,10 @@ from .responsesformattextjsonschemaconfig import (
10
10
  ResponsesFormatTextJSONSchemaConfig,
11
11
  ResponsesFormatTextJSONSchemaConfigTypedDict,
12
12
  )
13
+ from openrouter.utils import get_discriminator
14
+ from pydantic import Discriminator, Tag
13
15
  from typing import Union
14
- from typing_extensions import TypeAliasType
16
+ from typing_extensions import Annotated, TypeAliasType
15
17
 
16
18
 
17
19
  ResponseFormatTextConfigTypedDict = TypeAliasType(
@@ -25,12 +27,12 @@ ResponseFormatTextConfigTypedDict = TypeAliasType(
25
27
  r"""Text response format configuration"""
26
28
 
27
29
 
28
- ResponseFormatTextConfig = TypeAliasType(
29
- "ResponseFormatTextConfig",
30
+ ResponseFormatTextConfig = Annotated[
30
31
  Union[
31
- ResponsesFormatText,
32
- ResponsesFormatJSONObject,
33
- ResponsesFormatTextJSONSchemaConfig,
32
+ Annotated[ResponsesFormatText, Tag("text")],
33
+ Annotated[ResponsesFormatJSONObject, Tag("json_object")],
34
+ Annotated[ResponsesFormatTextJSONSchemaConfig, Tag("json_schema")],
34
35
  ],
35
- )
36
+ Discriminator(lambda m: get_discriminator(m, "type", "type")),
37
+ ]
36
38
  r"""Text response format configuration"""
@@ -25,8 +25,10 @@ from .responseswebsearchcalloutput import (
25
25
  ResponsesWebSearchCallOutput,
26
26
  ResponsesWebSearchCallOutputTypedDict,
27
27
  )
28
+ from openrouter.utils import get_discriminator
29
+ from pydantic import Discriminator, Tag
28
30
  from typing import Union
29
- from typing_extensions import TypeAliasType
31
+ from typing_extensions import Annotated, TypeAliasType
30
32
 
31
33
 
32
34
  ResponsesOutputItemTypedDict = TypeAliasType(
@@ -43,15 +45,15 @@ ResponsesOutputItemTypedDict = TypeAliasType(
43
45
  r"""An output item from the response"""
44
46
 
45
47
 
46
- ResponsesOutputItem = TypeAliasType(
47
- "ResponsesOutputItem",
48
+ ResponsesOutputItem = Annotated[
48
49
  Union[
49
- ResponsesWebSearchCallOutput,
50
- ResponsesOutputItemFileSearchCall,
51
- ResponsesImageGenerationCall,
52
- ResponsesOutputMessage,
53
- ResponsesOutputItemReasoning,
54
- ResponsesOutputItemFunctionCall,
50
+ Annotated[ResponsesOutputMessage, Tag("message")],
51
+ Annotated[ResponsesOutputItemReasoning, Tag("reasoning")],
52
+ Annotated[ResponsesOutputItemFunctionCall, Tag("function_call")],
53
+ Annotated[ResponsesWebSearchCallOutput, Tag("web_search_call")],
54
+ Annotated[ResponsesOutputItemFileSearchCall, Tag("file_search_call")],
55
+ Annotated[ResponsesImageGenerationCall, Tag("image_generation_call")],
55
56
  ],
56
- )
57
+ Discriminator(lambda m: get_discriminator(m, "type", "type")),
58
+ ]
57
59
  r"""An output item from the response"""
@@ -7,8 +7,10 @@ from .openairesponsesrefusalcontent import (
7
7
  )
8
8
  from .responseoutputtext import ResponseOutputText, ResponseOutputTextTypedDict
9
9
  from openrouter.types import BaseModel
10
+ from openrouter.utils import get_discriminator
11
+ from pydantic import Discriminator, Tag
10
12
  from typing import List, Literal, Optional, Union
11
- from typing_extensions import NotRequired, TypeAliasType, TypedDict
13
+ from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict
12
14
 
13
15
 
14
16
  ResponsesOutputMessageRole = Literal["assistant",]
@@ -52,10 +54,13 @@ ResponsesOutputMessageContentTypedDict = TypeAliasType(
52
54
  )
53
55
 
54
56
 
55
- ResponsesOutputMessageContent = TypeAliasType(
56
- "ResponsesOutputMessageContent",
57
- Union[OpenAIResponsesRefusalContent, ResponseOutputText],
58
- )
57
+ ResponsesOutputMessageContent = Annotated[
58
+ Union[
59
+ Annotated[ResponseOutputText, Tag("output_text")],
60
+ Annotated[OpenAIResponsesRefusalContent, Tag("refusal")],
61
+ ],
62
+ Discriminator(lambda m: get_discriminator(m, "type", "type")),
63
+ ]
59
64
 
60
65
 
61
66
  class ResponsesOutputMessageTypedDict(TypedDict):
openrouter/credits.py CHANGED
@@ -51,6 +51,7 @@ class Credits(BaseSDK):
51
51
  accept_header_value="application/json",
52
52
  http_headers=http_headers,
53
53
  security=self.sdk_configuration.security,
54
+ allow_empty_value=None,
54
55
  timeout_ms=timeout_ms,
55
56
  )
56
57
 
@@ -147,6 +148,7 @@ class Credits(BaseSDK):
147
148
  accept_header_value="application/json",
148
149
  http_headers=http_headers,
149
150
  security=self.sdk_configuration.security,
151
+ allow_empty_value=None,
150
152
  timeout_ms=timeout_ms,
151
153
  )
152
154
 
@@ -266,6 +268,7 @@ class Credits(BaseSDK):
266
268
  get_serialized_body=lambda: utils.serialize_request_body(
267
269
  request, False, False, "json", components.CreateChargeRequest
268
270
  ),
271
+ allow_empty_value=None,
269
272
  timeout_ms=timeout_ms,
270
273
  )
271
274
 
@@ -390,6 +393,7 @@ class Credits(BaseSDK):
390
393
  get_serialized_body=lambda: utils.serialize_request_body(
391
394
  request, False, False, "json", components.CreateChargeRequest
392
395
  ),
396
+ allow_empty_value=None,
393
397
  timeout_ms=timeout_ms,
394
398
  )
395
399
 
openrouter/embeddings.py CHANGED
@@ -96,6 +96,7 @@ class Embeddings(BaseSDK):
96
96
  get_serialized_body=lambda: utils.serialize_request_body(
97
97
  request, False, False, "json", operations.CreateEmbeddingsRequest
98
98
  ),
99
+ allow_empty_value=None,
99
100
  timeout_ms=timeout_ms,
100
101
  )
101
102
 
@@ -283,6 +284,7 @@ class Embeddings(BaseSDK):
283
284
  get_serialized_body=lambda: utils.serialize_request_body(
284
285
  request, False, False, "json", operations.CreateEmbeddingsRequest
285
286
  ),
287
+ allow_empty_value=None,
286
288
  timeout_ms=timeout_ms,
287
289
  )
288
290
 
@@ -431,6 +433,7 @@ class Embeddings(BaseSDK):
431
433
  accept_header_value="application/json",
432
434
  http_headers=http_headers,
433
435
  security=self.sdk_configuration.security,
436
+ allow_empty_value=None,
434
437
  timeout_ms=timeout_ms,
435
438
  )
436
439
 
@@ -522,6 +525,7 @@ class Embeddings(BaseSDK):
522
525
  accept_header_value="application/json",
523
526
  http_headers=http_headers,
524
527
  security=self.sdk_configuration.security,
528
+ allow_empty_value=None,
525
529
  timeout_ms=timeout_ms,
526
530
  )
527
531
 
openrouter/endpoints.py CHANGED
@@ -59,6 +59,7 @@ class Endpoints(BaseSDK):
59
59
  accept_header_value="application/json",
60
60
  http_headers=http_headers,
61
61
  security=self.sdk_configuration.security,
62
+ allow_empty_value=None,
62
63
  timeout_ms=timeout_ms,
63
64
  )
64
65
 
@@ -158,6 +159,7 @@ class Endpoints(BaseSDK):
158
159
  accept_header_value="application/json",
159
160
  http_headers=http_headers,
160
161
  security=self.sdk_configuration.security,
162
+ allow_empty_value=None,
161
163
  timeout_ms=timeout_ms,
162
164
  )
163
165
 
@@ -247,6 +249,7 @@ class Endpoints(BaseSDK):
247
249
  accept_header_value="application/json",
248
250
  http_headers=http_headers,
249
251
  security=self.sdk_configuration.security,
252
+ allow_empty_value=None,
250
253
  timeout_ms=timeout_ms,
251
254
  )
252
255
 
@@ -333,6 +336,7 @@ class Endpoints(BaseSDK):
333
336
  accept_header_value="application/json",
334
337
  http_headers=http_headers,
335
338
  security=self.sdk_configuration.security,
339
+ allow_empty_value=None,
336
340
  timeout_ms=timeout_ms,
337
341
  )
338
342
 
openrouter/generations.py CHANGED
@@ -56,6 +56,7 @@ class Generations(BaseSDK):
56
56
  accept_header_value="application/json",
57
57
  http_headers=http_headers,
58
58
  security=self.sdk_configuration.security,
59
+ allow_empty_value=None,
59
60
  timeout_ms=timeout_ms,
60
61
  )
61
62
 
@@ -193,6 +194,7 @@ class Generations(BaseSDK):
193
194
  accept_header_value="application/json",
194
195
  http_headers=http_headers,
195
196
  security=self.sdk_configuration.security,
197
+ allow_empty_value=None,
196
198
  timeout_ms=timeout_ms,
197
199
  )
198
200
 
@@ -0,0 +1,3 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ # package
@@ -0,0 +1,54 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from typing import TYPE_CHECKING
4
+ from importlib import import_module
5
+ import builtins
6
+ import sys
7
+
8
+ if TYPE_CHECKING:
9
+ from .globals import Globals, GlobalsTypedDict
10
+
11
+ __all__ = ["Globals", "GlobalsTypedDict"]
12
+
13
+ _dynamic_imports: dict[str, str] = {
14
+ "Globals": ".globals",
15
+ "GlobalsTypedDict": ".globals",
16
+ }
17
+
18
+
19
+ def dynamic_import(modname, retries=3):
20
+ for attempt in range(retries):
21
+ try:
22
+ return import_module(modname, __package__)
23
+ except KeyError:
24
+ # Clear any half-initialized module and retry
25
+ sys.modules.pop(modname, None)
26
+ if attempt == retries - 1:
27
+ break
28
+ raise KeyError(f"Failed to import module '{modname}' after {retries} attempts")
29
+
30
+
31
+ def __getattr__(attr_name: str) -> object:
32
+ module_name = _dynamic_imports.get(attr_name)
33
+ if module_name is None:
34
+ raise AttributeError(
35
+ f"No {attr_name} found in _dynamic_imports for module name -> {__name__} "
36
+ )
37
+
38
+ try:
39
+ module = dynamic_import(module_name)
40
+ result = getattr(module, attr_name)
41
+ return result
42
+ except ImportError as e:
43
+ raise ImportError(
44
+ f"Failed to import {attr_name} from {module_name}: {e}"
45
+ ) from e
46
+ except AttributeError as e:
47
+ raise AttributeError(
48
+ f"Failed to get {attr_name} from {module_name}: {e}"
49
+ ) from e
50
+
51
+
52
+ def __dir__():
53
+ lazy_attrs = builtins.list(_dynamic_imports.keys())
54
+ return builtins.sorted(lazy_attrs)
@@ -0,0 +1,41 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+ from openrouter.types import BaseModel
5
+ from openrouter.utils import FieldMetadata, HeaderMetadata
6
+ import pydantic
7
+ from typing import Optional
8
+ from typing_extensions import Annotated, NotRequired, TypedDict
9
+
10
+
11
+ class GlobalsTypedDict(TypedDict):
12
+ http_referer: NotRequired[str]
13
+ r"""The app identifier should be your app's URL and is used as the primary identifier for rankings.
14
+ This is used to track API usage per application.
15
+
16
+ """
17
+ x_title: NotRequired[str]
18
+ r"""The app display name allows you to customize how your app appears in OpenRouter's dashboard.
19
+
20
+ """
21
+
22
+
23
+ class Globals(BaseModel):
24
+ http_referer: Annotated[
25
+ Optional[str],
26
+ pydantic.Field(alias="HTTP-Referer"),
27
+ FieldMetadata(header=HeaderMetadata(style="simple", explode=False)),
28
+ ] = None
29
+ r"""The app identifier should be your app's URL and is used as the primary identifier for rankings.
30
+ This is used to track API usage per application.
31
+
32
+ """
33
+
34
+ x_title: Annotated[
35
+ Optional[str],
36
+ pydantic.Field(alias="X-Title"),
37
+ FieldMetadata(header=HeaderMetadata(style="simple", explode=False)),
38
+ ] = None
39
+ r"""The app display name allows you to customize how your app appears in OpenRouter's dashboard.
40
+
41
+ """
openrouter/models_.py CHANGED
@@ -49,6 +49,7 @@ class Models(BaseSDK):
49
49
  accept_header_value="application/json",
50
50
  http_headers=http_headers,
51
51
  security=self.sdk_configuration.security,
52
+ allow_empty_value=None,
52
53
  timeout_ms=timeout_ms,
53
54
  )
54
55
 
@@ -133,6 +134,7 @@ class Models(BaseSDK):
133
134
  accept_header_value="application/json",
134
135
  http_headers=http_headers,
135
136
  security=self.sdk_configuration.security,
137
+ allow_empty_value=None,
136
138
  timeout_ms=timeout_ms,
137
139
  )
138
140
 
@@ -227,6 +229,7 @@ class Models(BaseSDK):
227
229
  accept_header_value="application/json",
228
230
  http_headers=http_headers,
229
231
  security=self.sdk_configuration.security,
232
+ allow_empty_value=None,
230
233
  timeout_ms=timeout_ms,
231
234
  )
232
235
 
@@ -326,6 +329,7 @@ class Models(BaseSDK):
326
329
  accept_header_value="application/json",
327
330
  http_headers=http_headers,
328
331
  security=self.sdk_configuration.security,
332
+ allow_empty_value=None,
329
333
  timeout_ms=timeout_ms,
330
334
  )
331
335
 
@@ -422,6 +426,7 @@ class Models(BaseSDK):
422
426
  security=utils.get_pydantic_model(
423
427
  security, operations.ListModelsUserSecurity
424
428
  ),
429
+ allow_empty_value=None,
425
430
  timeout_ms=timeout_ms,
426
431
  )
427
432
 
@@ -516,6 +521,7 @@ class Models(BaseSDK):
516
521
  security=utils.get_pydantic_model(
517
522
  security, operations.ListModelsUserSecurity
518
523
  ),
524
+ allow_empty_value=None,
519
525
  timeout_ms=timeout_ms,
520
526
  )
521
527
 
openrouter/oauth.py CHANGED
@@ -74,6 +74,7 @@ class OAuth(BaseSDK):
74
74
  "json",
75
75
  operations.ExchangeAuthCodeForAPIKeyRequest,
76
76
  ),
77
+ allow_empty_value=None,
77
78
  timeout_ms=timeout_ms,
78
79
  )
79
80
 
@@ -194,6 +195,7 @@ class OAuth(BaseSDK):
194
195
  "json",
195
196
  operations.ExchangeAuthCodeForAPIKeyRequest,
196
197
  ),
198
+ allow_empty_value=None,
197
199
  timeout_ms=timeout_ms,
198
200
  )
199
201
 
@@ -316,6 +318,7 @@ class OAuth(BaseSDK):
316
318
  get_serialized_body=lambda: utils.serialize_request_body(
317
319
  request, False, False, "json", operations.CreateAuthKeysCodeRequest
318
320
  ),
321
+ allow_empty_value=None,
319
322
  timeout_ms=timeout_ms,
320
323
  )
321
324
 
@@ -438,6 +441,7 @@ class OAuth(BaseSDK):
438
441
  get_serialized_body=lambda: utils.serialize_request_body(
439
442
  request, False, False, "json", operations.CreateAuthKeysCodeRequest
440
443
  ),
444
+ allow_empty_value=None,
441
445
  timeout_ms=timeout_ms,
442
446
  )
443
447