trismik 0.9.1__py3-none-any.whl → 0.9.5__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.
trismik/__init__.py CHANGED
@@ -1,21 +1,10 @@
1
- from .client import TrismikClient
2
- from .client_async import TrismikAsyncClient
3
- from .exceptions import (
4
- TrismikError,
5
- TrismikApiError
6
- )
7
- from .runner import TrismikRunner
8
- from .runner_async import TrismikAsyncRunner
9
- from .types import (
10
- TrismikTest,
11
- TrismikAuth,
12
- TrismikSession,
13
- TrismikItem,
14
- TrismikMultipleChoiceTextItem,
15
- TrismikChoice,
16
- TrismikTextChoice,
17
- TrismikResult,
18
- TrismikResponse,
19
- TrismikRunResults,
20
- TrismikSessionMetadata,
21
- )
1
+ """
2
+ Trismik Python Client.
3
+
4
+ A Python client for the Trismik API.
5
+ """
6
+
7
+ import importlib.metadata
8
+
9
+ # get version from pyproject.toml
10
+ __version__ = importlib.metadata.version(__package__ or __name__)
trismik/_mapper.py CHANGED
@@ -1,79 +1,359 @@
1
- from typing import List, Any
1
+ from datetime import datetime
2
+ from typing import Any, Dict, List
2
3
 
