rapidata 2.35.0__py3-none-any.whl → 2.35.2__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 rapidata might be problematic. Click here for more details.

rapidata/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "2.35.0"
1
+ __version__ = "2.35.2"
2
2
 
3
3
  from .rapidata_client import (
4
4
  RapidataClient,
@@ -3117,7 +3117,7 @@ class LeaderboardApi:
3117
3117
  _headers: Optional[Dict[StrictStr, Any]] = None,
3118
3118
  _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
3119
3119
  ) -> None:
3120
- """Updates the name of a leaderboard.
3120
+ """Updates the response config of a leaderboard.
3121
3121
 
3122
3122
 
3123
3123
  :param leaderboard_id: (required)
@@ -3187,7 +3187,7 @@ class LeaderboardApi:
3187
3187
  _headers: Optional[Dict[StrictStr, Any]] = None,
3188
3188
  _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
3189
3189
  ) -> ApiResponse[None]:
3190
- """Updates the name of a leaderboard.
3190
+ """Updates the response config of a leaderboard.
3191
3191
 
3192
3192
 
3193
3193
  :param leaderboard_id: (required)
@@ -3257,7 +3257,7 @@ class LeaderboardApi:
3257
3257
  _headers: Optional[Dict[StrictStr, Any]] = None,
3258
3258
  _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
3259
3259
  ) -> RESTResponseType:
3260
- """Updates the name of a leaderboard.
3260
+ """Updates the response config of a leaderboard.
3261
3261
 
3262
3262
 
3263
3263
  :param leaderboard_id: (required)
