aimodelshare 0.1.55__py3-none-any.whl → 0.1.60__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 aimodelshare might be problematic. Click here for more details.
- aimodelshare/__init__.py +94 -14
- aimodelshare/aimsonnx.py +263 -82
- aimodelshare/api.py +13 -12
- aimodelshare/auth.py +163 -0
- aimodelshare/aws.py +4 -4
- aimodelshare/base_image.py +1 -1
- aimodelshare/containerisation.py +1 -1
- aimodelshare/data_sharing/download_data.py +133 -83
- aimodelshare/generatemodelapi.py +7 -6
- aimodelshare/main/authorization.txt +275 -275
- aimodelshare/main/eval_lambda.txt +81 -13
- aimodelshare/model.py +493 -197
- aimodelshare/modeluser.py +89 -1
- aimodelshare/moral_compass/README.md +408 -0
- aimodelshare/moral_compass/__init__.py +58 -0
- aimodelshare/moral_compass/_version.py +3 -0
- aimodelshare/moral_compass/api_client.py +601 -0
- aimodelshare/moral_compass/challenge.py +365 -0
- aimodelshare/moral_compass/config.py +187 -0
- aimodelshare/playground.py +26 -14
- aimodelshare/preprocessormodules.py +60 -6
- aimodelshare/pyspark/authorization.txt +258 -258
- aimodelshare/pyspark/eval_lambda.txt +1 -1
- aimodelshare/reproducibility.py +20 -5
- aimodelshare/utils/__init__.py +78 -0
- aimodelshare/utils/optional_deps.py +38 -0
- aimodelshare-0.1.60.dist-info/METADATA +258 -0
- {aimodelshare-0.1.55.dist-info → aimodelshare-0.1.60.dist-info}/RECORD +31 -25
- aimodelshare-0.1.60.dist-info/licenses/LICENSE +5 -0
- {aimodelshare-0.1.55.dist-info → aimodelshare-0.1.60.dist-info}/top_level.txt +0 -1
- aimodelshare-0.1.55.dist-info/METADATA +0 -63
- aimodelshare-0.1.55.dist-info/licenses/LICENSE +0 -2
- tests/__init__.py +0 -0
- tests/test_aimsonnx.py +0 -135
- tests/test_playground.py +0 -721
- {aimodelshare-0.1.55.dist-info → aimodelshare-0.1.60.dist-info}/WHEEL +0 -0
aimodelshare/modeluser.py
CHANGED
|
@@ -10,6 +10,27 @@ import datetime
|
|
|
10
10
|
import regex as re
|
|
11
11
|
from aimodelshare.exceptions import AuthorizationError, AWSAccessError, AWSUploadError
|
|
12
12
|
|
|
13
|
+
def decode_token_unverified(token: str):
|
|
14
|
+
"""Decode a JWT without verifying signature or audience, compatible with PyJWT<2 and >=2 versions.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
token : str
|
|
19
|
+
The JWT token to decode
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
dict
|
|
24
|
+
The decoded token payload
|
|
25
|
+
"""
|
|
26
|
+
import jwt
|
|
27
|
+
try:
|
|
28
|
+
return jwt.decode(token, options={"verify_signature": False, "verify_aud": False})
|
|
29
|
+
except TypeError:
|
|
30
|
+
# PyJWT >=2 may require specifying algorithms if verification is enabled; here we keep it disabled
|
|
31
|
+
return jwt.decode(token, options={"verify_signature": False, "verify_aud": False}, algorithms=["HS256"])
|
|
32
|
+
|
|
33
|
+
|
|
13
34
|
def get_jwt_token(username, password):
|
|
14
35
|
|
|
15
36
|
config = botocore.config.Config(signature_version=botocore.UNSIGNED)
|
|
@@ -34,8 +55,73 @@ def get_jwt_token(username, password):
|
|
|
34
55
|
|
|
35
56
|
return
|
|
36
57
|
|
|
37
|
-
def
|
|
58
|
+
def setup_bucket_only():
|
|
59
|
+
"""
|
|
60
|
+
Set up the S3 bucket for aimodelshare without creating new IAM users.
|
|
61
|
+
|
|
62
|
+
Uses the provided AWS credentials to create or access the bucket.
|
|
63
|
+
This avoids creating a new IAM user every time credentials are set.
|
|
64
|
+
"""
|
|
65
|
+
from aimodelshare.aws import get_s3_iam_client
|
|
66
|
+
|
|
67
|
+
s3, iam, region = get_s3_iam_client(os.environ.get("AWS_ACCESS_KEY_ID_AIMS"),
|
|
68
|
+
os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"),
|
|
69
|
+
os.environ.get("AWS_REGION_AIMS"))
|
|
70
|
+
|
|
71
|
+
user_session = boto3.session.Session(aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID_AIMS"),
|
|
72
|
+
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"),
|
|
73
|
+
region_name= os.environ.get("AWS_REGION_AIMS"))
|
|
74
|
+
|
|
75
|
+
account_number = user_session.client(
|
|
76
|
+
'sts').get_caller_identity().get('Account')
|
|
77
|
+
|
|
78
|
+
# Remove special characters from username
|
|
79
|
+
username_clean = re.sub('[^A-Za-z0-9-]+', '', os.environ.get("username"))
|
|
80
|
+
bucket_name = 'aimodelshare' + username_clean.lower() + str(account_number) + region.replace('-', '')
|
|
81
|
+
|
|
82
|
+
region = os.environ.get("AWS_REGION_AIMS")
|
|
83
|
+
s3_client = s3['client']
|
|
38
84
|
|
|
85
|
+
# Create bucket if it doesn't exist
|
|
86
|
+
try:
|
|
87
|
+
response = s3_client.head_bucket(Bucket=bucket_name)
|
|
88
|
+
except:
|
|
89
|
+
if region == "us-east-1":
|
|
90
|
+
response = s3_client.create_bucket(
|
|
91
|
+
ACL="private",
|
|
92
|
+
Bucket=bucket_name
|
|
93
|
+
)
|
|
94
|
+
else:
|
|
95
|
+
location = {'LocationConstraint': region}
|
|
96
|
+
response = s3_client.create_bucket(
|
|
97
|
+
ACL="private",
|
|
98
|
+
Bucket=bucket_name,
|
|
99
|
+
CreateBucketConfiguration=location
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Set the bucket name in environment for use by other functions
|
|
103
|
+
os.environ["BUCKET_NAME"] = bucket_name
|
|
104
|
+
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def create_user_getkeyandpassword():
|
|
109
|
+
"""
|
|
110
|
+
DEPRECATED: This function creates a new IAM user every time it's called.
|
|
111
|
+
|
|
112
|
+
Use setup_bucket_only() instead, which uses the provided AWS credentials
|
|
113
|
+
without creating new IAM users and policies.
|
|
114
|
+
|
|
115
|
+
This function is kept for backward compatibility but should not be used.
|
|
116
|
+
"""
|
|
117
|
+
import warnings
|
|
118
|
+
warnings.warn(
|
|
119
|
+
"create_user_getkeyandpassword() is deprecated and creates unnecessary IAM users. "
|
|
120
|
+
"Use setup_bucket_only() instead.",
|
|
121
|
+
DeprecationWarning,
|
|
122
|
+
stacklevel=2
|
|
123
|
+
)
|
|
124
|
+
|
|
39
125
|
from aimodelshare.bucketpolicy import _custom_s3_policy
|
|
40
126
|
from aimodelshare.tools import form_timestamp
|
|
41
127
|
from aimodelshare.aws import get_s3_iam_client
|
|
@@ -124,4 +210,6 @@ def create_user_getkeyandpassword():
|
|
|
124
210
|
__all__ = [
|
|
125
211
|
get_jwt_token,
|
|
126
212
|
create_user_getkeyandpassword,
|
|
213
|
+
setup_bucket_only,
|
|
214
|
+
decode_token_unverified,
|
|
127
215
|
]
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# moral_compass API Client
|
|
2
|
+
|
|
3
|
+
Production-ready Python client for the moral_compass REST API.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Automatic API Discovery**: Finds API base URL from environment variables, cached terraform outputs, or terraform command
|
|
8
|
+
- **Retry Logic**: Automatic retries for network errors and 5xx server errors with exponential backoff
|
|
9
|
+
- **Pagination**: Simple iterator helpers for paginating through large result sets
|
|
10
|
+
- **Type Safety**: Dataclasses for all API responses
|
|
11
|
+
- **Structured Exceptions**: Specific exceptions for different error types (NotFoundError, ServerError)
|
|
12
|
+
- **Backward Compatibility**: Available via both `aimodelshare.moral_compass` and `moral_compass` import paths
|
|
13
|
+
- **Authentication**: Automatic JWT token attachment from environment variables
|
|
14
|
+
- **Ownership Enforcement**: Table-level ownership and authorization controls
|
|
15
|
+
- **Naming Conventions**: Enforced naming patterns for moral compass tables
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install -e . # Install in development mode
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from aimodelshare.moral_compass import MoralcompassApiClient
|
|
27
|
+
|
|
28
|
+
# Create client (auto-discovers API URL from environment)
|
|
29
|
+
client = MoralcompassApiClient()
|
|
30
|
+
|
|
31
|
+
# Or specify URL explicitly
|
|
32
|
+
client = MoralcompassApiClient(api_base_url="https://api.example.com")
|
|
33
|
+
|
|
34
|
+
# Check API health
|
|
35
|
+
health = client.health()
|
|
36
|
+
print(health)
|
|
37
|
+
|
|
38
|
+
# Create a table
|
|
39
|
+
client.create_table("my-table", "My Display Name")
|
|
40
|
+
|
|
41
|
+
# Get table info
|
|
42
|
+
table = client.get_table("my-table")
|
|
43
|
+
print(f"Table: {table.table_id}, Users: {table.user_count}")
|
|
44
|
+
|
|
45
|
+
# List all tables with automatic pagination
|
|
46
|
+
for table in client.iter_tables():
|
|
47
|
+
print(f"- {table.table_id}: {table.display_name}")
|
|
48
|
+
|
|
49
|
+
# Add a user to a table
|
|
50
|
+
client.put_user("my-table", "user1", submission_count=10, total_count=100)
|
|
51
|
+
|
|
52
|
+
# Get user stats
|
|
53
|
+
user = client.get_user("my-table", "user1")
|
|
54
|
+
print(f"User {user.username}: {user.submission_count} submissions")
|
|
55
|
+
|
|
56
|
+
# List all users in a table
|
|
57
|
+
for user in client.iter_users("my-table"):
|
|
58
|
+
print(f"- {user.username}: {user.submission_count} submissions")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Authentication & Authorization
|
|
62
|
+
|
|
63
|
+
The moral compass API supports authentication and authorization controls when `AUTH_ENABLED=true` on the server.
|
|
64
|
+
|
|
65
|
+
> **⚠️ SECURITY WARNING**
|
|
66
|
+
> JWT signature verification is currently a **stub implementation** that performs unverified token decoding.
|
|
67
|
+
> **DO NOT use in production** for security-critical operations without implementing JWKS-based signature verification.
|
|
68
|
+
> This is suitable for development and testing only.
|
|
69
|
+
|
|
70
|
+
### Authentication Token
|
|
71
|
+
|
|
72
|
+
The client automatically attaches JWT authentication tokens from environment variables:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Preferred: Use JWT_AUTHORIZATION_TOKEN
|
|
76
|
+
export JWT_AUTHORIZATION_TOKEN="your.jwt.token"
|
|
77
|
+
|
|
78
|
+
# Legacy: AWS_TOKEN (deprecated, triggers warning)
|
|
79
|
+
export AWS_TOKEN="your.aws.token"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
You can also provide the token explicitly:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from aimodelshare.moral_compass import MoralcompassApiClient
|
|
86
|
+
|
|
87
|
+
# Explicit token
|
|
88
|
+
client = MoralcompassApiClient(auth_token="your.jwt.token")
|
|
89
|
+
|
|
90
|
+
# Auto-discover from environment
|
|
91
|
+
client = MoralcompassApiClient() # Uses JWT_AUTHORIZATION_TOKEN or AWS_TOKEN
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Automatic Token Acquisition
|
|
95
|
+
|
|
96
|
+
If no JWT token is found in environment variables, the client will attempt to auto-generate one using username and password credentials. This provides seamless integration when users have already configured credentials via `configure_credentials()`:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Set credentials for automatic JWT generation
|
|
100
|
+
export AIMODELSHARE_USERNAME="your-username"
|
|
101
|
+
export AIMODELSHARE_PASSWORD="your-password"
|
|
102
|
+
|
|
103
|
+
# Alternative variable names also supported
|
|
104
|
+
export username="your-username"
|
|
105
|
+
export password="your-password"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from aimodelshare.moral_compass import MoralcompassApiClient
|
|
110
|
+
from aimodelshare.modeluser import get_jwt_token
|
|
111
|
+
import os
|
|
112
|
+
|
|
113
|
+
# Method 1: Let the client auto-generate (recommended)
|
|
114
|
+
# Client will automatically use AIMODELSHARE_USERNAME/AIMODELSHARE_PASSWORD
|
|
115
|
+
client = MoralcompassApiClient()
|
|
116
|
+
|
|
117
|
+
# Method 2: Explicitly generate and set JWT token
|
|
118
|
+
if not os.getenv('JWT_AUTHORIZATION_TOKEN'):
|
|
119
|
+
username = os.getenv('AIMODELSHARE_USERNAME')
|
|
120
|
+
password = os.getenv('AIMODELSHARE_PASSWORD')
|
|
121
|
+
if username and password:
|
|
122
|
+
get_jwt_token(username, password) # Sets JWT_AUTHORIZATION_TOKEN
|
|
123
|
+
# Now JWT_AUTHORIZATION_TOKEN is available for subsequent API calls
|
|
124
|
+
|
|
125
|
+
client = MoralcompassApiClient()
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The auto-generation process:
|
|
129
|
+
1. Checks for existing JWT_AUTHORIZATION_TOKEN (skips if found)
|
|
130
|
+
2. Looks for AIMODELSHARE_USERNAME/AIMODELSHARE_PASSWORD or username/password
|
|
131
|
+
3. Calls `get_jwt_token()` to generate and set JWT_AUTHORIZATION_TOKEN
|
|
132
|
+
4. Uses the generated token for API authentication
|
|
133
|
+
5. Logs success/failure for debugging
|
|
134
|
+
|
|
135
|
+
### Table Ownership
|
|
136
|
+
|
|
137
|
+
When authentication is enabled, tables have ownership metadata:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
# Create a table for a playground (stores owner identity)
|
|
141
|
+
response = client.create_table(
|
|
142
|
+
table_id="my-playground-mc",
|
|
143
|
+
display_name="My Moral Compass Table",
|
|
144
|
+
playground_url="https://example.com/playground/my-playground"
|
|
145
|
+
)
|
|
146
|
+
# Response is a dict with structure:
|
|
147
|
+
# {
|
|
148
|
+
# 'tableId': 'my-playground-mc',
|
|
149
|
+
# 'displayName': 'My Moral Compass Table',
|
|
150
|
+
# 'ownerPrincipal': 'user@example.com', # Owner's identity
|
|
151
|
+
# 'playgroundId': 'my-playground',
|
|
152
|
+
# 'message': 'Table created successfully'
|
|
153
|
+
# }
|
|
154
|
+
print(response['ownerPrincipal']) # Shows who created the table
|
|
155
|
+
|
|
156
|
+
# Convenience method to create with naming convention
|
|
157
|
+
response = client.create_table_for_playground(
|
|
158
|
+
playground_url="https://example.com/playground/my-playground",
|
|
159
|
+
suffix="-mc" # Creates table: my-playground-mc
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Naming Convention
|
|
164
|
+
|
|
165
|
+
When `MC_ENFORCE_NAMING=true` on the server, moral compass tables must follow the pattern: `<playgroundId>-mc`
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# Valid: Follows naming convention
|
|
169
|
+
client.create_table(
|
|
170
|
+
table_id="my-playground-mc",
|
|
171
|
+
playground_url="https://example.com/playground/my-playground"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Invalid: Will return 400 error if MC_ENFORCE_NAMING=true
|
|
175
|
+
client.create_table(
|
|
176
|
+
table_id="random-name",
|
|
177
|
+
playground_url="https://example.com/playground/my-playground"
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Authorization Rules
|
|
182
|
+
|
|
183
|
+
When `AUTH_ENABLED=true`:
|
|
184
|
+
|
|
185
|
+
- **Table Creation**: Only authenticated users can create tables
|
|
186
|
+
- **Table Deletion**: Only the owner or admin can delete tables (requires `ALLOW_TABLE_DELETE=true`)
|
|
187
|
+
- **User Updates**: Only the user themselves or admin can update their progress
|
|
188
|
+
- **Read Operations**: Public by default when `ALLOW_PUBLIC_READ=true`
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
# Delete a table (owner or admin only)
|
|
192
|
+
client.delete_table("my-playground-mc")
|
|
193
|
+
|
|
194
|
+
# Update own progress
|
|
195
|
+
client.update_moral_compass(
|
|
196
|
+
table_id="my-playground-mc",
|
|
197
|
+
username="my-username",
|
|
198
|
+
metrics={"accuracy": 0.85},
|
|
199
|
+
tasks_completed=3,
|
|
200
|
+
total_tasks=6
|
|
201
|
+
)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Helper Functions
|
|
205
|
+
|
|
206
|
+
Use the `aimodelshare.auth` module for identity management:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from aimodelshare.auth import get_primary_token, get_identity_claims
|
|
210
|
+
|
|
211
|
+
# Get token from environment
|
|
212
|
+
token = get_primary_token()
|
|
213
|
+
|
|
214
|
+
# Extract identity claims (DEVELOPMENT ONLY - signature not verified)
|
|
215
|
+
if token:
|
|
216
|
+
# Currently verify=False (stub implementation)
|
|
217
|
+
# TODO: Use verify=True after implementing JWKS verification for production
|
|
218
|
+
claims = get_identity_claims(token, verify=False)
|
|
219
|
+
print(f"User: {claims['principal']}")
|
|
220
|
+
print(f"Email: {claims.get('email')}")
|
|
221
|
+
print(f"Subject: {claims['sub']}")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## API Base URL Configuration
|
|
225
|
+
|
|
226
|
+
The client discovers the API base URL using the following priority:
|
|
227
|
+
|
|
228
|
+
1. **Environment Variable**: `MORAL_COMPASS_API_BASE_URL` or `AIMODELSHARE_API_BASE_URL`
|
|
229
|
+
2. **Cached Terraform Outputs**: `infra/terraform_outputs.json`
|
|
230
|
+
3. **Terraform Command**: Runs `terraform output -raw api_base_url` in the `infra/` directory
|
|
231
|
+
4. **Explicit Parameter**: Pass `api_base_url` to the client constructor
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# Set via environment variable
|
|
235
|
+
export MORAL_COMPASS_API_BASE_URL="https://your-api.example.com"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Error Handling
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from aimodelshare.moral_compass import (
|
|
242
|
+
MoralcompassApiClient,
|
|
243
|
+
NotFoundError,
|
|
244
|
+
ServerError,
|
|
245
|
+
ApiClientError
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
client = MoralcompassApiClient()
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
table = client.get_table("nonexistent-table")
|
|
252
|
+
except NotFoundError:
|
|
253
|
+
print("Table not found (404)")
|
|
254
|
+
except ServerError:
|
|
255
|
+
print("Server error (5xx)")
|
|
256
|
+
except ApiClientError as e:
|
|
257
|
+
print(f"API error: {e}")
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Pagination
|
|
261
|
+
|
|
262
|
+
### Manual Pagination
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
# Get first page
|
|
266
|
+
response = client.list_tables(limit=10)
|
|
267
|
+
tables = response["tables"]
|
|
268
|
+
last_key = response.get("lastKey")
|
|
269
|
+
|
|
270
|
+
# Get next page if available
|
|
271
|
+
if last_key:
|
|
272
|
+
response = client.list_tables(limit=10, last_key=last_key)
|
|
273
|
+
tables.extend(response["tables"])
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Automatic Pagination with Iterators
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
# Automatically handles pagination behind the scenes
|
|
280
|
+
for table in client.iter_tables(limit=50):
|
|
281
|
+
print(table.table_id)
|
|
282
|
+
|
|
283
|
+
for user in client.iter_users("my-table", limit=50):
|
|
284
|
+
print(user.username)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Dataclasses
|
|
288
|
+
|
|
289
|
+
### MoralcompassTableMeta
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from aimodelshare.moral_compass import MoralcompassTableMeta
|
|
293
|
+
|
|
294
|
+
table = MoralcompassTableMeta(
|
|
295
|
+
table_id="my-table",
|
|
296
|
+
display_name="My Table",
|
|
297
|
+
created_at="2024-01-01T00:00:00Z",
|
|
298
|
+
is_archived=False,
|
|
299
|
+
user_count=42
|
|
300
|
+
)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### MoralcompassUserStats
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
from aimodelshare.moral_compass import MoralcompassUserStats
|
|
307
|
+
|
|
308
|
+
user = MoralcompassUserStats(
|
|
309
|
+
username="user1",
|
|
310
|
+
submission_count=10,
|
|
311
|
+
total_count=100,
|
|
312
|
+
last_updated="2024-01-01T12:00:00Z"
|
|
313
|
+
)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Backward Compatibility
|
|
317
|
+
|
|
318
|
+
Both import paths are supported:
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
# New path (recommended)
|
|
322
|
+
from aimodelshare.moral_compass import MoralcompassApiClient
|
|
323
|
+
|
|
324
|
+
# Legacy path (backward compatible)
|
|
325
|
+
from moral_compass import MoralcompassApiClient
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## API Methods
|
|
329
|
+
|
|
330
|
+
### Tables
|
|
331
|
+
|
|
332
|
+
- `create_table(table_id, display_name=None, playground_url=None)` - Create a new table
|
|
333
|
+
- `create_table_for_playground(playground_url, suffix='-mc', display_name=None)` - Create table with naming convention
|
|
334
|
+
- `list_tables(limit=50, last_key=None)` - List tables with pagination
|
|
335
|
+
- `iter_tables(limit=50)` - Iterate all tables with automatic pagination
|
|
336
|
+
- `get_table(table_id)` - Get specific table metadata
|
|
337
|
+
- `patch_table(table_id, display_name=None, is_archived=None)` - Update table metadata
|
|
338
|
+
- `delete_table(table_id)` - Delete a table (requires owner/admin auth)
|
|
339
|
+
|
|
340
|
+
### Users
|
|
341
|
+
|
|
342
|
+
- `put_user(table_id, username, submission_count, total_count)` - Create/update user
|
|
343
|
+
- `get_user(table_id, username)` - Get user stats
|
|
344
|
+
- `list_users(table_id, limit=50, last_key=None)` - List users with pagination
|
|
345
|
+
- `iter_users(table_id, limit=50)` - Iterate all users with automatic pagination
|
|
346
|
+
- `update_moral_compass(table_id, username, metrics, tasks_completed=0, total_tasks=0, questions_correct=0, total_questions=0, primary_metric=None)` - Update user's moral compass score
|
|
347
|
+
|
|
348
|
+
### Health
|
|
349
|
+
|
|
350
|
+
- `health()` - Check API health status
|
|
351
|
+
|
|
352
|
+
## Testing
|
|
353
|
+
|
|
354
|
+
### Unit Tests
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
# Run all tests except integration tests
|
|
358
|
+
pytest -m "not integration"
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Integration Tests
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Requires deployed API with MORAL_COMPASS_API_BASE_URL set
|
|
365
|
+
export MORAL_COMPASS_API_BASE_URL="https://your-api.example.com"
|
|
366
|
+
pytest -m integration tests/test_moral_compass_client_minimal.py -v
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## CI/CD Integration
|
|
370
|
+
|
|
371
|
+
The deploy-infra workflow automatically:
|
|
372
|
+
1. Caches terraform outputs
|
|
373
|
+
2. Verifies API health endpoint is reachable
|
|
374
|
+
3. Installs the package in editable mode
|
|
375
|
+
4. Runs integration tests
|
|
376
|
+
|
|
377
|
+
See `.github/workflows/deploy-infra.yml` for details.
|
|
378
|
+
|
|
379
|
+
## Scripts
|
|
380
|
+
|
|
381
|
+
### Cache Terraform Outputs
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
bash scripts/cache_terraform_outputs.sh
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Exports `MORAL_COMPASS_API_BASE_URL` and writes `infra/terraform_outputs.json`.
|
|
388
|
+
|
|
389
|
+
### Verify API Health
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
bash scripts/verify_api_reachable.sh [API_BASE_URL]
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Checks that the `/health` endpoint is reachable with retries.
|
|
396
|
+
|
|
397
|
+
## Version
|
|
398
|
+
|
|
399
|
+
Current version: 0.1.0
|
|
400
|
+
|
|
401
|
+
```python
|
|
402
|
+
from aimodelshare.moral_compass import __version__
|
|
403
|
+
print(__version__) # "0.1.0"
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## License
|
|
407
|
+
|
|
408
|
+
Same as parent aimodelshare package.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
aimodelshare.moral_compass - Production-ready client for moral_compass REST API
|
|
3
|
+
|
|
4
|
+
This submodule provides a client for interacting with the moral_compass API
|
|
5
|
+
with support for pagination, retries, and structured exceptions.
|
|
6
|
+
|
|
7
|
+
Example usage:
|
|
8
|
+
from aimodelshare.moral_compass import MoralcompassApiClient, ChallengeManager
|
|
9
|
+
|
|
10
|
+
client = MoralcompassApiClient()
|
|
11
|
+
|
|
12
|
+
# Create a table
|
|
13
|
+
client.create_table("my-table", "My Display Name")
|
|
14
|
+
|
|
15
|
+
# List tables with pagination
|
|
16
|
+
for table in client.iter_tables():
|
|
17
|
+
print(f"Table: {table.table_id} - {table.display_name}")
|
|
18
|
+
|
|
19
|
+
# Get specific table
|
|
20
|
+
table = client.get_table("my-table")
|
|
21
|
+
|
|
22
|
+
# Manage users
|
|
23
|
+
client.put_user("my-table", "user1", submission_count=10, total_count=100)
|
|
24
|
+
user = client.get_user("my-table", "user1")
|
|
25
|
+
|
|
26
|
+
# Use ChallengeManager for multi-metric tracking
|
|
27
|
+
manager = ChallengeManager("my-table", "user1")
|
|
28
|
+
manager.set_metric("accuracy", 0.85, primary=True)
|
|
29
|
+
manager.set_metric("fairness", 0.92)
|
|
30
|
+
manager.set_progress(tasks_completed=5, total_tasks=10)
|
|
31
|
+
manager.sync()
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from ._version import __version__
|
|
35
|
+
from .api_client import (
|
|
36
|
+
MoralcompassApiClient,
|
|
37
|
+
MoralcompassTableMeta,
|
|
38
|
+
MoralcompassUserStats,
|
|
39
|
+
ApiClientError,
|
|
40
|
+
NotFoundError,
|
|
41
|
+
ServerError,
|
|
42
|
+
)
|
|
43
|
+
from .config import get_api_base_url, get_aws_region
|
|
44
|
+
from .challenge import ChallengeManager, JusticeAndEquityChallenge
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"__version__",
|
|
48
|
+
"MoralcompassApiClient",
|
|
49
|
+
"MoralcompassTableMeta",
|
|
50
|
+
"MoralcompassUserStats",
|
|
51
|
+
"ApiClientError",
|
|
52
|
+
"NotFoundError",
|
|
53
|
+
"ServerError",
|
|
54
|
+
"get_api_base_url",
|
|
55
|
+
"get_aws_region",
|
|
56
|
+
"ChallengeManager",
|
|
57
|
+
"JusticeAndEquityChallenge",
|
|
58
|
+
]
|