3
- from dateutil.parser import parse as parse_date
4
-
5
- from .exceptions import TrismikApiError
6
- from .types import (
4
+ from trismik.exceptions import TrismikApiError
5
+ from trismik.types import (
6
+ TrismikClassicEvalResponse,
7
+ TrismikDataset,
7
8
  TrismikItem,
8
- TrismikResult,
9
+ TrismikMeResponse,
9
10
  TrismikMultipleChoiceTextItem,
10
- TrismikTextChoice,
11
- TrismikAuth,
12
- TrismikTest,
13
- TrismikSession,
11
+ TrismikOrganization,
12
+ TrismikProject,
13
+ TrismikReplayResponse,
14
14
  TrismikResponse,
15
+ TrismikResult,
16
+ TrismikRun,
17
+ TrismikRunInfo,
18
+ TrismikRunResponse,
19
+ TrismikRunState,
20
+ TrismikRunSummary,
21
+ TrismikTextChoice,
22
+ TrismikUserInfo,
15
23
  )
16
24
 
17
25
 
18
26
  class TrismikResponseMapper:
27
+ """
28
+ Maps JSON responses from the Trismik API to Python objects.
19
29
 
20
- @staticmethod
21
- def to_auth(json: dict[str, Any]) -> TrismikAuth:
22
- return TrismikAuth(
23
- token=json["token"],
24
- expires=parse_date(json["expires"]),
25
- )
30
+ This class provides static methods to convert JSON responses from various
31
+ API endpoints into their corresponding Python object representations.
32
+ """
26
33
 
27
34
  @staticmethod
28
- def to_tests(json: List[dict[str, Any]]) -> List[TrismikTest]:
35
+ def to_datasets(json: Dict[str, Any]) -> List[TrismikDataset]:
36
+ """
37
+ Convert JSON response to a list of TrismikDataset objects.
38
+
39
+ Args:
40
+ json (Dict[str, Any]): JSON response containing dataset data.
41
+
42
+ Returns:
43
+ List[TrismikDataset]: List of
44
+ dataset objects with IDs and names.
45
+ """
29
46
  return [
30
- TrismikTest(
31
- id=item["id"],
32
- name=item["name"],
33
- ) for item in json
47
+ TrismikDataset(
48
+ id=item["id"],
49
+ name=item["name"],
50
+ )
51
+ for item in json["data"]
34
52
  ]
35
53
 
36
54
  @staticmethod
37
- def to_session(json: dict[str, Any]) -> TrismikSession:
38
- return TrismikSession(
39
- id=json["id"],
40
- url=json["url"],
41
- status=json["status"],
55
+ def to_run(json: Dict[str, Any]) -> TrismikRun:
56
+ """
57
+ Convert JSON response to a TrismikRun object.
58
+
59
+ Args:
60
+ json (Dict[str, Any]): JSON response containing run data.
61
+
62
+ Returns:
63
+ TrismikRun: Run object with ID, URL, and status.
64
+ """
65
+ return TrismikRun(
66
+ id=json["id"],
67
+ url=json["url"],
68
+ status=json["status"],
69
+ )
70
+
71
+ @staticmethod
72
+ def to_run_info(json: Dict[str, Any]) -> TrismikRunInfo:
73
+ """
74
+ Convert JSON response to a TrismikRunInfo object.
75
+
76
+ Args:
77
+ json (Dict[str, Any]): JSON response containing run info.
78
+
79
+ Returns:
80
+ TrismikRunInfo: Run info object with ID.
81
+ """
82
+ return TrismikRunInfo(id=json["id"])
83
+
84
+ @staticmethod
85
+ def to_run_state(json: Dict[str, Any]) -> TrismikRunState:
86
+ """
87
+ Convert JSON response to a TrismikRunState object.
88
+
89
+ Args:
90
+ json (Dict[str, Any]): JSON response containing run state.
91
+
92
+ Returns:
93
+ TrismikRunState: Run state object.
94
+ """
95
+ return TrismikRunState(
96
+ responses=json.get("responses", []),
97
+ thetas=json.get("thetas", []),
98
+ std_error_history=json.get("std_error_history", []),
99
+ kl_info_history=json.get("kl_info_history", []),
100
+ effective_difficulties=json.get("effective_difficulties", []),
101
+ )
102
+
103
+ @staticmethod
104
+ def to_run_response(json: Dict[str, Any]) -> TrismikRunResponse:
105
+ """
106
+ Convert JSON response to a TrismikRunResponse object.
107
+
108
+ Args:
109
+ json (Dict[str, Any]): JSON response from run endpoints.
110
+
111
+ Returns:
112
+ TrismikRunResponse: Run response.
113
+ """
114
+ return TrismikRunResponse(
115
+ run_info=TrismikResponseMapper.to_run_info(json["runInfo"]),
116
+ state=TrismikResponseMapper.to_run_state(json["state"]),
117
+ next_item=(
118
+ TrismikResponseMapper.to_item(json["nextItem"])
119
+ if json.get("nextItem")
120
+ else None
121
+ ),
122
+ completed=json.get("completed", False),
42
123
  )
43
124
 
44
125
  @staticmethod
45
- def to_item(json: dict[str, Any]) -> TrismikItem:
46
- if json["type"] == "multiple_choice_text":
126
+ def to_run_summary(json: Dict[str, Any]) -> TrismikRunSummary:
127
+ """
128
+ Convert JSON response to a TrismikRunSummary object.
129
+
130
+ Args:
131
+ json (Dict[str, Any]): JSON response from run summary endpoint.
132
+
133
+ Returns:
134
+ TrismikRunSummary: Complete run summary.
135
+ """
136
+ return TrismikRunSummary(
137
+ id=json["id"],
138
+ dataset_id=json["datasetId"],
139
+ state=TrismikResponseMapper.to_run_state(json["state"]),
140
+ dataset=[
141
+ TrismikResponseMapper.to_item(item)
142
+ for item in json.get("dataset", [])
143
+ ],
144
+ responses=TrismikResponseMapper.to_responses(
145
+ json.get("responses", [])
146
+ ),
147
+ metadata=json.get("metadata", {}),
148
+ )
149
+
150
+ @staticmethod
151
+ def to_item(json: Dict[str, Any]) -> TrismikItem:
152
+ """
153
+ Convert JSON response to a TrismikItem object.
154
+
155
+ Args:
156
+ json (Dict[str, Any]): JSON response containing item data.
157
+
158
+ Returns:
159
+ TrismikItem: Item object with question and choices.
160
+
161
+ Raises:
162
+ TrismikApiError: If the item type is not recognized.
163
+ """
164
+ if "question" in json and "choices" in json:
47
165
  return TrismikMultipleChoiceTextItem(
48
- id=json["id"],
49
- question=json["question"],
50
- choices=[
51
- TrismikTextChoice(
52
- id=choice["id"],
53
- text=choice["text"],
54
- ) for choice in json["choices"]
55
- ]
166
+ id=json["id"],
167
+ question=json["question"],
168
+ choices=[
169
+ TrismikTextChoice(
170
+ id=choice["id"],
171
+ text=choice["value"],
172
+ )
173
+ for choice in json["choices"]
174
+ ],
56
175
  )
57
176
  else:
177
+ item_type = json.get("type", "unknown")
58
178
  raise TrismikApiError(
59
- f"API has returned unrecognized item type: {json['type']}")
179
+ f"API has returned unrecognized item type: {item_type}"
180
+ )
60
181
 
61
182
  @staticmethod
62
- def to_results(json: List[dict[str, Any]]) -> List[TrismikResult]:
183
+ def to_results(json: List[Dict[str, Any]]) -> List[TrismikResult]:
184
+ """
185
+ Convert JSON response to a list of TrismikResult objects.
186
+
187
+ Args:
188
+ json (List[Dict[str, Any]]): JSON response containing result data.
189
+
190
+ Returns:
191
+ List[TrismikResult]: List of result objects with trait, name, and
192
+ value.
193
+ """
63
194
  return [
64
195
  TrismikResult(
65
- trait=item["trait"],
66
- name=item["name"],
67
- value=item["value"],
68
- ) for item in json
196
+ trait=item["trait"],
197
+ name=item["name"],
198
+ value=item["value"],
199
+ )
200
+ for item in json
69
201
  ]
