enlitt 0.1.0__tar.gz

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.
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ dist/
3
+ __pycache__/
4
+ .pytest_cache/
5
+ .mypy_cache/
6
+ .ruff_cache/
enlitt-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.5
2
+ Name: enlitt
3
+ Version: 0.1.0
4
+ Summary: Enlitt SDK for Python: per-user model calls and premium-call accounting for apps whose users bring their own credits.
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: httpx<1,>=0.27
8
+ Provides-Extra: openai
9
+ Requires-Dist: openai<3,>=1.66; extra == 'openai'
10
+ Provides-Extra: starlette
11
+ Requires-Dist: starlette<1,>=0.37; extra == 'starlette'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # enlitt (Python SDK)
15
+
16
+ Server-side Enlitt SDK for Python (asyncio). Behavior is specified in `specs/SDK-side routing.md`
17
+ ("Python SDK") and `specs/Account.md`.
enlitt-0.1.0/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # enlitt (Python SDK)
2
+
3
+ Server-side Enlitt SDK for Python (asyncio). Behavior is specified in `specs/SDK-side routing.md`
4
+ ("Python SDK") and `specs/Account.md`.
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "enlitt"
7
+ version = "0.1.0"
8
+ description = "Enlitt SDK for Python: per-user model calls and premium-call accounting for apps whose users bring their own credits."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ dependencies = ["httpx>=0.27,<1"]
13
+
14
+ [project.optional-dependencies]
15
+ openai = ["openai>=1.66,<3"]
16
+ starlette = ["starlette>=0.37,<1"]
17
+
18
+ [dependency-groups]
19
+ dev = [
20
+ "pytest",
21
+ "pytest-asyncio",
22
+ "respx",
23
+ "ruff",
24
+ "mypy",
25
+ "openai>=1.66,<3",
26
+ "starlette>=0.37,<1",
27
+ "httpx>=0.27,<1",
28
+ ]
29
+
30
+ [tool.hatch.build.targets.wheel]
31
+ packages = ["src/enlitt"]
32
+
33
+ [tool.hatch.build.targets.sdist]
34
+ include = ["src/enlitt", "README.md", "pyproject.toml"]
35
+
36
+ [tool.pytest.ini_options]
37
+ asyncio_mode = "auto"
38
+ asyncio_default_fixture_loop_scope = "function"
39
+ testpaths = ["tests"]
40
+ addopts = "-ra"
41
+
42
+ [tool.ruff]
43
+ line-length = 100
44
+ target-version = "py310"
45
+ src = ["src", "tests"]
46
+
47
+ [tool.ruff.lint]
48
+ select = ["E", "F", "I", "B", "UP", "SIM", "RUF"]
49
+ ignore = ["RUF012"]
50
+
51
+ [tool.mypy]
52
+ strict = true
53
+ python_version = "3.10"
54
+ files = ["src", "tests"]
55
+ mypy_path = "src"
@@ -0,0 +1,67 @@
1
+ """Enlitt SDK for Python: per-user model calls and premium-call accounting.
2
+
3
+ The core entry points are re-exported here. The OpenAI adapter lives in :mod:`enlitt.openai`,
4
+ the account handlers in :mod:`enlitt.auth` and :mod:`enlitt.client_sign_in`, and the ASGI and
5
+ Starlette helpers in :mod:`enlitt.asgi` and :mod:`enlitt.starlette`.
6
+ """
7
+
8
+ from ._transport import EnlittOptions, EnlittRateLimitRetryOptions
9
+ from ._version import SDK_VERSION
10
+ from .errors import (
11
+ EnlittAuthenticationError,
12
+ EnlittAuthorizationError,
13
+ EnlittConfigurationError,
14
+ EnlittError,
15
+ EnlittInsufficientCreditsError,
16
+ EnlittLimitExceededError,
17
+ EnlittLoginRequiredError,
18
+ EnlittRateLimitError,
19
+ EnlittSubscriptionInactiveError,
20
+ )
21
+ from .premium import (
22
+ PremiumChargeFunction,
23
+ PremiumChargeHandle,
24
+ PremiumProvider,
25
+ PremiumTerminalFacts,
26
+ WithPremiumChargeOptions,
27
+ with_premium_charge,
28
+ )
29
+ from .session import (
30
+ ENLITT_SESSION_COOKIE_NAME,
31
+ ENLITT_SESSION_HEADER_NAME,
32
+ EnlittSession,
33
+ enlitt_session,
34
+ get_enlitt_session,
35
+ run_with_enlitt_session,
36
+ )
37
+ from .usage import UsageAvailability, UsageResult, usage
38
+
39
+ __all__ = [
40
+ "ENLITT_SESSION_COOKIE_NAME",
41
+ "ENLITT_SESSION_HEADER_NAME",
42
+ "SDK_VERSION",
43
+ "EnlittAuthenticationError",
44
+ "EnlittAuthorizationError",
45
+ "EnlittConfigurationError",
46
+ "EnlittError",
47
+ "EnlittInsufficientCreditsError",
48
+ "EnlittLimitExceededError",
49
+ "EnlittLoginRequiredError",
50
+ "EnlittOptions",
51
+ "EnlittRateLimitError",
52
+ "EnlittRateLimitRetryOptions",
53
+ "EnlittSession",
54
+ "EnlittSubscriptionInactiveError",
55
+ "PremiumChargeFunction",
56
+ "PremiumChargeHandle",
57
+ "PremiumProvider",
58
+ "PremiumTerminalFacts",
59
+ "UsageAvailability",
60
+ "UsageResult",
61
+ "WithPremiumChargeOptions",
62
+ "enlitt_session",
63
+ "get_enlitt_session",
64
+ "run_with_enlitt_session",
65
+ "usage",
66
+ "with_premium_charge",
67
+ ]
@@ -0,0 +1,491 @@
1
+ {
2
+ "contract_format": 1,
3
+ "api_version": "2026-10-05",
4
+ "version_header": "Enlitt-Version",
5
+ "unknown_enum_value": "other",
6
+ "unrecognized_error_code": "unrecognized",
7
+ "idempotency_key": {
8
+ "min_length": 16,
9
+ "max_length": 255,
10
+ "min_retention_days": 30
11
+ },
12
+ "error_types": [
13
+ "authentication_error",
14
+ "permission_error",
15
+ "invalid_request_error",
16
+ "insufficient_credits_error",
17
+ "rate_limit_error",
18
+ "conflict_error",
19
+ "server_error"
20
+ ],
21
+ "error_codes": {
22
+ "access_denied": {
23
+ "type": "invalid_request_error",
24
+ "http_status": 400,
25
+ "retryable": false,
26
+ "retry_after": "forbidden",
27
+ "authorize_url": "forbidden",
28
+ "admissible_max_charge": "forbidden",
29
+ "limit_rule": "forbidden"
30
+ },
31
+ "admin_authentication_required": {
32
+ "type": "authentication_error",
33
+ "http_status": 401,
34
+ "retryable": false,
35
+ "retry_after": "forbidden",
36
+ "authorize_url": "forbidden",
37
+ "admissible_max_charge": "forbidden",
38
+ "limit_rule": "forbidden"
39
+ },
40
+ "authorization_pending": {
41
+ "type": "invalid_request_error",
42
+ "http_status": 400,
43
+ "retryable": false,
44
+ "retry_after": "forbidden",
45
+ "authorize_url": "forbidden",
46
+ "admissible_max_charge": "forbidden",
47
+ "limit_rule": "forbidden"
48
+ },
49
+ "bot_verification_failed": {
50
+ "type": "permission_error",
51
+ "http_status": 403,
52
+ "retryable": false,
53
+ "retry_after": "forbidden",
54
+ "authorize_url": "forbidden",
55
+ "admissible_max_charge": "forbidden",
56
+ "limit_rule": "forbidden"
57
+ },
58
+ "business_name_unavailable": {
59
+ "type": "conflict_error",
60
+ "http_status": 409,
61
+ "retryable": false,
62
+ "retry_after": "forbidden",
63
+ "authorize_url": "forbidden",
64
+ "admissible_max_charge": "forbidden",
65
+ "limit_rule": "forbidden"
66
+ },
67
+ "business_selection_required": {
68
+ "type": "invalid_request_error",
69
+ "http_status": 409,
70
+ "retryable": false,
71
+ "retry_after": "forbidden",
72
+ "authorize_url": "forbidden",
73
+ "admissible_max_charge": "forbidden",
74
+ "limit_rule": "forbidden"
75
+ },
76
+ "conflict_error": {
77
+ "type": "conflict_error",
78
+ "http_status": 409,
79
+ "retryable": false,
80
+ "retry_after": "forbidden",
81
+ "authorize_url": "forbidden",
82
+ "admissible_max_charge": "forbidden",
83
+ "limit_rule": "forbidden"
84
+ },
85
+ "dashboard_login_required": {
86
+ "type": "authentication_error",
87
+ "http_status": 401,
88
+ "retryable": false,
89
+ "retry_after": "forbidden",
90
+ "authorize_url": "forbidden",
91
+ "admissible_max_charge": "forbidden",
92
+ "limit_rule": "forbidden"
93
+ },
94
+ "expired_token": {
95
+ "type": "invalid_request_error",
96
+ "http_status": 400,
97
+ "retryable": false,
98
+ "retry_after": "forbidden",
99
+ "authorize_url": "forbidden",
100
+ "admissible_max_charge": "forbidden",
101
+ "limit_rule": "forbidden"
102
+ },
103
+ "idempotency_replay_unavailable": {
104
+ "type": "conflict_error",
105
+ "http_status": 409,
106
+ "retryable": false,
107
+ "retry_after": "forbidden",
108
+ "authorize_url": "forbidden",
109
+ "admissible_max_charge": "forbidden",
110
+ "limit_rule": "forbidden"
111
+ },
112
+ "insufficient_permissions": {
113
+ "type": "permission_error",
114
+ "http_status": 403,
115
+ "retryable": false,
116
+ "retry_after": "forbidden",
117
+ "authorize_url": "forbidden",
118
+ "admissible_max_charge": "forbidden",
119
+ "limit_rule": "forbidden"
120
+ },
121
+ "invalid_api_key": {
122
+ "type": "authentication_error",
123
+ "http_status": 401,
124
+ "retryable": false,
125
+ "retry_after": "forbidden",
126
+ "authorize_url": "forbidden",
127
+ "admissible_max_charge": "forbidden",
128
+ "limit_rule": "forbidden"
129
+ },
130
+ "invalid_phone_code": {
131
+ "type": "invalid_request_error",
132
+ "http_status": 400,
133
+ "retryable": false,
134
+ "retry_after": "forbidden",
135
+ "authorize_url": "forbidden",
136
+ "admissible_max_charge": "forbidden",
137
+ "limit_rule": "forbidden"
138
+ },
139
+ "invalid_sign_in_code": {
140
+ "type": "invalid_request_error",
141
+ "http_status": 400,
142
+ "retryable": false,
143
+ "retry_after": "forbidden",
144
+ "authorize_url": "forbidden",
145
+ "admissible_max_charge": "forbidden",
146
+ "limit_rule": "forbidden"
147
+ },
148
+ "invalid_value": {
149
+ "type": "invalid_request_error",
150
+ "http_status": 400,
151
+ "retryable": false,
152
+ "retry_after": "forbidden",
153
+ "authorize_url": "forbidden",
154
+ "admissible_max_charge": "forbidden",
155
+ "limit_rule": "forbidden"
156
+ },
157
+ "not_found": {
158
+ "type": "invalid_request_error",
159
+ "http_status": 404,
160
+ "retryable": false,
161
+ "retry_after": "forbidden",
162
+ "authorize_url": "forbidden",
163
+ "admissible_max_charge": "forbidden",
164
+ "limit_rule": "forbidden"
165
+ },
166
+ "operation_in_progress": {
167
+ "type": "conflict_error",
168
+ "http_status": 409,
169
+ "retryable": false,
170
+ "retry_after": "forbidden",
171
+ "authorize_url": "forbidden",
172
+ "admissible_max_charge": "forbidden",
173
+ "limit_rule": "forbidden"
174
+ },
175
+ "payment_declined": {
176
+ "type": "invalid_request_error",
177
+ "http_status": 402,
178
+ "retryable": false,
179
+ "retry_after": "forbidden",
180
+ "authorize_url": "forbidden",
181
+ "admissible_max_charge": "forbidden",
182
+ "limit_rule": "forbidden"
183
+ },
184
+ "personal_login_required": {
185
+ "type": "authentication_error",
186
+ "http_status": 401,
187
+ "retryable": false,
188
+ "retry_after": "forbidden",
189
+ "authorize_url": "forbidden",
190
+ "admissible_max_charge": "forbidden",
191
+ "limit_rule": "forbidden"
192
+ },
193
+ "phone_number_not_supported": {
194
+ "type": "invalid_request_error",
195
+ "http_status": 400,
196
+ "retryable": false,
197
+ "retry_after": "forbidden",
198
+ "authorize_url": "forbidden",
199
+ "admissible_max_charge": "forbidden",
200
+ "limit_rule": "forbidden"
201
+ },
202
+ "phone_number_unavailable": {
203
+ "type": "conflict_error",
204
+ "http_status": 409,
205
+ "retryable": false,
206
+ "retry_after": "forbidden",
207
+ "authorize_url": "forbidden",
208
+ "admissible_max_charge": "forbidden",
209
+ "limit_rule": "forbidden"
210
+ },
211
+ "phone_verification_required": {
212
+ "type": "permission_error",
213
+ "http_status": 403,
214
+ "retryable": false,
215
+ "retry_after": "forbidden",
216
+ "authorize_url": "forbidden",
217
+ "admissible_max_charge": "forbidden",
218
+ "limit_rule": "forbidden"
219
+ },
220
+ "premium_balance_exhausted": {
221
+ "type": "insufficient_credits_error",
222
+ "http_status": 403,
223
+ "retryable": false,
224
+ "retry_after": "forbidden",
225
+ "authorize_url": "allowed",
226
+ "admissible_max_charge": "allowed",
227
+ "limit_rule": "forbidden"
228
+ },
229
+ "premium_not_enabled": {
230
+ "type": "permission_error",
231
+ "http_status": 403,
232
+ "retryable": false,
233
+ "retry_after": "forbidden",
234
+ "authorize_url": "forbidden",
235
+ "admissible_max_charge": "forbidden",
236
+ "limit_rule": "forbidden"
237
+ },
238
+ "provider_error": {
239
+ "type": "server_error",
240
+ "http_status": 502,
241
+ "retryable": true,
242
+ "retry_after": "forbidden",
243
+ "authorize_url": "forbidden",
244
+ "admissible_max_charge": "forbidden",
245
+ "limit_rule": "forbidden"
246
+ },
247
+ "rate_limit_exceeded": {
248
+ "type": "rate_limit_error",
249
+ "http_status": 429,
250
+ "retryable": true,
251
+ "retry_after": "required",
252
+ "authorize_url": "forbidden",
253
+ "admissible_max_charge": "forbidden",
254
+ "limit_rule": "forbidden"
255
+ },
256
+ "reauthentication_required": {
257
+ "type": "authentication_error",
258
+ "http_status": 401,
259
+ "retryable": false,
260
+ "retry_after": "forbidden",
261
+ "authorize_url": "forbidden",
262
+ "admissible_max_charge": "forbidden",
263
+ "limit_rule": "forbidden"
264
+ },
265
+ "request_too_large": {
266
+ "type": "invalid_request_error",
267
+ "http_status": 413,
268
+ "retryable": false,
269
+ "retry_after": "forbidden",
270
+ "authorize_url": "forbidden",
271
+ "admissible_max_charge": "forbidden",
272
+ "limit_rule": "forbidden"
273
+ },
274
+ "server_error": {
275
+ "type": "server_error",
276
+ "http_status": 500,
277
+ "retryable": true,
278
+ "retry_after": "forbidden",
279
+ "authorize_url": "forbidden",
280
+ "admissible_max_charge": "forbidden",
281
+ "limit_rule": "forbidden"
282
+ },
283
+ "slow_down": {
284
+ "type": "invalid_request_error",
285
+ "http_status": 400,
286
+ "retryable": false,
287
+ "retry_after": "forbidden",
288
+ "authorize_url": "forbidden",
289
+ "admissible_max_charge": "forbidden",
290
+ "limit_rule": "forbidden"
291
+ },
292
+ "spending_limit_exceeded": {
293
+ "type": "insufficient_credits_error",
294
+ "http_status": 403,
295
+ "retryable": false,
296
+ "retry_after": "forbidden",
297
+ "authorize_url": "allowed",
298
+ "admissible_max_charge": "forbidden",
299
+ "limit_rule": "required"
300
+ },
301
+ "standard_balance_exhausted": {
302
+ "type": "insufficient_credits_error",
303
+ "http_status": 403,
304
+ "retryable": false,
305
+ "retry_after": "forbidden",
306
+ "authorize_url": "allowed",
307
+ "admissible_max_charge": "forbidden",
308
+ "limit_rule": "forbidden"
309
+ },
310
+ "subscription_inactive": {
311
+ "type": "insufficient_credits_error",
312
+ "http_status": 403,
313
+ "retryable": false,
314
+ "retry_after": "forbidden",
315
+ "authorize_url": "allowed",
316
+ "admissible_max_charge": "forbidden",
317
+ "limit_rule": "forbidden"
318
+ },
319
+ "unsupported_parameter": {
320
+ "type": "invalid_request_error",
321
+ "http_status": 400,
322
+ "retryable": false,
323
+ "retry_after": "forbidden",
324
+ "authorize_url": "forbidden",
325
+ "admissible_max_charge": "forbidden",
326
+ "limit_rule": "forbidden"
327
+ },
328
+ "unsupported_value": {
329
+ "type": "invalid_request_error",
330
+ "http_status": 400,
331
+ "retryable": false,
332
+ "retry_after": "forbidden",
333
+ "authorize_url": "forbidden",
334
+ "admissible_max_charge": "forbidden",
335
+ "limit_rule": "forbidden"
336
+ }
337
+ },
338
+ "closed_sets": {
339
+ "api_key.scope": [
340
+ "model",
341
+ "reporting"
342
+ ],
343
+ "api_key.status": [
344
+ "active",
345
+ "revoked",
346
+ "expired"
347
+ ],
348
+ "application.environment": [
349
+ "test",
350
+ "live"
351
+ ],
352
+ "application.status": [
353
+ "active",
354
+ "suspended",
355
+ "closed"
356
+ ],
357
+ "cli_authorization.kind": [
358
+ "login",
359
+ "step_up"
360
+ ],
361
+ "contact.type": [
362
+ "email",
363
+ "phone"
364
+ ],
365
+ "email_sign_in.account_type": [
366
+ "user",
367
+ "business"
368
+ ],
369
+ "order.kind": [
370
+ "subscription",
371
+ "bundle"
372
+ ],
373
+ "order.refund.eligibility": [
374
+ "full",
375
+ "half",
376
+ "none"
377
+ ],
378
+ "order.refund.reason": [
379
+ "unused",
380
+ "within_usage_threshold",
381
+ "usage_threshold_exceeded",
382
+ "window_expired",
383
+ "not_active",
384
+ "gift"
385
+ ],
386
+ "order.status": [
387
+ "pending",
388
+ "active",
389
+ "refund_pending",
390
+ "refunded",
391
+ "failed",
392
+ "expired"
393
+ ],
394
+ "payout.blocked_reason": [
395
+ "payout_blocked",
396
+ "below_minimum",
397
+ "outstanding_deficit",
398
+ "tax_details_required",
399
+ "payout_account_incomplete",
400
+ "monthly_payout_taken",
401
+ "payout_pending"
402
+ ],
403
+ "payout.connected_account_status": [
404
+ "none",
405
+ "onboarding",
406
+ "restricted",
407
+ "enabled",
408
+ "rejected"
409
+ ],
410
+ "personal_application_call.credit_type": [
411
+ "standard",
412
+ "premium"
413
+ ],
414
+ "personal_application_call.sort": [
415
+ "time",
416
+ "charge"
417
+ ],
418
+ "personal_application_call.status": [
419
+ "pending",
420
+ "running",
421
+ "completed",
422
+ "failed",
423
+ "canceled",
424
+ "expired"
425
+ ],
426
+ "plan.interval": [
427
+ "month",
428
+ "year"
429
+ ],
430
+ "premium_operation.billing": [
431
+ "managed"
432
+ ],
433
+ "premium_terminal.status": [
434
+ "completed",
435
+ "failed",
436
+ "canceled"
437
+ ],
438
+ "response.incomplete_details.reason": [
439
+ "max_output_tokens"
440
+ ],
441
+ "response.output_item.status": [
442
+ "in_progress",
443
+ "completed",
444
+ "incomplete"
445
+ ],
446
+ "response.status": [
447
+ "completed",
448
+ "failed",
449
+ "in_progress",
450
+ "cancelled",
451
+ "queued",
452
+ "incomplete"
453
+ ],
454
+ "response_stream.type": [
455
+ "response.created",
456
+ "response.in_progress",
457
+ "response.output_item.added",
458
+ "response.output_item.done",
459
+ "response.content_part.added",
460
+ "response.content_part.done",
461
+ "response.output_text.delta",
462
+ "response.output_text.done",
463
+ "response.function_call_arguments.delta",
464
+ "response.function_call_arguments.done",
465
+ "response.completed",
466
+ "response.incomplete",
467
+ "response.failed"
468
+ ],
469
+ "subscription.change_mode": [
470
+ "checkout",
471
+ "change",
472
+ "locked",
473
+ "unavailable"
474
+ ],
475
+ "subscription.locked_downgrade": [
476
+ "checkout",
477
+ "change"
478
+ ],
479
+ "subscription.service_tier": [
480
+ "normal",
481
+ "reduced",
482
+ "minimum"
483
+ ],
484
+ "subscription.status": [
485
+ "active",
486
+ "past_due",
487
+ "canceled",
488
+ "suspended"
489
+ ]
490
+ }
491
+ }
@@ -0,0 +1,34 @@
1
+ """Loader for the vendored wire contract (``_contract.json``).
2
+
3
+ The file is a byte-identical copy of ``packages/protocol/generated/contract.json``. Every
4
+ error-code property, closed set, API version and idempotency bound the SDK needs is read from
5
+ it, never restated as a literal.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from importlib import resources
12
+ from typing import Any, cast
13
+
14
+ _RAW: bytes = resources.files("enlitt").joinpath("_contract.json").read_bytes()
15
+ CONTRACT: dict[str, Any] = cast("dict[str, Any]", json.loads(_RAW))
16
+
17
+ API_VERSION: str = CONTRACT["api_version"]
18
+ """Value sent as the ``Enlitt-Version`` header."""
19
+
20
+ VERSION_HEADER: str = CONTRACT["version_header"]
21
+ UNKNOWN_ENUM_VALUE: str = CONTRACT["unknown_enum_value"]
22
+ """The reserved value (``"other"``) an unknown closed-set member reads as."""
23
+
24
+ UNRECOGNIZED_ERROR_CODE: str = CONTRACT["unrecognized_error_code"]
25
+ IDEMPOTENCY_KEY_MIN_LENGTH: int = CONTRACT["idempotency_key"]["min_length"]
26
+ IDEMPOTENCY_KEY_MAX_LENGTH: int = CONTRACT["idempotency_key"]["max_length"]
27
+ ERROR_TYPES: list[str] = CONTRACT["error_types"]
28
+ ERROR_CODES: dict[str, dict[str, Any]] = CONTRACT["error_codes"]
29
+ CLOSED_SETS: dict[str, list[str]] = CONTRACT["closed_sets"]
30
+
31
+
32
+ def raw_contract_bytes() -> bytes:
33
+ """Return the vendored file's exact bytes (used by the staleness test)."""
34
+ return _RAW