trismik 0.9.1__py3-none-any.whl → 0.9.4__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,335 @@
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
+ TrismikReplayResponse,
14
13
  TrismikResponse,
14
+ TrismikResult,
15
+ TrismikRun,
16
+ TrismikRunInfo,
17
+ TrismikRunResponse,
18
+ TrismikRunState,
19
+ TrismikRunSummary,
20
+ TrismikTextChoice,
21
+ TrismikUserInfo,
15
22
  )
16
23
 
17
24
 
18
25
  class TrismikResponseMapper:
26
+ """
27
+ Maps JSON responses from the Trismik API to Python objects.
19
28
 
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
- )
29
+ This class provides static methods to convert JSON responses from various
30
+ API endpoints into their corresponding Python object representations.
31
+ """
26
32
 
27
33
  @staticmethod
28
- def to_tests(json: List[dict[str, Any]]) -> List[TrismikTest]:
34
+ def to_datasets(json: Dict[str, Any]) -> List[TrismikDataset]:
35
+ """
36
+ Convert JSON response to a list of TrismikDataset objects.
37
+
38
+ Args:
39
+ json (Dict[str, Any]): JSON response containing dataset data.
40
+
41
+ Returns:
42
+ List[TrismikDataset]: List of
43
+ dataset objects with IDs and names.
44
+ """
29
45
  return [
30
- TrismikTest(
31
- id=item["id"],
32
- name=item["name"],
33
- ) for item in json
46
+ TrismikDataset(
47
+ id=item["id"],
48
+ name=item["name"],
49
+ )
50
+ for item in json["data"]
34
51
  ]
35
52
 
36
53
  @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"],
54
+ def to_run(json: Dict[str, Any]) -> TrismikRun:
55
+ """
56
+ Convert JSON response to a TrismikRun object.
57
+
58
+ Args:
59
+ json (Dict[str, Any]): JSON response containing run data.
60
+
61
+ Returns:
62
+ TrismikRun: Run object with ID, URL, and status.
63
+ """
64
+ return TrismikRun(
65
+ id=json["id"],
66
+ url=json["url"],
67
+ status=json["status"],
42
68
  )
43
69
 
44
70
  @staticmethod
45
- def to_item(json: dict[str, Any]) -> TrismikItem:
46
- if json["type"] == "multiple_choice_text":
71
+ def to_run_info(json: Dict[str, Any]) -> TrismikRunInfo:
72
+ """
73
+ Convert JSON response to a TrismikRunInfo object.
74
+
75
+ Args:
76
+ json (Dict[str, Any]): JSON response containing run info.
77
+
78
+ Returns:
79
+ TrismikRunInfo: Run info object with ID.
80
+ """
81
+ return TrismikRunInfo(id=json["id"])
82
+
83
+ @staticmethod
84
+ def to_run_state(json: Dict[str, Any]) -> TrismikRunState:
85
+ """
86
+ Convert JSON response to a TrismikRunState object.
87
+
88
+ Args:
89
+ json (Dict[str, Any]): JSON response containing run state.
90
+
91
+ Returns:
92
+ TrismikRunState: Run state object.
93
+ """
94
+ return TrismikRunState(
95
+ responses=json.get("responses", []),
96
+ thetas=json.get("thetas", []),
97
+ std_error_history=json.get("std_error_history", []),
98
+ kl_info_history=json.get("kl_info_history", []),
99
+ effective_difficulties=json.get("effective_difficulties", []),
100
+ )
101
+
102
+ @staticmethod
103
+ def to_run_response(json: Dict[str, Any]) -> TrismikRunResponse:
104
+ """
105
+ Convert JSON response to a TrismikRunResponse object.
106
+
107
+ Args:
108
+ json (Dict[str, Any]): JSON response from run endpoints.
109
+
110
+ Returns:
111
+ TrismikRunResponse: Run response.
112
+ """
113
+ return TrismikRunResponse(
114
+ run_info=TrismikResponseMapper.to_run_info(json["runInfo"]),
115
+ state=TrismikResponseMapper.to_run_state(json["state"]),
116
+ next_item=(
117
+ TrismikResponseMapper.to_item(json["nextItem"])
118
+ if json.get("nextItem")
119
+ else None
120
+ ),
121
+ completed=json.get("completed", False),
122
+ )
123
+
124
+ @staticmethod
125
+ def to_run_summary(json: Dict[str, Any]) -> TrismikRunSummary:
126
+ """
127
+ Convert JSON response to a TrismikRunSummary object.
128
+
129
+ Args:
130
+ json (Dict[str, Any]): JSON response from run summary endpoint.
131
+
132
+ Returns:
133
+ TrismikRunSummary: Complete run summary.
134
+ """
135
+ return TrismikRunSummary(
136
+ id=json["id"],
137
+ dataset_id=json["datasetId"],
138
+ state=TrismikResponseMapper.to_run_state(json["state"]),
139
+ dataset=[
140
+ TrismikResponseMapper.to_item(item)
141
+ for item in json.get("dataset", [])
142
+ ],
143
+ responses=TrismikResponseMapper.to_responses(
144
+ json.get("responses", [])
145
+ ),
146
+ metadata=json.get("metadata", {}),
147
+ )
148
+
149
+ @staticmethod
150
+ def to_item(json: Dict[str, Any]) -> TrismikItem:
151
+ """
152
+ Convert JSON response to a TrismikItem object.
153
+
154
+ Args:
155
+ json (Dict[str, Any]): JSON response containing item data.
156
+
157
+ Returns:
158
+ TrismikItem: Item object with question and choices.
159
+
160
+ Raises:
161
+ TrismikApiError: If the item type is not recognized.
162
+ """
163
+ if "question" in json and "choices" in json:
47
164
  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