70
202
 
71
203
  @staticmethod
72
- def to_responses(json: List[dict[str, Any]]) -> List[TrismikResponse]:
204
+ def to_responses(json: List[Dict[str, Any]]) -> List[TrismikResponse]:
205
+ """
206
+ Convert JSON response to a list of TrismikResponse objects.
207
+
208
+ Args:
209
+ json (List[Dict[str, Any]]): JSON response containing response data.
210
+
211
+ Returns:
212
+ List[TrismikResponse]: List of response objects with dataset item
213
+ ID, value, and correctness.
214
+ """
73
215
  return [
74
216
  TrismikResponse(
75
- item_id=response["itemId"],
76
- value=response["value"],
77
- score=response["score"],
78
- ) for response in json
217
+ dataset_item_id=response["datasetItemId"],
218
+ value=response["value"],
219
+ correct=response["correct"],
220
+ )
221
+ for response in json
79
222
  ]
223
+
224
+ @staticmethod
225
+ def to_replay_response(json: Dict[str, Any]) -> TrismikReplayResponse:
226
+ """
227
+ Convert JSON response to a TrismikReplayResponse object.
228
+
229
+ Args:
230
+ json (Dict[str, Any]): JSON response from replay endpoint.
231
+
232
+ Returns:
233
+ TrismikReplayResponse: Replay response object.
234
+ """
235
+ # Parse datetime strings if they exist
236
+ completed_at = None
237
+ if "completedAt" in json and json["completedAt"]:
238
+ completed_at = datetime.fromisoformat(
239
+ json["completedAt"].replace("Z", "+00:00")
240
+ )
241
+
242
+ created_at = None
243
+ if "createdAt" in json and json["createdAt"]:
244
+ created_at = datetime.fromisoformat(
245
+ json["createdAt"].replace("Z", "+00:00")
246
+ )
247
+
248
+ return TrismikReplayResponse(
249
+ id=json["id"],
250
+ datasetId=json["datasetId"],
251
+ state=TrismikResponseMapper.to_run_state(json["state"]),
252
+ replay_of_run=json["replayOfRun"],
253
+ completedAt=completed_at,
254
+ createdAt=created_at,
255
+ metadata=json.get("metadata", {}),
256
+ dataset=[
257
+ TrismikResponseMapper.to_item(item)
258
+ for item in json.get("dataset", [])
259
+ ],
260
+ responses=TrismikResponseMapper.to_responses(
261
+ json.get("responses", [])
262
+ ),
263
+ )
264
+
265
+ @staticmethod
266
+ def to_me_response(json: Dict[str, Any]) -> TrismikMeResponse:
267
+ """
268
+ Convert JSON response to a TrismikMeResponse object.
269
+
270
+ Args:
271
+ json (Dict[str, Any]): JSON response from /admin/api-keys/me
272
+ endpoint.
273
+
274
+ Returns:
275
+ TrismikMeResponse: Me response object.
276
+ """
277
+ user_data = json["user"]
278
+ organizations_data = json["organizations"]
279
+
280
+ user_info = TrismikUserInfo(
281
+ id=user_data["id"],
282
+ email=user_data["email"],
283
+ firstname=user_data["firstname"],
284
+ lastname=user_data["lastname"],
285
+ createdAt=user_data.get("createdAt"),
286
+ )
287
+
288
+ organizations = [
289
+ TrismikOrganization(
290
+ id=org_data["id"],
291
+ name=org_data["name"],
292
+ type=org_data["type"],
293
+ role=org_data["role"],
294
+ )
295
+ for org_data in organizations_data
296
+ ]
297
+
298
+ return TrismikMeResponse(
299
+ user=user_info,
300
+ organizations=organizations,
301
+ )
302
+
303
+ @staticmethod
304
+ def to_classic_eval_response(
305
+ json: Dict[str, Any]
306
+ ) -> TrismikClassicEvalResponse:
307
+ """
308
+ Convert JSON response to a TrismikClassicEvalResponse object.
309
+
310
+ Args:
311
+ json (Dict[str, Any]): JSON response from classic evaluation
312
+ endpoint.
313
+
314
+ Returns:
315
+ TrismikClassicEvalResponse: Classic evaluation response object.
316
+ """
317
+ user_data = json["user"]
318
+ user_info = TrismikUserInfo(
319
+ id=user_data["id"],
320
+ email=user_data["email"],
321
+ firstname=user_data["firstname"],
322
+ lastname=user_data["lastname"],
323
+ )
324
+
325
+ return TrismikClassicEvalResponse(
326
+ id=json["id"],
327
+ organizationId=json["organizationId"],
328
+ projectId=json["projectId"],
329
+ experimentId=json["experimentId"],
330
+ experimentName=json["experimentName"],
331
+ datasetId=json["datasetId"],
332
+ userId=json["userId"],
333
+ type=json["type"],
334
+ modelName=json["modelName"],
335
+ hyperparameters=json.get("hyperparameters", {}),
336
+ createdAt=json["createdAt"],
337
+ user=user_info,
338
+ responseCount=json["responseCount"],
339
+ )
340
+
341
+ @staticmethod
342
+ def to_project(json: Dict[str, Any]) -> TrismikProject:
343
+ """
344
+ Convert JSON response to a TrismikProject object.
345
+
346
+ Args:
347
+ json (Dict[str, Any]): JSON response from project creation endpoint.
348
+
349
+ Returns:
350
+ TrismikProject: Project object.
351
+ """
352
+ return TrismikProject(
353
+ id=json["id"],
354
+ name=json["name"],
355
+ description=json.get("description"),
356
+ organizationId=json["organizationId"],
357
+ createdAt=json["createdAt"],
358
+ updatedAt=json["updatedAt"],
359
+ )
trismik/_utils.py CHANGED
@@ -1,44 +1,121 @@
1
1
  import os