@@ -135,7 +135,7 @@ Class | Method | HTTP request | Description
135
135
  *LeaderboardApi* | [**leaderboard_leaderboard_id_participants_post**](rapidata/api_client/docs/LeaderboardApi.md#leaderboard_leaderboard_id_participants_post) | **POST** /leaderboard/{leaderboardId}/participants | Creates a participant in a leaderboard.
136
136
  *LeaderboardApi* | [**leaderboard_leaderboard_id_prompts_get**](rapidata/api_client/docs/LeaderboardApi.md#leaderboard_leaderboard_id_prompts_get) | **GET** /leaderboard/{leaderboardId}/prompts | returns the paged prompts of a leaderboard by its ID.
137
137
  *LeaderboardApi* | [**leaderboard_leaderboard_id_prompts_post**](rapidata/api_client/docs/LeaderboardApi.md#leaderboard_leaderboard_id_prompts_post) | **POST** /leaderboard/{leaderboardId}/prompts | adds a new prompt to a leaderboard.
138
- *LeaderboardApi* | [**leaderboard_leaderboard_id_response_config_put**](rapidata/api_client/docs/LeaderboardApi.md#leaderboard_leaderboard_id_response_config_put) | **PUT** /leaderboard/{leaderboardId}/response-config | Updates the name of a leaderboard.
138
+ *LeaderboardApi* | [**leaderboard_leaderboard_id_response_config_put**](rapidata/api_client/docs/LeaderboardApi.md#leaderboard_leaderboard_id_response_config_put) | **PUT** /leaderboard/{leaderboardId}/response-config | Updates the response config of a leaderboard.
139
139
  *LeaderboardApi* | [**leaderboard_leaderboard_id_runs_get**](rapidata/api_client/docs/LeaderboardApi.md#leaderboard_leaderboard_id_runs_get) | **GET** /leaderboard/{leaderboardId}/runs | Gets the runs related to a leaderboard
140
140
  *LeaderboardApi* | [**leaderboard_leaderboard_id_standings_get**](rapidata/api_client/docs/LeaderboardApi.md#leaderboard_leaderboard_id_standings_get) | **GET** /leaderboard/{leaderboardId}/standings | queries all the participants connected to leaderboard by its ID.
141
141
  *LeaderboardApi* | [**leaderboard_post**](rapidata/api_client/docs/LeaderboardApi.md#leaderboard_post) | **POST** /leaderboard | Creates a new leaderboard with the specified name and criteria.
@@ -2,106 +2,135 @@ from typing import Optional, Any
2
2
  from rapidata.api_client.api_client import ApiClient, rest, ApiResponse, ApiResponseT
3
3
  from rapidata.api_client.exceptions import ApiException
4
4
  import json
5
+ import threading
6
+ from contextlib import contextmanager
5
7
  from rapidata.rapidata_client.logging import logger
6
8
 
9
+ # Thread-local storage for controlling error logging
10
+ _thread_local = threading.local()
11
+
12
+
13
+ @contextmanager
14
+ def suppress_rapidata_error_logging():
15
+ """Context manager to suppress error logging for RapidataApiClient calls."""
16
+ old_value = getattr(_thread_local, "suppress_error_logging", False)
17
+ _thread_local.suppress_error_logging = True
18
+ try:
19
+ yield
20
+ finally:
21
+ _thread_local.suppress_error_logging = old_value
22
+
23
+
24
+ def _should_suppress_error_logging() -> bool:
25
+ """Check if error logging should be suppressed for the current thread."""
26
+ return getattr(_thread_local, "suppress_error_logging", False)
27
+
28
+
7
29
  class RapidataError(Exception):
8
30
  """Custom error class for Rapidata API errors."""
9
-
31
+
10
32
  def __init__(
11
- self,
12
- status_code: Optional[int] = None,
13
- message: str | None = None,
33
+ self,
34
+ status_code: Optional[int] = None,
35
+ message: str | None = None,
14
36
  original_exception: Exception | None = None,
15
- details: Any = None
37
+ details: Any = None,
16
38
  ):
17
39
  self.status_code = status_code
18
40
  self.message = message
19
41
  self.original_exception = original_exception
20
42
  self.details = details
21
-
43
+
22
44
  # Create a nice error message
23
45
  error_msg = "Rapidata API Error"
24
46
  if status_code:
25
47
  error_msg += f" ({status_code})"
26
48
  if message:
27
49
  error_msg += f": {message}"
28
-
50
+
29
51
  super().__init__(error_msg)
30
52
 
31
53
  def __str__(self):
32
- """Return a string representation of the error."""
54
+ """Return a string representation of the error."""
33
55
  # Extract information from message if available
34
56
  title = None
35
57
  errors = None
36
58
  trace_id = None
37
-
59
+
38
60
  # Try to extract from details if available and is a dict
39
61
  if self.details and isinstance(self.details, dict):
40
- title = self.details.get('title')
41
- errors = self.details.get('errors')
42
- trace_id = self.details.get('traceId')
43
-
62
+ title = self.details.get("title")
63
+ errors = self.details.get("errors")
64
+ trace_id = self.details.get("traceId")
65
+
44
66
  # Build the error string
45
67
  error_parts = []
46
-
68
+
47
69
  # Main error line
48
70
  if title:
49
71
  error_parts.append(f"{title}")
50
72
  else:
51
73
  error_parts.append(f"{self.message or 'Unknown error'}")
52
-
74
+
53
75
  # Reasons
54
76
  if errors:
55
77
  if isinstance(errors, dict):
56
78
  error_parts.append(f"Reasons: {json.dumps({'errors': errors})}")
57
79
  else:
58
80
  error_parts.append(f"Reasons: {errors}")
59
-
81
+
60
82
  # Trace ID
61
83
  if trace_id:
62
84
  error_parts.append(f"Trace Id: {trace_id}")
63
85
  else:
64
86
  error_parts.append("Trace Id: N/A")
65
-
87
+
66
88
  return "\n".join(error_parts)
67
89
 
90
+
68
91
  class RapidataApiClient(ApiClient):
69
92
  """Custom API client that wraps errors in RapidataError."""
70
93
 
71
94
  def response_deserialize(
72
95
  self,
73
96
  response_data: rest.RESTResponse,
74
- response_types_map: Optional[dict[str, ApiResponseT]] = None
97
+ response_types_map: Optional[dict[str, ApiResponseT]] = None,
75
98
  ) -> ApiResponse[ApiResponseT]:
76
99
  """Override the response_deserialize method to catch and convert exceptions."""
77
100
  try:
78
101
  return super().response_deserialize(response_data, response_types_map)
79
102
  except ApiException as e:
80
- status_code = getattr(e, 'status', None)
103
+ status_code = getattr(e, "status", None)
81
104
  message = str(e)
82
105
  details = None
83
-
106
+
84
107
  # Extract more detailed error message from response body if available
85
- if hasattr(e, 'body') and e.body:
108
+ if hasattr(e, "body") and e.body:
86
109
  try:
87
110
  body_json = json.loads(e.body)
88
111
  if isinstance(body_json, dict):
89
- if 'message' in body_json:
90
- message = body_json['message']
91
- elif 'error' in body_json:
92
- message = body_json['error']
93
-
112
+ if "message" in body_json:
113
+ message = body_json["message"]
114
+ elif "error" in body_json:
115
+ message = body_json["error"]
116
+
94
117
  # Store the full error details for debugging
95
118
  details = body_json
96
119
  except (json.JSONDecodeError, AttributeError):
97
120
  # If we can't parse the body as JSON, use the original message
98
121
  pass
99
-
100
- error_formatted = RapidataError(
101
- status_code=status_code,
102
- message=message,
122
+
123
+ error_formatted = RapidataError(
124
+ status_code=status_code,
125
+ message=message,
103
126
  original_exception=e,
104
- details=details
127
+ details=details,
105
128
  )
106
- logger.error(f"Error: {error_formatted}")
129
+
130
+ # Only log error if not suppressed
131
+ if not _should_suppress_error_logging():
132
+ logger.error("Error: %s", error_formatted)
133
+ else:
134
+ logger.debug("Suppressed Error: %s", error_formatted)
135
+
107
136
  raise error_formatted from None
@@ -2,8 +2,6 @@ from typing import Literal
2
2
 
3
3
 
4
4
  class DetailMapper:
5
- MIN_RESPONSES = 3
6
-
7
5
  @staticmethod
8
6
  def get_budget(
9
7
  level_of_detail: Literal["low", "medium", "high", "very high"]
@@ -31,6 +31,7 @@ class RapidataLeaderboard:
31
31
  show_prompt_asset: bool,
32
32
  inverse_ranking: bool,
33
33
  response_budget: int,
34
+ min_responses_per_matchup: int,
34
35
  id: str,
35
36
  openapi_service: OpenAPIService,
36
37
  ):
@@ -41,6 +42,7 @@ class RapidataLeaderboard:
41
42
  self.__show_prompt_asset = show_prompt_asset
42
43
  self.__inverse_ranking = inverse_ranking
43
44
  self.__response_budget = response_budget
45
+ self.__min_responses_per_matchup = min_responses_per_matchup
44
46
  self.id = id
45
47
 
46
48
  @property
@@ -62,11 +64,41 @@ class RapidataLeaderboard:
62
64
  leaderboard_id=self.id,
63
65
  update_leaderboard_response_config_model=UpdateLeaderboardResponseConfigModel(
64
66
  responseBudget=DetailMapper.get_budget(level_of_detail),
65
- minResponses=DetailMapper.MIN_RESPONSES,
67
+ minResponses=self.__min_responses_per_matchup,
66
68
  ),
67
69
  )
68
70
  self.__response_budget = DetailMapper.get_budget(level_of_detail)
69
71
 
72
+ @property
73
+ def min_responses_per_matchup(self) -> int:
74
+ """
75
+ Returns the minimum number of responses required to be considered for the leaderboard.
76
+ """
77
+ return self.__min_responses_per_matchup
78
+
79
+ @min_responses_per_matchup.setter
80
+ def min_responses_per_matchup(self, min_responses: int):
81
+ """
82
+ Sets the minimum number of responses required to be considered for the leaderboard.
83
+ """
84
+ if not isinstance(min_responses, int):
85
+ raise ValueError("Min responses per matchup must be an integer")
86
+
87
+ if min_responses < 3:
88
+ raise ValueError("Min responses per matchup must be at least 3")
89
+
90
+ logger.debug(
91
+ f"Setting min responses per matchup to {min_responses} for leaderboard {self.name}"
92
+ )
93
+ self.__openapi_service.leaderboard_api.leaderboard_leaderboard_id_response_config_put(
94
+ leaderboard_id=self.id,
95
+ update_leaderboard_response_config_model=UpdateLeaderboardResponseConfigModel(
96
+ responseBudget=self.__response_budget,
97
+ minResponses=min_responses,
98
+ ),
99
+ )
100
+ self.__min_responses_per_matchup = min_responses
101
+
70
102
  @property
71
103
  def show_prompt_asset(self) -> bool:
72
104
  """
@@ -172,6 +172,7 @@ class RapidataBenchmark:
172
172
  leaderboard.show_prompt_asset,
173
173
  leaderboard.is_inversed,
174
174
  leaderboard.response_budget,
175
+ leaderboard.min_responses,
175
176
  leaderboard.id,
176
177
  self.__openapi_service,
177
178
  )
@@ -258,6 +259,7 @@ class RapidataBenchmark:
258
259
  show_prompt_asset: bool = False,
259
260
  inverse_ranking: bool = False,
260
261
  level_of_detail: Literal["low", "medium", "high", "very high"] = "low",
262
+ min_responses_per_matchup: int = 3,
261
263
  ) -> RapidataLeaderboard:
262
264
  """
263
265
  Creates a new leaderboard for the benchmark.
@@ -269,7 +271,14 @@ class RapidataBenchmark:
269
271
  show_prompt_asset: Whether to show the prompt asset to the users. (only works if the prompt asset is a URL) (default: False)
270
272
  inverse_ranking: Whether to inverse the ranking of the leaderboard. (if the question is inversed, e.g. "Which video is worse?")
271
273
  level_of_detail: The level of detail of the leaderboard. This will effect how many comparisons are done per model evaluation. (default: "low")
274
+ min_responses_per_matchup: The minimum number of responses required to be considered for the leaderboard. (default: 3)
272
275
  """
276
+ if not isinstance(min_responses_per_matchup, int):
277
+ raise ValueError("Min responses per matchup must be an integer")
278
+
279
+ if min_responses_per_matchup < 3:
280
+ raise ValueError("Min responses per matchup must be at least 3")
281
+
273
282
  leaderboard_result = self.__openapi_service.leaderboard_api.leaderboard_post(
274
283
  create_leaderboard_model=CreateLeaderboardModel(
275
284
  benchmarkId=self.id,
@@ -278,7 +287,7 @@ class RapidataBenchmark:
278
287
  showPrompt=show_prompt,
279
288
  showPromptAsset=show_prompt_asset,
280
289
  isInversed=inverse_ranking,
281
- minResponses=DetailMapper.MIN_RESPONSES,
290
+ minResponses=min_responses_per_matchup,
282
291
  responseBudget=DetailMapper.get_budget(level_of_detail),
283
292
  )
284
293
  )
@@ -294,6 +303,7 @@ class RapidataBenchmark:
294
303
  show_prompt_asset,
295
304
  inverse_ranking,
296
305
  leaderboard_result.response_budget,
306
+ min_responses_per_matchup,
297
307
  leaderboard_result.id,
298
308
  self.__openapi_service,
299
309
  )
@@ -5,7 +5,7 @@ Defines the MultiAsset class for handling multiple BaseAsset instances.
5
5
 
6
6
  from rapidata.rapidata_client.datapoints.assets._base_asset import BaseAsset
7
7
  from rapidata.rapidata_client.datapoints.assets import MediaAsset, TextAsset
8
- from typing import Iterator, Sequence, cast
8
+ from typing import Iterator, Sequence
9
9
 
10
10
 
11
11
  class MultiAsset(BaseAsset):
@@ -26,16 +26,16 @@ class MultiAsset(BaseAsset):
26
26
  """
27
27
  if len(assets) != 2:
28
28
  raise ValueError("Assets must come in pairs for comparison tasks.")
29
-
29
+
30
30
  for asset in assets:
31
31
  if not isinstance(asset, (TextAsset, MediaAsset)):
32
- raise TypeError("All assets must be a TextAsset or MediaAsset.")
33
-
32
+ raise TypeError("All assets must be a TextAsset or MediaAsset.")
33
+
34
34
  if not all(isinstance(asset, type(assets[0])) for asset in assets):
35
35
  raise ValueError("All assets must be of the same type.")
36
-
36
+
37
37
  self.assets = assets
38
-
38
+
39
39
  def __len__(self) -> int:
40
40
  """
41
41
  Get the number of assets in the MultiAsset.
@@ -56,6 +56,6 @@ class MultiAsset(BaseAsset):
56
56
 
57
57
  def __str__(self) -> str:
58
58
  return f"MultiAsset(assets={self.assets})"
59
-
59
+
60
60
  def __repr__(self) -> str:
61
61
  return f"MultiAsset(assets={self.assets})"