lark-billing 0.0.7__py3-none-any.whl → 0.0.8__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.

Potentially problematic release.


This version of lark-billing might be problematic. Click here for more details.

Files changed (46) hide show
  1. lark/__init__.py +42 -28
  2. lark/checkout/client.py +26 -8
  3. lark/checkout/raw_client.py +18 -6
  4. lark/client.py +19 -0
  5. lark/core/client_wrapper.py +2 -2
  6. lark/core/http_sse/__init__.py +42 -0
  7. lark/core/http_sse/_api.py +112 -0
  8. lark/core/http_sse/_decoders.py +61 -0
  9. lark/core/http_sse/_exceptions.py +7 -0
  10. lark/core/http_sse/_models.py +17 -0
  11. lark/core/pydantic_utilities.py +3 -1
  12. lark/invoices/__init__.py +4 -0
  13. lark/invoices/client.py +136 -0
  14. lark/invoices/raw_client.py +147 -0
  15. lark/pricing_metrics/client.py +69 -0
  16. lark/pricing_metrics/raw_client.py +101 -0
  17. lark/rate_cards/__init__.py +15 -3
  18. lark/rate_cards/client.py +10 -10
  19. lark/rate_cards/raw_client.py +14 -14
  20. lark/rate_cards/types/__init__.py +12 -2
  21. lark/rate_cards/types/create_rate_card_request_usage_based_rates_item.py +30 -0
  22. lark/subjects/client.py +16 -18
  23. lark/subjects/raw_client.py +14 -8
  24. lark/subscriptions/client.py +192 -8
  25. lark/subscriptions/raw_client.py +254 -4
  26. lark/types/__init__.py +31 -32
  27. lark/types/aggregation.py +1 -43
  28. lark/types/{create_fixed_rate_interface.py → create_fixed_rate_request.py} +1 -1
  29. lark/types/{create_simple_usage_based_rate_interface.py → create_simple_usage_based_rate_request.py} +5 -3
  30. lark/types/create_subject_response.py +29 -6
  31. lark/types/{custom_pricing_metric_resource.py → invoice_line_item_resource.py} +6 -2
  32. lark/types/invoice_resource.py +31 -0
  33. lark/types/invoice_status.py +5 -0
  34. lark/types/{max_aggregation_pricing_metric_resource.py → list_invoices_response.py} +4 -2
  35. lark/types/list_pricing_metrics_response.py +21 -0
  36. lark/types/period_resource.py +23 -0
  37. lark/types/{last_aggregation_pricing_metric_resource.py → pricing_metric_resource.py} +7 -2
  38. lark/types/rate_card_resource_usage_based_rates_item.py +0 -1
  39. lark/types/simple_usage_based_rate_interface.py +0 -5
  40. lark/types/subject_resource.py +29 -6
  41. lark/types/subscription_resource.py +6 -3
  42. lark/types/subscription_status.py +5 -0
  43. {lark_billing-0.0.7.dist-info → lark_billing-0.0.8.dist-info}/METADATA +4 -3
  44. {lark_billing-0.0.7.dist-info → lark_billing-0.0.8.dist-info}/RECORD +45 -32
  45. lark/types/status.py +0 -5
  46. {lark_billing-0.0.7.dist-info → lark_billing-0.0.8.dist-info}/WHEEL +0 -0