- ]
165
+ id=json["id"],
166
+ question=json["question"],
167
+ choices=[
168
+ TrismikTextChoice(
169
+ id=choice["id"],
170
+ text=choice["value"],
171
+ )
172
+ for choice in json["choices"]
173
+ ],
56
174
  )
57
175
  else:
176
+ item_type = json.get("type", "unknown")
58
177
  raise TrismikApiError(
59
- f"API has returned unrecognized item type: {json['type']}")
178
+ f"API has returned unrecognized item type: {item_type}"
179
+ )
60
180
 
61
181
  @staticmethod
62
- def to_results(json: List[dict[str, Any]]) -> List[TrismikResult]:
182
+ def to_results(json: List[Dict[str, Any]]) -> List[TrismikResult]:
183
+ """
184
+ Convert JSON response to a list of TrismikResult objects.
185
+
186
+ Args:
187
+ json (List[Dict[str, Any]]): JSON response containing result data.
188
+
189
+ Returns:
190
+ List[TrismikResult]: List of result objects with trait, name, and
191
+ value.
192
+ """
63
193
  return [
64
194
  TrismikResult(
65
- trait=item["trait"],
66
- name=item["name"],
67
- value=item["value"],
68
- ) for item in json
195
+ trait=item["trait"],
196
+ name=item["name"],
197
+ value=item["value"],
198
+ )
199
+ for item in json
69
200
  ]
70
201
 
71
202
  @staticmethod
72
- def to_responses(json: List[dict[str, Any]]) -> List[TrismikResponse]:
203
+ def to_responses(json: List[Dict[str, Any]]) -> List[TrismikResponse]:
204
+ """
205
+ Convert JSON response to a list of TrismikResponse objects.
206
+
207
+ Args:
208
+ json (List[Dict[str, Any]]): JSON response containing response data.
209
+
210
+ Returns:
211
+ List[TrismikResponse]: List of response objects with dataset item
212
+ ID, value, and correctness.
213
+ """
73
214
  return [
74
215
  TrismikResponse(
75
- item_id=response["itemId"],
76
- value=response["value"],
77
- score=response["score"],
78
- ) for response in json
216
+ dataset_item_id=response["datasetItemId"],
217
+ value=response["value"],
218
+ correct=response["correct"],
219
+ )
220
+ for response in json
79
221
  ]
