ai-lls-lib 1.0.0__py3-none-any.whl → 1.2.0__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.
- ai_lls_lib/__init__.py +1 -1
- ai_lls_lib/auth/__init__.py +4 -0
- ai_lls_lib/auth/context_parser.py +68 -0
- ai_lls_lib/cli/__main__.py +2 -1
- ai_lls_lib/cli/commands/stripe.py +307 -0
- ai_lls_lib/cli/commands/verify.py +3 -3
- ai_lls_lib/cli/env_loader.py +122 -0
- ai_lls_lib/core/processor.py +219 -59
- ai_lls_lib/core/verifier.py +29 -40
- ai_lls_lib/payment/__init__.py +13 -0
- ai_lls_lib/payment/credit_manager.py +174 -0
- ai_lls_lib/payment/models.py +96 -0
- ai_lls_lib/payment/stripe_manager.py +473 -0
- ai_lls_lib/payment/webhook_processor.py +163 -0
- ai_lls_lib/providers/__init__.py +7 -0
- ai_lls_lib/providers/base.py +28 -0
- ai_lls_lib/providers/external.py +87 -0
- ai_lls_lib/providers/stub.py +48 -0
- {ai_lls_lib-1.0.0.dist-info → ai_lls_lib-1.2.0.dist-info}/METADATA +61 -8
- ai_lls_lib-1.2.0.dist-info/RECORD +33 -0
- ai_lls_lib-1.0.0.dist-info/RECORD +0 -20
- {ai_lls_lib-1.0.0.dist-info → ai_lls_lib-1.2.0.dist-info}/WHEEL +0 -0
- {ai_lls_lib-1.0.0.dist-info → ai_lls_lib-1.2.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,163 @@
|
|
1
|
+
"""Stripe webhook event processing."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
from typing import Dict, Any
|
6
|
+
|
7
|
+
try:
|
8
|
+
import stripe
|
9
|
+
except ImportError:
|
10
|
+
stripe = None
|
11
|
+
|
12
|
+
from .credit_manager import CreditManager
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class WebhookProcessor:
|
18
|
+
"""Process Stripe webhook events."""
|
19
|
+
|
20
|
+
def __init__(self, webhook_secret: str, credit_manager: CreditManager):
|
21
|
+
"""Initialize with webhook secret and credit manager."""
|
22
|
+
self.webhook_secret = webhook_secret
|
23
|
+
self.credit_manager = credit_manager
|
24
|
+
|
25
|
+
def verify_and_parse(self, payload: str, signature: str) -> Dict[str, Any]:
|
26
|
+
"""Verify webhook signature and parse event."""
|
27
|
+
if not stripe:
|
28
|
+
raise ImportError("stripe package not installed")
|
29
|
+
|
30
|
+
try:
|
31
|
+
event = stripe.Webhook.construct_event(
|
32
|
+
payload, signature, self.webhook_secret
|
33
|
+
)
|
34
|
+
return event
|
35
|
+
except ValueError as e:
|
36
|
+
logger.error(f"Invalid webhook payload: {e}")
|
37
|
+
raise
|
38
|
+
except stripe.error.SignatureVerificationError as e:
|
39
|
+
logger.error(f"Invalid webhook signature: {e}")
|
40
|
+
raise
|
41
|
+
|
42
|
+
def process_event(self, event: Dict[str, Any]) -> Dict[str, Any]:
|
43
|
+
"""
|
44
|
+
Process a verified webhook event.
|
45
|
+
Returns response data.
|
46
|
+
"""
|
47
|
+
event_type = event.get("type")
|
48
|
+
event_data = event.get("data", {}).get("object", {})
|
49
|
+
|
50
|
+
logger.info(f"Processing webhook event: {event_type}")
|
51
|
+
|
52
|
+
if event_type == "checkout.session.completed":
|
53
|
+
return self._handle_checkout_completed(event_data)
|
54
|
+
|
55
|
+
elif event_type == "customer.subscription.created":
|
56
|
+
return self._handle_subscription_created(event_data)
|
57
|
+
|
58
|
+
elif event_type == "customer.subscription.updated":
|
59
|
+
return self._handle_subscription_updated(event_data)
|
60
|
+
|
61
|
+
elif event_type == "customer.subscription.deleted":
|
62
|
+
return self._handle_subscription_deleted(event_data)
|
63
|
+
|
64
|
+
elif event_type == "invoice.payment_succeeded":
|
65
|
+
return self._handle_invoice_paid(event_data)
|
66
|
+
|
67
|
+
elif event_type == "invoice.payment_failed":
|
68
|
+
return self._handle_invoice_failed(event_data)
|
69
|
+
|
70
|
+
else:
|
71
|
+
logger.info(f"Unhandled event type: {event_type}")
|
72
|
+
return {"message": f"Event {event_type} received but not processed"}
|
73
|
+
|
74
|
+
def _handle_checkout_completed(self, session: Dict[str, Any]) -> Dict[str, Any]:
|
75
|
+
"""Handle successful checkout session for credit purchase."""
|
76
|
+
metadata = session.get("metadata", {})
|
77
|
+
user_id = metadata.get("user_id")
|
78
|
+
|
79
|
+
if not user_id:
|
80
|
+
logger.error("No user_id in checkout session metadata")
|
81
|
+
return {"error": "Missing user_id"}
|
82
|
+
|
83
|
+
# Get line items to determine credits purchased
|
84
|
+
if session.get("mode") == "payment":
|
85
|
+
# One-time payment for credits
|
86
|
+
# In production, fetch line items from Stripe to get price metadata
|
87
|
+
# For now, extract from session metadata if available
|
88
|
+
credits = int(metadata.get("credits", 0))
|
89
|
+
|
90
|
+
if credits > 0:
|
91
|
+
new_balance = self.credit_manager.add_credits(user_id, credits)
|
92
|
+
logger.info(f"Added {credits} credits to user {user_id}, new balance: {new_balance}")
|
93
|
+
return {"credits_added": credits, "new_balance": new_balance}
|
94
|
+
|
95
|
+
return {"message": "Checkout processed"}
|
96
|
+
|
97
|
+
def _handle_subscription_created(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
98
|
+
"""Handle new subscription creation."""
|
99
|
+
metadata = subscription.get("metadata", {})
|
100
|
+
user_id = metadata.get("user_id")
|
101
|
+
customer_id = subscription.get("customer")
|
102
|
+
subscription_id = subscription.get("id")
|
103
|
+
status = subscription.get("status")
|
104
|
+
|
105
|
+
if user_id:
|
106
|
+
self.credit_manager.set_subscription_state(
|
107
|
+
user_id=user_id,
|
108
|
+
status=status,
|
109
|
+
stripe_customer_id=customer_id,
|
110
|
+
stripe_subscription_id=subscription_id
|
111
|
+
)
|
112
|
+
logger.info(f"Created subscription {subscription_id} for user {user_id}")
|
113
|
+
|
114
|
+
return {"subscription_id": subscription_id, "status": status}
|
115
|
+
|
116
|
+
def _handle_subscription_updated(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
117
|
+
"""Handle subscription updates (pause/resume/etc)."""
|
118
|
+
metadata = subscription.get("metadata", {})
|
119
|
+
user_id = metadata.get("user_id")
|
120
|
+
subscription_id = subscription.get("id")
|
121
|
+
status = subscription.get("status")
|
122
|
+
|
123
|
+
if user_id:
|
124
|
+
self.credit_manager.set_subscription_state(
|
125
|
+
user_id=user_id,
|
126
|
+
status=status,
|
127
|
+
stripe_subscription_id=subscription_id
|
128
|
+
)
|
129
|
+
logger.info(f"Updated subscription {subscription_id} status to {status}")
|
130
|
+
|
131
|
+
return {"subscription_id": subscription_id, "status": status}
|
132
|
+
|
133
|
+
def _handle_subscription_deleted(self, subscription: Dict[str, Any]) -> Dict[str, Any]:
|
134
|
+
"""Handle subscription cancellation."""
|
135
|
+
metadata = subscription.get("metadata", {})
|
136
|
+
user_id = metadata.get("user_id")
|
137
|
+
subscription_id = subscription.get("id")
|
138
|
+
|
139
|
+
if user_id:
|
140
|
+
self.credit_manager.set_subscription_state(
|
141
|
+
user_id=user_id,
|
142
|
+
status="cancelled",
|
143
|
+
stripe_subscription_id=subscription_id
|
144
|
+
)
|
145
|
+
logger.info(f"Cancelled subscription {subscription_id} for user {user_id}")
|
146
|
+
|
147
|
+
return {"subscription_id": subscription_id, "status": "cancelled"}
|
148
|
+
|
149
|
+
def _handle_invoice_paid(self, invoice: Dict[str, Any]) -> Dict[str, Any]:
|
150
|
+
"""Handle successful subscription payment."""
|
151
|
+
# For monthly subscriptions, could grant monthly credit allotment here
|
152
|
+
# For now, just log the payment
|
153
|
+
customer_id = invoice.get("customer")
|
154
|
+
amount = invoice.get("amount_paid", 0) / 100.0
|
155
|
+
logger.info(f"Invoice paid: ${amount} from customer {customer_id}")
|
156
|
+
return {"amount_paid": amount}
|
157
|
+
|
158
|
+
def _handle_invoice_failed(self, invoice: Dict[str, Any]) -> Dict[str, Any]:
|
159
|
+
"""Handle failed subscription payment."""
|
160
|
+
customer_id = invoice.get("customer")
|
161
|
+
logger.warning(f"Invoice payment failed for customer {customer_id}")
|
162
|
+
# Could pause subscription or send notification here
|
163
|
+
return {"status": "payment_failed"}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""
|
2
|
+
Base protocol for verification providers
|
3
|
+
"""
|
4
|
+
from typing import Protocol, Tuple
|
5
|
+
from ..core.models import LineType
|
6
|
+
|
7
|
+
|
8
|
+
class VerificationProvider(Protocol):
|
9
|
+
"""
|
10
|
+
Protocol for phone verification providers.
|
11
|
+
All providers must implement this interface.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def verify_phone(self, phone: str) -> Tuple[LineType, bool]:
|
15
|
+
"""
|
16
|
+
Verify a phone number's line type and DNC status.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
phone: E.164 formatted phone number
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
Tuple of (line_type, is_on_dnc_list)
|
23
|
+
|
24
|
+
Raises:
|
25
|
+
ValueError: If phone format is invalid
|
26
|
+
Exception: For provider-specific errors
|
27
|
+
"""
|
28
|
+
...
|
@@ -0,0 +1,87 @@
|
|
1
|
+
"""
|
2
|
+
External API provider for production phone verification
|
3
|
+
"""
|
4
|
+
import os
|
5
|
+
from typing import Tuple, Optional
|
6
|
+
import httpx
|
7
|
+
from aws_lambda_powertools import Logger
|
8
|
+
from ..core.models import LineType
|
9
|
+
|
10
|
+
logger = Logger()
|
11
|
+
|
12
|
+
|
13
|
+
class ExternalAPIProvider:
|
14
|
+
"""
|
15
|
+
Production provider that calls external verification APIs.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(
|
19
|
+
self,
|
20
|
+
phone_api_key: Optional[str] = None,
|
21
|
+
dnc_api_key: Optional[str] = None,
|
22
|
+
timeout: float = 10.0
|
23
|
+
):
|
24
|
+
"""
|
25
|
+
Initialize external API provider.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
phone_api_key: API key for phone line type verification
|
29
|
+
dnc_api_key: API key for DNC list checking
|
30
|
+
timeout: HTTP request timeout in seconds
|
31
|
+
"""
|
32
|
+
self.phone_api_key = phone_api_key or os.environ.get("PHONE_VERIFY_API_KEY", "")
|
33
|
+
self.dnc_api_key = dnc_api_key or os.environ.get("DNC_API_KEY", "")
|
34
|
+
self.http_client = httpx.Client(timeout=timeout)
|
35
|
+
|
36
|
+
def verify_phone(self, phone: str) -> Tuple[LineType, bool]:
|
37
|
+
"""
|
38
|
+
Verify phone using external APIs.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
phone: E.164 formatted phone number
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
Tuple of (line_type, is_on_dnc_list)
|
45
|
+
|
46
|
+
Raises:
|
47
|
+
httpx.HTTPError: For API communication errors
|
48
|
+
ValueError: For invalid responses
|
49
|
+
"""
|
50
|
+
line_type = self._check_line_type(phone)
|
51
|
+
is_dnc = self._check_dnc(phone)
|
52
|
+
return line_type, is_dnc
|
53
|
+
|
54
|
+
def _check_line_type(self, phone: str) -> LineType:
|
55
|
+
"""
|
56
|
+
Check line type via external API.
|
57
|
+
|
58
|
+
TODO: Implement actual API call
|
59
|
+
- Use self.phone_api_key for authentication
|
60
|
+
- Parse API response
|
61
|
+
- Map to LineType enum
|
62
|
+
"""
|
63
|
+
logger.info(f"External line type check for {phone[:6]}***")
|
64
|
+
|
65
|
+
# Placeholder implementation
|
66
|
+
# In production, this would make an actual API call
|
67
|
+
raise NotImplementedError("External line type API not yet configured")
|
68
|
+
|
69
|
+
def _check_dnc(self, phone: str) -> bool:
|
70
|
+
"""
|
71
|
+
Check DNC status via external API.
|
72
|
+
|
73
|
+
TODO: Implement actual API call
|
74
|
+
- Use self.dnc_api_key for authentication
|
75
|
+
- Parse API response
|
76
|
+
- Return boolean status
|
77
|
+
"""
|
78
|
+
logger.info(f"External DNC check for {phone[:6]}***")
|
79
|
+
|
80
|
+
# Placeholder implementation
|
81
|
+
# In production, this would make an actual API call
|
82
|
+
raise NotImplementedError("External DNC API not yet configured")
|
83
|
+
|
84
|
+
def __del__(self):
|
85
|
+
"""Cleanup HTTP client"""
|
86
|
+
if hasattr(self, 'http_client'):
|
87
|
+
self.http_client.close()
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"""
|
2
|
+
Stub provider for development and testing
|
3
|
+
"""
|
4
|
+
from typing import Tuple
|
5
|
+
from aws_lambda_powertools import Logger
|
6
|
+
from ..core.models import LineType
|
7
|
+
|
8
|
+
logger = Logger()
|
9
|
+
|
10
|
+
|
11
|
+
class StubProvider:
|
12
|
+
"""
|
13
|
+
Stub implementation for development and testing.
|
14
|
+
Uses deterministic rules based on phone number digits.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def verify_phone(self, phone: str) -> Tuple[LineType, bool]:
|
18
|
+
"""
|
19
|
+
Verify using stub logic based on last digit.
|
20
|
+
|
21
|
+
Line type:
|
22
|
+
- Ends in 2 or 0: LANDLINE
|
23
|
+
- Otherwise: MOBILE
|
24
|
+
|
25
|
+
DNC status:
|
26
|
+
- Ends in 1 or 0: on DNC list
|
27
|
+
- Otherwise: not on DNC
|
28
|
+
|
29
|
+
Args:
|
30
|
+
phone: E.164 formatted phone number
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
Tuple of (line_type, is_on_dnc_list)
|
34
|
+
"""
|
35
|
+
logger.info(f"Stub verification for {phone[:6]}***")
|
36
|
+
|
37
|
+
last_digit = phone[-1] if phone else '5'
|
38
|
+
|
39
|
+
# Determine line type
|
40
|
+
if last_digit in ['2', '0']:
|
41
|
+
line_type = LineType.LANDLINE
|
42
|
+
else:
|
43
|
+
line_type = LineType.MOBILE
|
44
|
+
|
45
|
+
# Determine DNC status
|
46
|
+
is_dnc = last_digit in ['1', '0']
|
47
|
+
|
48
|
+
return line_type, is_dnc
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: ai-lls-lib
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.2.0
|
4
4
|
Summary: Landline Scrubber core library - phone verification and DNC checking
|
5
5
|
Author: LandlineScrubber Team
|
6
6
|
Requires-Python: >=3.12,<4.0
|
@@ -14,12 +14,28 @@ Requires-Dist: httpx (>=0.25.0,<0.26.0)
|
|
14
14
|
Requires-Dist: phonenumbers (>=8.13.0,<9.0.0)
|
15
15
|
Requires-Dist: pydantic (>=2.5.0,<3.0.0)
|
16
16
|
Requires-Dist: rich (>=14.0,<15.0)
|
17
|
+
Requires-Dist: stripe (>=12.5.1,<13.0.0)
|
17
18
|
Description-Content-Type: text/markdown
|
18
19
|
|
19
20
|
# AI LLS Library
|
20
21
|
|
21
22
|
Core business logic library and CLI tools for Landline Scrubber - phone verification and DNC checking.
|
22
23
|
|
24
|
+
## Version 2.1.0 - Streaming & Provider Architecture
|
25
|
+
|
26
|
+
New features:
|
27
|
+
- **Streaming support** for large CSV files to reduce memory usage
|
28
|
+
- **Provider architecture** for clean separation of verification logic
|
29
|
+
- **Contract tests** ensuring all providers behave consistently
|
30
|
+
|
31
|
+
## Version 2.0.0 - Breaking Changes
|
32
|
+
|
33
|
+
This is a greenfield rewrite with no backwards compatibility:
|
34
|
+
- All file-based CSV processing replaced with text-based methods
|
35
|
+
- Removed `_sync` suffix from all methods (everything is sync)
|
36
|
+
- `process_csv_sync(file_path)` → `process_csv(csv_text)`
|
37
|
+
- `generate_results_csv(...)` now returns CSV string instead of writing to file
|
38
|
+
|
23
39
|
## Features
|
24
40
|
|
25
41
|
- Phone number normalization (E.164 format)
|
@@ -50,7 +66,7 @@ from ai_lls_lib import PhoneVerifier, DynamoDBCache
|
|
50
66
|
cache = DynamoDBCache(table_name="phone-cache")
|
51
67
|
verifier = PhoneVerifier(cache)
|
52
68
|
|
53
|
-
result = verifier.
|
69
|
+
result = verifier.verify("+15551234567")
|
54
70
|
print(f"Line type: {result.line_type}")
|
55
71
|
print(f"DNC: {result.dnc}")
|
56
72
|
print(f"From cache: {result.cached}")
|
@@ -65,12 +81,49 @@ cache = DynamoDBCache(table_name="phone-cache")
|
|
65
81
|
verifier = PhoneVerifier(cache)
|
66
82
|
processor = BulkProcessor(verifier)
|
67
83
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
)
|
84
|
+
# Process CSV text content
|
85
|
+
csv_text = "name,phone\nJohn,+15551234567\nJane,+15551234568"
|
86
|
+
results = processor.process_csv(csv_text)
|
87
|
+
|
88
|
+
# Generate results CSV
|
89
|
+
results_csv = processor.generate_results_csv(csv_text, results)
|
90
|
+
print(results_csv) # CSV string with added line_type, dnc, cached columns
|
91
|
+
```
|
92
|
+
|
93
|
+
### Streaming Large Files
|
94
|
+
|
95
|
+
For memory-efficient processing of large CSV files:
|
96
|
+
|
97
|
+
```python
|
98
|
+
from ai_lls_lib import BulkProcessor, PhoneVerifier, DynamoDBCache
|
99
|
+
|
100
|
+
cache = DynamoDBCache(table_name="phone-cache")
|
101
|
+
verifier = PhoneVerifier(cache)
|
102
|
+
processor = BulkProcessor(verifier)
|
103
|
+
|
104
|
+
# Process CSV as a stream, yielding batches
|
105
|
+
csv_lines = open('large_file.csv').readlines()
|
106
|
+
for batch in processor.process_csv_stream(csv_lines, batch_size=100):
|
107
|
+
print(f"Processed batch of {len(batch)} phones")
|
108
|
+
# Each batch is a list of PhoneVerification objects
|
109
|
+
```
|
110
|
+
|
111
|
+
### Custom Verification Providers
|
112
|
+
|
113
|
+
Use different verification providers based on your needs:
|
114
|
+
|
115
|
+
```python
|
116
|
+
from ai_lls_lib import PhoneVerifier, DynamoDBCache
|
117
|
+
from ai_lls_lib.providers import StubProvider
|
118
|
+
|
119
|
+
# Use stub provider for testing
|
120
|
+
cache = DynamoDBCache(table_name="phone-cache")
|
121
|
+
provider = StubProvider() # Deterministic testing provider
|
122
|
+
verifier = PhoneVerifier(cache, provider=provider)
|
123
|
+
|
124
|
+
# When external APIs are ready, switch to:
|
125
|
+
# from ai_lls_lib.providers.external import ExternalAPIProvider
|
126
|
+
# provider = ExternalAPIProvider(phone_api_key="...", dnc_api_key="...")
|
74
127
|
```
|
75
128
|
|
76
129
|
## CLI Usage
|
@@ -0,0 +1,33 @@
|
|
1
|
+
ai_lls_lib/__init__.py,sha256=vX28RZKgWt_4PDkQ_H69AwCCp7Jt9nHwhxt4RhKOotU,584
|
2
|
+
ai_lls_lib/auth/__init__.py,sha256=c6zomHSB6y9Seakf84ciGsD3XgWarIty9xty6P8fxVw,194
|
3
|
+
ai_lls_lib/auth/context_parser.py,sha256=8I0vGbtykNLWqm8ldedxXjE-E3nqsCy113JgeyuiJoM,2222
|
4
|
+
ai_lls_lib/cli/__init__.py,sha256=m9qjZTW1jpENwXAUeuRrlP0b66BWRcqSO28MSjvOyCs,74
|
5
|
+
ai_lls_lib/cli/__main__.py,sha256=8tUdq4GJwzIiTC1pvCsTkwmq8iNujdFTbI45_wm3d7A,716
|
6
|
+
ai_lls_lib/cli/aws_client.py,sha256=YcCWCpTNOW9JPLxSNLRy5-F5HPKguJJk7dPNrPqhJv0,3952
|
7
|
+
ai_lls_lib/cli/commands/__init__.py,sha256=_kROrYuR_p2i110c0OvNeArfEFQbn15zR1c3pdeZOoo,28
|
8
|
+
ai_lls_lib/cli/commands/admin.py,sha256=bNBJi2fZBP0J40JQP6HP7NYadNmI214iII1TeLhooyE,6687
|
9
|
+
ai_lls_lib/cli/commands/cache.py,sha256=vWt0vy4L9CEgUEWUzfdehU6u43PE8vUvHx7xxg4e5tw,5680
|
10
|
+
ai_lls_lib/cli/commands/stripe.py,sha256=x5QtirbCPiBrjdMRVTR0aeQN2gy0_p6jcGzELObFfzg,12006
|
11
|
+
ai_lls_lib/cli/commands/test_stack.py,sha256=rNq4mhRXX7Ixo67kSoEPWlxqgXCzM9e2PR96qTAG7pE,7378
|
12
|
+
ai_lls_lib/cli/commands/verify.py,sha256=V5ucjmjCUxqN8_AeEJWWgrmYin8BNV3h4WbZ-iX3loU,4238
|
13
|
+
ai_lls_lib/cli/env_loader.py,sha256=YTCB6QDyknOuedPbQsW4bezB5SgHzJMGjhpSPArHFVc,3762
|
14
|
+
ai_lls_lib/core/__init__.py,sha256=QUaeQHIyvknkgMxIbfXRo1a5jSQpiJkB84dId5ubuEk,36
|
15
|
+
ai_lls_lib/core/cache.py,sha256=MubgyAF3y7BBF9am39Ni98NgikZ9UBZUG-KtbE3XWX4,3711
|
16
|
+
ai_lls_lib/core/models.py,sha256=ABRYaeMCWahQh4WdbkCxt3AO0-EvPWZwlL-JITQSnEM,2381
|
17
|
+
ai_lls_lib/core/processor.py,sha256=6752IPDQ-Mz5i_CU7aBM0UjvV7IjyZFl35LKVPkHMpc,9974
|
18
|
+
ai_lls_lib/core/verifier.py,sha256=6uB_jawWoIqsNYAadTKr0lSolIWygg2gK6ykf8lrul0,2716
|
19
|
+
ai_lls_lib/payment/__init__.py,sha256=xhUWgfLnk3syXXQItswmDXdfXUyJTXTQAA0zIUuCVII,295
|
20
|
+
ai_lls_lib/payment/credit_manager.py,sha256=TDrdW7BYjhtlg8MX_EJEnMNct1SU_6Tdb_k19_e8kpo,6343
|
21
|
+
ai_lls_lib/payment/models.py,sha256=JjSmWKwpuFF85Jzmabj6y7UyolJlJlsh5CmOWRg21B8,3339
|
22
|
+
ai_lls_lib/payment/stripe_manager.py,sha256=-L329stuu-6V_QKzRdkm9qnoi74SbWzST0fuf2WAJXA,17111
|
23
|
+
ai_lls_lib/payment/webhook_processor.py,sha256=UVngeAGcou5ieRAp-49pqdWh0wsJQVwpiRoLmyl5Adc,6448
|
24
|
+
ai_lls_lib/providers/__init__.py,sha256=AEv3ARenWDwDo5PLCoszP2fQ70RgSHkrSLSUz7xHJDk,179
|
25
|
+
ai_lls_lib/providers/base.py,sha256=344XYOg7bxDQMWJ6Lle8U7NOHpabnCp0XYbZpeWpPAk,681
|
26
|
+
ai_lls_lib/providers/external.py,sha256=-Hhnlm8lQWRcWr5vG0dmD3sca2rohURrx0usOR2y1MM,2623
|
27
|
+
ai_lls_lib/providers/stub.py,sha256=847Tmw522B3HQ2j38BH1sdcZQy--RdtDcXsrIrFKNBQ,1150
|
28
|
+
ai_lls_lib/testing/__init__.py,sha256=RUxRYBzzPCPS15Umb6bUrE6rL5BQXBQf4SJM2E3ffrg,39
|
29
|
+
ai_lls_lib/testing/fixtures.py,sha256=_n6bbr95LnQf9Dvu1qKs2HsvHEA7AAbe59B75qxE10w,3310
|
30
|
+
ai_lls_lib-1.2.0.dist-info/METADATA,sha256=o76nenDApQ5pITFYnlmX7IS-maEi39JgU75iUyI9udk,7248
|
31
|
+
ai_lls_lib-1.2.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
32
|
+
ai_lls_lib-1.2.0.dist-info/entry_points.txt,sha256=Pi0V_HBViEKGFbNQKatl5lhhnHHBXlxaom-5gH9gXZ0,55
|
33
|
+
ai_lls_lib-1.2.0.dist-info/RECORD,,
|
@@ -1,20 +0,0 @@
|
|
1
|
-
ai_lls_lib/__init__.py,sha256=XfIG376ImlT_OH0R75BNpoqcxsx0fIp6cSFpQw_XhL8,584
|
2
|
-
ai_lls_lib/cli/__init__.py,sha256=m9qjZTW1jpENwXAUeuRrlP0b66BWRcqSO28MSjvOyCs,74
|
3
|
-
ai_lls_lib/cli/__main__.py,sha256=DH9x08k6GWBSqVWspHgWTxGM7NkTBZ2OS2KrVW-XQaA,671
|
4
|
-
ai_lls_lib/cli/aws_client.py,sha256=YcCWCpTNOW9JPLxSNLRy5-F5HPKguJJk7dPNrPqhJv0,3952
|
5
|
-
ai_lls_lib/cli/commands/__init__.py,sha256=_kROrYuR_p2i110c0OvNeArfEFQbn15zR1c3pdeZOoo,28
|
6
|
-
ai_lls_lib/cli/commands/admin.py,sha256=bNBJi2fZBP0J40JQP6HP7NYadNmI214iII1TeLhooyE,6687
|
7
|
-
ai_lls_lib/cli/commands/cache.py,sha256=vWt0vy4L9CEgUEWUzfdehU6u43PE8vUvHx7xxg4e5tw,5680
|
8
|
-
ai_lls_lib/cli/commands/test_stack.py,sha256=rNq4mhRXX7Ixo67kSoEPWlxqgXCzM9e2PR96qTAG7pE,7378
|
9
|
-
ai_lls_lib/cli/commands/verify.py,sha256=tKHkSghrtnqkjAQGvTs-5uDUgstQO0HMW04EddRPgJU,4253
|
10
|
-
ai_lls_lib/core/__init__.py,sha256=QUaeQHIyvknkgMxIbfXRo1a5jSQpiJkB84dId5ubuEk,36
|
11
|
-
ai_lls_lib/core/cache.py,sha256=MubgyAF3y7BBF9am39Ni98NgikZ9UBZUG-KtbE3XWX4,3711
|
12
|
-
ai_lls_lib/core/models.py,sha256=ABRYaeMCWahQh4WdbkCxt3AO0-EvPWZwlL-JITQSnEM,2381
|
13
|
-
ai_lls_lib/core/processor.py,sha256=s49fVlZt6lnfWNgboiHsC6ruman4oIXzgVqzuwExKBQ,4916
|
14
|
-
ai_lls_lib/core/verifier.py,sha256=fglRDoFgrQyX4UuYknZLYX4t9HlpTS2HMVsfszSNXQ4,3257
|
15
|
-
ai_lls_lib/testing/__init__.py,sha256=RUxRYBzzPCPS15Umb6bUrE6rL5BQXBQf4SJM2E3ffrg,39
|
16
|
-
ai_lls_lib/testing/fixtures.py,sha256=_n6bbr95LnQf9Dvu1qKs2HsvHEA7AAbe59B75qxE10w,3310
|
17
|
-
ai_lls_lib-1.0.0.dist-info/METADATA,sha256=q4xSigc2_m2Jmv45SfXFfH9dBbFOVDcfIBa0Uzaw48w,5344
|
18
|
-
ai_lls_lib-1.0.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
19
|
-
ai_lls_lib-1.0.0.dist-info/entry_points.txt,sha256=Pi0V_HBViEKGFbNQKatl5lhhnHHBXlxaom-5gH9gXZ0,55
|
20
|
-
ai_lls_lib-1.0.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|