lark/__init__.py CHANGED
@@ -9,46 +9,47 @@ if typing.TYPE_CHECKING:
9
9
  from .types import (
10
10
  Aggregation,
11
11
  Aggregation_Count,
12
- Aggregation_Custom,
13
- Aggregation_Last,
14
- Aggregation_Max,
15
12
  Aggregation_Sum,
16
13
  Amount,
17
14
  BillingStateResponse,
18
15
  CountAggregationPricingMetricInterface,
19
16
  CountAggregationPricingMetricResource,
20
17
  CreateCustomerPortalSessionResponse,
21
- CreateFixedRateInterface,
18
+ CreateFixedRateRequest,
22
19
  CreatePricingMetricResponse,
23
- CreateSimpleUsageBasedRateInterface,
20
+ CreateSimpleUsageBasedRateRequest,
24
21
  CreateSubjectResponse,
25
22
  CreateSubscriptionCheckoutSessionResponse,
26
23
  CreateUsageEventSummaryResponse,
27
- CustomPricingMetricResource,
28
24
  FixedRateInterface,
29
25
  FlatPrice,
30
26
  GetPricingMetricResponse,
31
27
  HttpValidationError,
32
- LastAggregationPricingMetricResource,
28
+ InvoiceLineItemResource,
29
+ InvoiceResource,
30
+ InvoiceStatus,
31
+ ListInvoicesResponse,
32
+ ListPricingMetricsResponse,
33
33
  ListRateCardsResponse,
34
34
  ListSubjectsResponse,
35
35
  ListSubscriptionsResponse,
36
- MaxAggregationPricingMetricResource,
37
36
  PackagePrice,
38
37
  PackagePriceInputRoundingBehavior,
39
38
  PackagePriceOutputRoundingBehavior,
40
39
  Period,
40
+ PeriodResource,
41
41
  Price,
42
42
  Price_Flat,
43
43
  Price_Package,
44
+ PricingMetricResource,
44
45
  RateCardResource,
45
46
  RateCardResourceBillingInterval,
46
47
  RateCardResourceUsageBasedRatesItem,
47
48
  RateCardResourceUsageBasedRatesItem_Simple,
48
49
  SimpleUsageBasedRateInterface,
49
- Status,
50
50
  SubjectResource,
51
51
  SubscriptionResource,
52
+ SubscriptionStatus,
52
53
  SumAggregationPricingMetricInterface,
53
54
  SumAggregationPricingMetricResource,
54
55
  ValidationError,
@@ -60,6 +61,7 @@ if typing.TYPE_CHECKING:
60
61
  checkout,
61
62
  customer_access,
62
63
  customer_portal,
64
+ invoices,
63
65
  pricing_metrics,
64
66
  rate_cards,
65
67
  subjects,
@@ -69,15 +71,16 @@ if typing.TYPE_CHECKING:
69
71
  from .client import AsyncLark, Lark
70
72
  from .environment import LarkEnvironment
71
73
  from .pricing_metrics import PricingMetricAggregation, PricingMetricAggregation_Count, PricingMetricAggregation_Sum
72
- from .rate_cards import CreateRateCardRequestBillingInterval
74
+ from .rate_cards import (
75
+ CreateRateCardRequestBillingInterval,
76
+ CreateRateCardRequestUsageBasedRatesItem,
77
+ CreateRateCardRequestUsageBasedRatesItem_Simple,
78
+ )
73
79
  from .usage_events import CreateUsageEventSummaryRequestAggregationType
74
80
  from .version import __version__
75
81
  _dynamic_imports: typing.Dict[str, str] = {
76
82
  "Aggregation": ".types",
77
83
  "Aggregation_Count": ".types",
78
- "Aggregation_Custom": ".types",
79
- "Aggregation_Last": ".types",
80
- "Aggregation_Max": ".types",
81
84
  "Aggregation_Sum": ".types",
82
85
  "Amount": ".types",
83
86
  "AsyncLark": ".client",
@@ -85,44 +88,50 @@ _dynamic_imports: typing.Dict[str, str] = {
85
88
  "CountAggregationPricingMetricInterface": ".types",
86
89
  "CountAggregationPricingMetricResource": ".types",
87
90
  "CreateCustomerPortalSessionResponse": ".types",
88
- "CreateFixedRateInterface": ".types",
91
+ "CreateFixedRateRequest": ".types",
89
92
  "CreatePricingMetricResponse": ".types",
90
93
  "CreateRateCardRequestBillingInterval": ".rate_cards",
91
- "CreateSimpleUsageBasedRateInterface": ".types",
94
+ "CreateRateCardRequestUsageBasedRatesItem": ".rate_cards",
95
+ "CreateRateCardRequestUsageBasedRatesItem_Simple": ".rate_cards",
96
+ "CreateSimpleUsageBasedRateRequest": ".types",
92
97
  "CreateSubjectResponse": ".types",
93
98
  "CreateSubscriptionCheckoutSessionResponse": ".types",
94
99
  "CreateUsageEventSummaryRequestAggregationType": ".usage_events",
95
100
  "CreateUsageEventSummaryResponse": ".types",
96
- "CustomPricingMetricResource": ".types",
97
101
  "FixedRateInterface": ".types",
98
102
  "FlatPrice": ".types",
99
103
  "GetPricingMetricResponse": ".types",
100
104
  "HttpValidationError": ".types",
105
+ "InvoiceLineItemResource": ".types",
106
+ "InvoiceResource": ".types",
107
+ "InvoiceStatus": ".types",
101
108
  "Lark": ".client",
102
109
  "LarkEnvironment": ".environment",
103
- "LastAggregationPricingMetricResource": ".types",
110
+ "ListInvoicesResponse": ".types",
111
+ "ListPricingMetricsResponse": ".types",
104
112
  "ListRateCardsResponse": ".types",
105
113
  "ListSubjectsResponse": ".types",
106
114
  "ListSubscriptionsResponse": ".types",
107
- "MaxAggregationPricingMetricResource": ".types",
108
115
  "PackagePrice": ".types",
109
116
  "PackagePriceInputRoundingBehavior": ".types",
110
117
  "PackagePriceOutputRoundingBehavior": ".types",
111
118
  "Period": ".types",
119
+ "PeriodResource": ".types",
112
120
  "Price": ".types",
113
121
  "Price_Flat": ".types",
114
122
  "Price_Package": ".types",
115
123
  "PricingMetricAggregation": ".pricing_metrics",
116
124
  "PricingMetricAggregation_Count": ".pricing_metrics",
117
125
  "PricingMetricAggregation_Sum": ".pricing_metrics",
126
+ "PricingMetricResource": ".types",
118
127
  "RateCardResource": ".types",
119
128
  "RateCardResourceBillingInterval": ".types",
120
129
  "RateCardResourceUsageBasedRatesItem": ".types",
121
130
  "RateCardResourceUsageBasedRatesItem_Simple": ".types",
122
131
  "SimpleUsageBasedRateInterface": ".types",
123
- "Status": ".types",
124
132
  "SubjectResource": ".types",
125
133
  "SubscriptionResource": ".types",
134
+ "SubscriptionStatus": ".types",
126
135
  "SumAggregationPricingMetricInterface": ".types",
127
136
  "SumAggregationPricingMetricResource": ".types",
128
137
  "UnprocessableEntityError": ".errors",
@@ -133,6 +142,7 @@ _dynamic_imports: typing.Dict[str, str] = {
133
142
  "checkout": ".checkout",
134
143
  "customer_access": ".customer_access",
135
144
  "customer_portal": ".customer_portal",
145
+ "invoices": ".invoices",
136
146
  "pricing_metrics": ".pricing_metrics",
137
147
  "rate_cards": ".rate_cards",
138
148
  "subjects": ".subjects",
@@ -165,9 +175,6 @@ def __dir__():
165
175
  __all__ = [
166
176
  "Aggregation",
167
177
  "Aggregation_Count",
168
- "Aggregation_Custom",
169
- "Aggregation_Last",
170
- "Aggregation_Max",
171
178
  "Aggregation_Sum",
172
179
  "Amount",
173
180
  "AsyncLark",
@@ -175,44 +182,50 @@ __all__ = [
175
182
  "CountAggregationPricingMetricInterface",
176
183
  "CountAggregationPricingMetricResource",
177
184
  "CreateCustomerPortalSessionResponse",
178
- "CreateFixedRateInterface",
185
+ "CreateFixedRateRequest",
179
186
  "CreatePricingMetricResponse",
180
187
  "CreateRateCardRequestBillingInterval",
181
- "CreateSimpleUsageBasedRateInterface",
188
+ "CreateRateCardRequestUsageBasedRatesItem",
189
+ "CreateRateCardRequestUsageBasedRatesItem_Simple",
190
+ "CreateSimpleUsageBasedRateRequest",
182
191
  "CreateSubjectResponse",
183
192
  "CreateSubscriptionCheckoutSessionResponse",
184
193
  "CreateUsageEventSummaryRequestAggregationType",
185
194
  "CreateUsageEventSummaryResponse",
186
- "CustomPricingMetricResource",
187
195
  "FixedRateInterface",
188
196
  "FlatPrice",
189
197
  "GetPricingMetricResponse",
190
198
  "HttpValidationError",
199
+ "InvoiceLineItemResource",
200
+ "InvoiceResource",
201
+ "InvoiceStatus",
191
202
  "Lark",
192
203
  "LarkEnvironment",
193
- "LastAggregationPricingMetricResource",
204
+ "ListInvoicesResponse",
205
+ "ListPricingMetricsResponse",
194
206
  "ListRateCardsResponse",
195
207
  "ListSubjectsResponse",
196
208
  "ListSubscriptionsResponse",
197
- "MaxAggregationPricingMetricResource",
198
209
  "PackagePrice",
199
210
  "PackagePriceInputRoundingBehavior",
200
211
  "PackagePriceOutputRoundingBehavior",
201
212
  "Period",
213
+ "PeriodResource",
202
214
  "Price",
203
215
  "Price_Flat",
204
216
  "Price_Package",
205
217
  "PricingMetricAggregation",
206
218
  "PricingMetricAggregation_Count",
207
219
  "PricingMetricAggregation_Sum",
220
+ "PricingMetricResource",
208
221
  "RateCardResource",
209
222
  "RateCardResourceBillingInterval",
210
223
  "RateCardResourceUsageBasedRatesItem",
211
224
  "RateCardResourceUsageBasedRatesItem_Simple",
212
225
  "SimpleUsageBasedRateInterface",
213
- "Status",
214
226
  "SubjectResource",
215
227
  "SubscriptionResource",
228
+ "SubscriptionStatus",
216
229
  "SumAggregationPricingMetricInterface",
217
230
  "SumAggregationPricingMetricResource",
218
231
  "UnprocessableEntityError",
@@ -223,6 +236,7 @@ __all__ = [
223
236
  "checkout",
224
237
  "customer_access",
225
238
  "customer_portal",
239
+ "invoices",
226
240
  "pricing_metrics",
227
241
  "rate_cards",
228
242
  "subjects",
lark/checkout/client.py CHANGED
@@ -31,7 +31,8 @@ class CheckoutClient:
31
31
  *,
32
32
  subject_id: str,
33
33
  rate_card_id: str,
34
- callback_url: str,
34
+ success_callback_url: str,
35
+ cancel_url: typing.Optional[str] = OMIT,
35
36
  request_options: typing.Optional[RequestOptions] = None,
36
37
  ) -> CreateSubscriptionCheckoutSessionResponse:
37
38
  """
@@ -41,7 +42,11 @@ class CheckoutClient:
41
42
 
42
43
  rate_card_id : str
43
44
 
44
- callback_url : str
45
+ success_callback_url : str
46
+ The URL to redirect to after the checkout is successful.
47
+
48
+ cancel_url : typing.Optional[str]
49
+ The URL to redirect to after the checkout is cancelled.
45
50
 
46
51
  request_options : typing.Optional[RequestOptions]
47
52
  Request-specific configuration.
@@ -61,11 +66,15 @@ class CheckoutClient:
61
66
  client.checkout.create_subscription_checkout_session(
62
67
  subject_id="subject_id",
63
68
  rate_card_id="rate_card_id",
64
- callback_url="callback_url",
69
+ success_callback_url="https://example.com/callback",
65
70
  )
66
71
  """
67
72
  _response = self._raw_client.create_subscription_checkout_session(
68
- subject_id=subject_id, rate_card_id=rate_card_id, callback_url=callback_url, request_options=request_options
73
+ subject_id=subject_id,
74
+ rate_card_id=rate_card_id,
75
+ success_callback_url=success_callback_url,
76
+ cancel_url=cancel_url,
77
+ request_options=request_options,
69
78
  )
70
79
  return _response.data
71
80
 
@@ -90,7 +99,8 @@ class AsyncCheckoutClient:
90
99
  *,
91
100
  subject_id: str,
92
101
  rate_card_id: str,
93
- callback_url: str,
102
+ success_callback_url: str,
103
+ cancel_url: typing.Optional[str] = OMIT,
94
104
  request_options: typing.Optional[RequestOptions] = None,
95
105
  ) -> CreateSubscriptionCheckoutSessionResponse:
96
106
  """
@@ -100,7 +110,11 @@ class AsyncCheckoutClient:
100
110
 
101
111
  rate_card_id : str
102
112
 
103
- callback_url : str
113
+ success_callback_url : str
114
+ The URL to redirect to after the checkout is successful.
115
+
116
+ cancel_url : typing.Optional[str]
117
+ The URL to redirect to after the checkout is cancelled.
104
118
 
105
119
  request_options : typing.Optional[RequestOptions]
106
120
  Request-specific configuration.
@@ -125,13 +139,17 @@ class AsyncCheckoutClient:
125
139
  await client.checkout.create_subscription_checkout_session(
126
140
  subject_id="subject_id",
127
141
  rate_card_id="rate_card_id",
128
- callback_url="callback_url",
142
+ success_callback_url="https://example.com/callback",
129
143
  )
130
144
 
131
145
 
132
146
  asyncio.run(main())
133
147
  """
134
148
  _response = await self._raw_client.create_subscription_checkout_session(
135
- subject_id=subject_id, rate_card_id=rate_card_id, callback_url=callback_url, request_options=request_options
149
+ subject_id=subject_id,
150
+ rate_card_id=rate_card_id,
151
+ success_callback_url=success_callback_url,
152
+ cancel_url=cancel_url,
153
+ request_options=request_options,
136
154
  )
137
155
  return _response.data
@@ -25,7 +25,8 @@ class RawCheckoutClient:
25
25
  *,
26
26
  subject_id: str,
27
27
  rate_card_id: str,
28
- callback_url: str,
28
+ success_callback_url: str,
29
+ cancel_url: typing.Optional[str] = OMIT,
29
30
  request_options: typing.Optional[RequestOptions] = None,
30
31
  ) -> HttpResponse[CreateSubscriptionCheckoutSessionResponse]:
31
32
  """
@@ -35,7 +36,11 @@ class RawCheckoutClient:
35
36
 
36
37
  rate_card_id : str
37
38
 
38
- callback_url : str
39
+ success_callback_url : str
40
+ The URL to redirect to after the checkout is successful.
41
+
42
+ cancel_url : typing.Optional[str]
43
+ The URL to redirect to after the checkout is cancelled.
39
44
 
40
45
  request_options : typing.Optional[RequestOptions]
41
46
  Request-specific configuration.
@@ -51,7 +56,8 @@ class RawCheckoutClient:
51
56
  json={
52
57
  "subject_id": subject_id,
53
58
  "rate_card_id": rate_card_id,
54
- "callback_url": callback_url,
59
+ "success_callback_url": success_callback_url,
60
+ "cancel_url": cancel_url,
55
61
  },
56
62
  headers={
57
63
  "content-type": "application/json",
@@ -95,7 +101,8 @@ class AsyncRawCheckoutClient:
95
101
  *,
96
102
  subject_id: str,
97
103
  rate_card_id: str,
98
- callback_url: str,
104
+ success_callback_url: str,
105
+ cancel_url: typing.Optional[str] = OMIT,
99
106
  request_options: typing.Optional[RequestOptions] = None,
100
107
  ) -> AsyncHttpResponse[CreateSubscriptionCheckoutSessionResponse]:
101
108
  """
@@ -105,7 +112,11 @@ class AsyncRawCheckoutClient:
105
112
 
106
113
  rate_card_id : str
107
114
 
108
- callback_url : str
115
+ success_callback_url : str
116
+ The URL to redirect to after the checkout is successful.
117
+
118
+ cancel_url : typing.Optional[str]
119
+ The URL to redirect to after the checkout is cancelled.
109
120
 
110
121
  request_options : typing.Optional[RequestOptions]
111
122
  Request-specific configuration.
@@ -121,7 +132,8 @@ class AsyncRawCheckoutClient:
121
132
  json={
122
133
  "subject_id": subject_id,
123
134
  "rate_card_id": rate_card_id,
124
- "callback_url": callback_url,
135
+ "success_callback_url": success_callback_url,
136
+ "cancel_url": cancel_url,
125
137
  },
126
138
  headers={
127
139
  "content-type": "application/json",
lark/client.py CHANGED
@@ -12,6 +12,7 @@ if typing.TYPE_CHECKING:
12
12
  from .checkout.client import AsyncCheckoutClient, CheckoutClient
13
13
  from .customer_access.client import AsyncCustomerAccessClient, CustomerAccessClient
14
14
  from .customer_portal.client import AsyncCustomerPortalClient, CustomerPortalClient
15
+ from .invoices.client import AsyncInvoicesClient, InvoicesClient
15
16
  from .pricing_metrics.client import AsyncPricingMetricsClient, PricingMetricsClient
16
17
  from .rate_cards.client import AsyncRateCardsClient, RateCardsClient
17
18
  from .subjects.client import AsyncSubjectsClient, SubjectsClient
@@ -92,6 +93,7 @@ class Lark:
92
93
  self._subjects: typing.Optional[SubjectsClient] = None
93
94
  self._pricing_metrics: typing.Optional[PricingMetricsClient] = None
94
95
  self._customer_access: typing.Optional[CustomerAccessClient] = None
96
+ self._invoices: typing.Optional[InvoicesClient] = None
95
97
 
96
98
  @property
97
99
  def checkout(self):
@@ -157,6 +159,14 @@ class Lark:
157
159
  self._customer_access = CustomerAccessClient(client_wrapper=self._client_wrapper)
158
160
  return self._customer_access
159
161
 
162
+ @property
163
+ def invoices(self):
164
+ if self._invoices is None:
165
+ from .invoices.client import InvoicesClient # noqa: E402
166
+
167
+ self._invoices = InvoicesClient(client_wrapper=self._client_wrapper)
168
+ return self._invoices
169
+
160
170
 
161
171
  class AsyncLark:
162
172
  """
@@ -231,6 +241,7 @@ class AsyncLark:
231
241
  self._subjects: typing.Optional[AsyncSubjectsClient] = None
232
242
  self._pricing_metrics: typing.Optional[AsyncPricingMetricsClient] = None
233
243
  self._customer_access: typing.Optional[AsyncCustomerAccessClient] = None
244
+ self._invoices: typing.Optional[AsyncInvoicesClient] = None
234
245
 
235
246
  @property
236
247
  def checkout(self):
@@ -296,6 +307,14 @@ class AsyncLark:
296
307
  self._customer_access = AsyncCustomerAccessClient(client_wrapper=self._client_wrapper)
297
308
  return self._customer_access
298
309
 
310
+ @property
311
+ def invoices(self):
312
+ if self._invoices is None:
313
+ from .invoices.client import AsyncInvoicesClient # noqa: E402
314
+
315
+ self._invoices = AsyncInvoicesClient(client_wrapper=self._client_wrapper)
316
+ return self._invoices
317
+
299
318
 
300
319
  def _get_base_url(*, base_url: typing.Optional[str] = None, environment: LarkEnvironment) -> str:
301
320
  if base_url is not None:
@@ -22,10 +22,10 @@ class BaseClientWrapper:
22
22
 
23
23
  def get_headers(self) -> typing.Dict[str, str]:
24
24
  headers: typing.Dict[str, str] = {
25
- "User-Agent": "lark-billing/0.0.7",
25
+ "User-Agent": "lark-billing/0.0.8",
26
26
  "X-Fern-Language": "Python",
27
27
  "X-Fern-SDK-Name": "lark-billing",
28
- "X-Fern-SDK-Version": "0.0.7",
28
+ "X-Fern-SDK-Version": "0.0.8",
29
29
  **(self.get_custom_headers() or {}),
30
30
  }
31
31
  headers["X-API-Key"] = self.api_key
@@ -0,0 +1,42 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ # isort: skip_file
4
+
5
+ import typing
6
+ from importlib import import_module
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from ._api import EventSource, aconnect_sse, connect_sse
10
+ from ._exceptions import SSEError
11
+ from ._models import ServerSentEvent
12
+ _dynamic_imports: typing.Dict[str, str] = {
13
+ "EventSource": "._api",
14
+ "SSEError": "._exceptions",
15
+ "ServerSentEvent": "._models",
16
+ "aconnect_sse": "._api",
17
+ "connect_sse": "._api",
18
+ }
19
+
20
+
21
+ def __getattr__(attr_name: str) -> typing.Any:
22
+ module_name = _dynamic_imports.get(attr_name)
23
+ if module_name is None:
24
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
25
+ try:
26
+ module = import_module(module_name, __package__)
27
+ if module_name == f".{attr_name}":
28
+ return module
29
+ else:
30
+ return getattr(module, attr_name)
31
+ except ImportError as e:
32
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
33
+ except AttributeError as e:
34
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
35
+
36
+
37
+ def __dir__():
38
+ lazy_attrs = list(_dynamic_imports.keys())
39
+ return sorted(lazy_attrs)
40
+
41
+
42
+ __all__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"]
@@ -0,0 +1,112 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import re
4
+ from contextlib import asynccontextmanager, contextmanager
5
+ from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast
6
+
7
+ import httpx
8
+ from ._decoders import SSEDecoder
9
+ from ._exceptions import SSEError
10
+ from ._models import ServerSentEvent
11
+
12
+
13
+ class EventSource:
14
+ def __init__(self, response: httpx.Response) -> None:
15
+ self._response = response
16
+
17
+ def _check_content_type(self) -> None:
18
+ content_type = self._response.headers.get("content-type", "").partition(";")[0]
19
+ if "text/event-stream" not in content_type:
20
+ raise SSEError(
21
+ f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}"
22
+ )
23
+
24
+ def _get_charset(self) -> str:
25
+ """Extract charset from Content-Type header, fallback to UTF-8."""
26
+ content_type = self._response.headers.get("content-type", "")
27
+
28
+ # Parse charset parameter using regex
29
+ charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE)
30
+ if charset_match:
31
+ charset = charset_match.group(1).strip("\"'")
32
+ # Validate that it's a known encoding
33
+ try:
34
+ # Test if the charset is valid by trying to encode/decode
35
+ "test".encode(charset).decode(charset)
36
+ return charset
37
+ except (LookupError, UnicodeError):
38
+ # If charset is invalid, fall back to UTF-8
39
+ pass
40
+
41
+ # Default to UTF-8 if no charset specified or invalid charset
42
+ return "utf-8"
43
+
44
+ @property
45
+ def response(self) -> httpx.Response:
46
+ return self._response
47
+
48
+ def iter_sse(self) -> Iterator[ServerSentEvent]:
49
+ self._check_content_type()
50
+ decoder = SSEDecoder()
51
+ charset = self._get_charset()
52
+
53
+ buffer = ""
54
+ for chunk in self._response.iter_bytes():
55
+ # Decode chunk using detected charset
56
+ text_chunk = chunk.decode(charset, errors="replace")
57
+ buffer += text_chunk
58
+
59
+ # Process complete lines
60
+ while "\n" in buffer:
61
+ line, buffer = buffer.split("\n", 1)
62
+ line = line.rstrip("\r")
63
+ sse = decoder.decode(line)
64
+ # when we reach a "\n\n" => line = ''
65
+ # => decoder will attempt to return an SSE Event
66
+ if sse is not None:
67
+ yield sse
68
+
69
+ # Process any remaining data in buffer
70
+ if buffer.strip():
71
+ line = buffer.rstrip("\r")
72
+ sse = decoder.decode(line)
73
+ if sse is not None:
74
+ yield sse
75
+
76
+ async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]:
77
+ self._check_content_type()
78
+ decoder = SSEDecoder()
79
+ lines = cast(AsyncGenerator[str, None], self._response.aiter_lines())
80
+ try:
81
+ async for line in lines:
82
+ line = line.rstrip("\n")
83
+ sse = decoder.decode(line)
84
+ if sse is not None:
85
+ yield sse
86
+ finally:
87
+ await lines.aclose()
88
+
89
+
90
+ @contextmanager
91
+ def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]:
92
+ headers = kwargs.pop("headers", {})
93
+ headers["Accept"] = "text/event-stream"
94
+ headers["Cache-Control"] = "no-store"
95
+
96
+ with client.stream(method, url, headers=headers, **kwargs) as response:
97
+ yield EventSource(response)
98
+
99
+
100
+ @asynccontextmanager
101
+ async def aconnect_sse(
102
+ client: httpx.AsyncClient,
103
+ method: str,
104
+ url: str,
105
+ **kwargs: Any,
106
+ ) -> AsyncIterator[EventSource]:
107
+ headers = kwargs.pop("headers", {})
108
+ headers["Accept"] = "text/event-stream"
109
+ headers["Cache-Control"] = "no-store"
110
+
111
+ async with client.stream(method, url, headers=headers, **kwargs) as response:
112
+ yield EventSource(response)
@@ -0,0 +1,61 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from typing import List, Optional
4
+
5
+ from ._models import ServerSentEvent
6
+
7
+
8
+ class SSEDecoder:
9
+ def __init__(self) -> None:
10
+ self._event = ""
11
+ self._data: List[str] = []
12
+ self._last_event_id = ""
13
+ self._retry: Optional[int] = None
14
+
15
+ def decode(self, line: str) -> Optional[ServerSentEvent]:
16
+ # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501
17
+
18
+ if not line:
19
+ if not self._event and not self._data and not self._last_event_id and self._retry is None:
20
+ return None
21
+
22
+ sse = ServerSentEvent(
23
+ event=self._event,
24
+ data="\n".join(self._data),
25
+ id=self._last_event_id,
26
+ retry=self._retry,
27
+ )
28
+
29
+ # NOTE: as per the SSE spec, do not reset last_event_id.
30
+ self._event = ""
31
+ self._data = []
32
+ self._retry = None
33
+
34
+ return sse
35
+
36
+ if line.startswith(":"):
37
+ return None
38
+
39
+ fieldname, _, value = line.partition(":")
40
+
41
+ if value.startswith(" "):
42
+ value = value[1:]
43
+
44
+ if fieldname == "event":
45
+ self._event = value
46
+ elif fieldname == "data":
47
+ self._data.append(value)
48
+ elif fieldname == "id":
49
+ if "\0" in value:
50
+ pass
51
+ else:
52
+ self._last_event_id = value
53
+ elif fieldname == "retry":
54
+ try:
55
+ self._retry = int(value)
56
+ except (TypeError, ValueError):
57
+ pass
58
+ else:
59
+ pass # Field is ignored.
60
+
61
+ return None
@@ -0,0 +1,7 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import httpx
4
+
5
+
6
+ class SSEError(httpx.TransportError):
7
+ pass
@@ -0,0 +1,17 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from typing import Any, Optional
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class ServerSentEvent:
10
+ event: str = "message"
11
+ data: str = ""
12
+ id: str = ""
13
+ retry: Optional[int] = None
14
+
15
+ def json(self) -> Any:
16
+ """Parse the data field as JSON."""
17
+ return json.loads(self.data)