2
+ from typing import Optional, Union
2
3
 
3
4
  import httpx
4
5
 
5
- from .exceptions import TrismikError
6
+ from trismik.exceptions import TrismikError
6
7
 
7
8
 
8
9
  class TrismikUtils:
10
+ """
11
+ Utility functions for the Trismik client.
12
+
13
+ This class provides helper methods for error handling and configuration
14
+ management.
15
+ """
9
16
 
10
17
  @staticmethod
11
18
  def get_error_message(response: httpx.Response) -> str:
19
+ """
20
+ Extract error message from an HTTP response.
21
+
22
+ Args:
23
+ response (httpx.Response): The HTTP response containing the error.
24
+
25
+ Returns:
26
+ str: The error message from the response JSON or content.
27
+ """
12
28
  try:
13
- return (response.json()).get("message", "Unknown error")
29
+ json_data = response.json()
30
+ message = json_data.get("message", "Unknown error")
31
+ return str(message) # Ensure we return a string
14
32
  except (httpx.RequestError, ValueError):
15
- error_message = response.content.decode("utf-8", errors="ignore")
16
- return error_message
33
+ error_message: str = response.content.decode(
34
+ "utf-8", errors="ignore"
35
+ )
36
+ return error_message
17
37
 
18
38
  @staticmethod