222
+
223
+ @staticmethod
224
+ def to_replay_response(json: Dict[str, Any]) -> TrismikReplayResponse:
225
+ """
226
+ Convert JSON response to a TrismikReplayResponse object.
227
+
228
+ Args:
229
+ json (Dict[str, Any]): JSON response from replay endpoint.
230
+
231
+ Returns:
232
+ TrismikReplayResponse: Replay response object.
233
+ """
234
+ # Parse datetime strings if they exist
235
+ completed_at = None
236
+ if "completedAt" in json and json["completedAt"]:
237
+ completed_at = datetime.fromisoformat(
238
+ json["completedAt"].replace("Z", "+00:00")
239
+ )
240
+
241
+ created_at = None
242
+ if "createdAt" in json and json["createdAt"]:
243
+ created_at = datetime.fromisoformat(
244
+ json["createdAt"].replace("Z", "+00:00")
245
+ )
246
+
247
+ return TrismikReplayResponse(
248
+ id=json["id"],
249
+ datasetId=json["datasetId"],
250
+ state=TrismikResponseMapper.to_run_state(json["state"]),
251
+ replay_of_run=json["replayOfRun"],
252
+ completedAt=completed_at,
253
+ createdAt=created_at,
254
+ metadata=json.get("metadata", {}),
255
+ dataset=[
256
+ TrismikResponseMapper.to_item(item)
257
+ for item in json.get("dataset", [])
258
+ ],
259
+ responses=TrismikResponseMapper.to_responses(
260
+ json.get("responses", [])
261
+ ),
262
+ )
263
+
264
+ @staticmethod
265
+ def to_me_response(json: Dict[str, Any]) -> TrismikMeResponse:
266
+ """
267
+ Convert JSON response to a TrismikMeResponse object.
268
+
269
+ Args:
270
+ json (Dict[str, Any]): JSON response from /admin/api-keys/me
271
+ endpoint.
272
+
273
+ Returns:
274
+ TrismikMeResponse: Me response object.
275
+ """
276
+ user_data = json["user"]
277
+ organization_data = json["organization"]
278
+
279
+ user_info = TrismikUserInfo(
280
+ id=user_data["id"],
281
+ email=user_data["email"],
282
+ firstname=user_data["firstname"],
283
+ lastname=user_data["lastname"],
284
+ createdAt=user_data.get("createdAt"),
285
+ )
286
+
287
+ organization = TrismikOrganization(
288
+ id=organization_data["id"],
289
+ name=organization_data["name"],
290
+ type=organization_data["type"],
291
+ role=organization_data["role"],
292
+ )
293
+
294
+ return TrismikMeResponse(
295
+ user=user_info,
296
+ organization=organization,
297
+ )
298
+
299
+ @staticmethod
300
+ def to_classic_eval_response(
301
+ json: Dict[str, Any]
302
+ ) -> TrismikClassicEvalResponse:
303
+ """
304
+ Convert JSON response to a TrismikClassicEvalResponse object.
305
+
306
+ Args:
307
+ json (Dict[str, Any]): JSON response from classic evaluation
308
+ endpoint.
309
+
310
+ Returns:
311
+ TrismikClassicEvalResponse: Classic evaluation response object.
312
+ """
313
+ user_data = json["user"]
314
+ user_info = TrismikUserInfo(
315
+ id=user_data["id"],
316
+ email=user_data["email"],
317
+ firstname=user_data["firstname"],
318
+ lastname=user_data["lastname"],
319
+ )
320
+
321
+ return TrismikClassicEvalResponse(
322
+ id=json["id"],
323
+ organizationId=json["organizationId"],
324
+ projectId=json["projectId"],
325
+ experimentId=json["experimentId"],
326
+ experimentName=json["experimentName"],
327
+ datasetId=json["datasetId"],
328
+ userId=json["userId"],
329
+ type=json["type"],
330
+ modelName=json["modelName"],
331
+ hyperparameters=json.get("hyperparameters", {}),
332
+ createdAt=json["createdAt"],
333
+ user=user_info,
334
+ responseCount=json["responseCount"],
335
+ )
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
+ )