19
- def required_option(
20
- value: str | None,
21
- name: str,
22
- env: str
23
- ) -> str:
39
+ def required_option(value: Optional[str], name: str, env: str) -> str:
40
+ """
41
+ Get a required configuration option.
42
+
43
+ Get a required configuration option from either the provided value or
44
+ environment variable.
45
+
46
+ Args:
47
+ value (Optional[str]): The provided value for the option.
48
+ name (str): The name of the option for error messages.
49
+ env (str): The environment variable name to check if value is None.
50
+
51
+ Returns:
52
+ str: The option value.
53
+
54
+ Raises:
55
+ TrismikError: If neither value nor environment variable is set.
56
+ """
24
57
  if value is None:
25
58
  value = os.environ.get(env)
26
59
  if value is None:
27
60
  raise TrismikError(
28
- f"The {name} client option must be set either by passing "
29
- f"{env} to the client or by setting the {env} "
30
- "environment variable"
61
+ f"The {name} client option must be set either by passing "
62
+ f"{env} to the client or by setting the {env} "
63
+ "environment variable"
31
64
  )
32
65
  return value
33
66
 
34
67
  @staticmethod
35
68
  def option(
36
- value: str | None,
37
- default: str,
38
- env: str,
69
+ value: Optional[str],
70
+ default: str,
71
+ env: str,
39
72
  ) -> str:
73
+ """
74
+ Get an optional configuration value.
75
+
76
+ Get an optional configuration value from either the provided value,
77
+ environment variable, or default value.
78
+
79
+ Args:
80
+ value (Optional[str]): The provided value for the option.
81
+ default (str): The default value to use if neither value nor env var
82
+ is set.
83
+ env (str): The environment variable name to check if value is None.
84
+
85
+ Returns:
86
+ str: The option value, falling back to default if not set.
87
+ """
40
88
  if value is None:
41
89
  value = os.environ.get(env)
42
90
  if value is None:
43
91
  return default
44
92
  return value
93
+
94
+ @staticmethod
95
+ def metric_value_to_type(value: Union[str, float, int, bool]) -> str:
96
+ """
97
+ Automatically determine valueType from Python value type.
98
+
99
+ Args:
100
+ value (Union[str, float, int, bool]): The metric value to analyze.
101
+
102
+ Returns:
103
+ str: The corresponding valueType string for the API.
104
+
105
+ Raises:
106
+ TypeError: If the value type is not supported.
107
+ """
108
+ if isinstance(value, bool):
109
+ # Handle bool first since bool is a subclass of int in Python
110
+ return "Boolean"
111
+ elif isinstance(value, str):
112
+ return "String"
113
+ elif isinstance(value, float):
114
+ return "Float"
115
+ elif isinstance(value, int):
116
+ return "Integer"
117
+ else:
118
+ raise TypeError(
119
+ f"Unsupported metric value type: {type(value).__name__}. "
120
+ f"Supported types: str, float, int, bool"
121
+ )