prediction-market-agent-tooling 0.56.0.dev1859__py3-none-any.whl → 0.56.0.dev1860__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.
@@ -114,6 +114,7 @@ def db_cache(
114
114
 
115
115
  @wraps(func)
116
116
  def wrapper(*args: Any, **kwargs: Any) -> Any:
117
+ # If caching is disabled, just call the function and return it
117
118
  if not api_keys.ENABLE_CACHE:
118
119
  return func(*args, **kwargs)
119
120
 
@@ -183,11 +184,15 @@ def db_cache(
183
184
 
184
185
  if cached_result:
185
186
  logger.info(
186
- f"Cache hit for {full_function_name} with args {args_dict} and output {cached_result.result}"
187
+ # Keep the special [case-hit] identifier so we can easily track it in GCP.
188
+ f"[cache-hit] Cache hit for {full_function_name} with args {args_dict} and output {cached_result.result}"
187
189
  )
188
190
  if is_pydantic_model:
191
+ # If the output contains any Pydantic models, we need to initialise them.
189
192
  try:
190
- return convert_to_pydantic(return_type, cached_result.result)
193
+ return convert_return_type_to_pydantic(
194
+ return_type, cached_result.result
195
+ )
191
196
  except ValueError as e:
192
197
  # In case of backward-incompatible pydantic model, just treat it as cache miss, to not error out.
193
198
  logger.warning(
@@ -199,13 +204,14 @@ def db_cache(
199
204
 
200
205
  # On cache miss, compute the result
201
206
  computed_result = func(*args, **kwargs)
207
+ # Keep the special [case-miss] identifier so we can easily track it in GCP.
202
208
  logger.info(
203
- f"Cache miss for {full_function_name} with args {args_dict}, computed the output {computed_result}"
209
+ f"[cache-miss] Cache miss for {full_function_name} with args {args_dict}, computed the output {computed_result}"
204
210
  )
205
211
 
206
- # If postgres access was specified, save it to dB.
212
+ # If postgres access was specified, save it.
207
213
  if engine is not None and (cache_none or computed_result is not None):
208
- # Call the original function
214
+ # In case of Pydantic outputs, we need to dictionarize it for it to be serializable.
209
215
  result_data = (
210
216
  convert_pydantic_to_dict(computed_result)
211
217
  if is_pydantic_model
@@ -229,18 +235,25 @@ def db_cache(
229
235
  return cast(FunctionT, wrapper)
230
236
 
231
237
 
232
- def contains_pydantic_model(tp: Any) -> bool:
233
- if tp is None:
238
+ def contains_pydantic_model(return_type: Any) -> bool:
239
+ """
240
+ Check if the return type contains anything that's a Pydantic model (including nested structures, like `list[BaseModel]`, `dict[str, list[BaseModel]]`, etc.)
241
+ """
242
+ if return_type is None:
234
243
  return False
235
- origin = get_origin(tp)
244
+ origin = get_origin(return_type)
236
245
  if origin is not None:
237
- return any(contains_pydantic_model(arg) for arg in get_args(tp))
238
- if inspect.isclass(tp):
239
- return issubclass(tp, BaseModel)
246
+ return any(contains_pydantic_model(arg) for arg in get_args(return_type))
247
+ if inspect.isclass(return_type):
248
+ return issubclass(return_type, BaseModel)
240
249
  return False
241
250
 
242
251
 
243
- def json_serializer_default_fn(y: Any) -> Any:
252
+ def json_serializer_default_fn(y: DatetimeUTC | timedelta | date) -> str:
253
+ """
254
+ Used to serialize objects that don't support it by default into a specific string that can be deserialized out later.
255
+ If you add something here, also add it to `replace_custom_stringified_objects` below.
256
+ """
244
257
  if isinstance(y, DatetimeUTC):
245
258
  return f"DatetimeUTC::{y.isoformat()}"
246
259
  elif isinstance(y, timedelta):
@@ -257,6 +270,9 @@ def json_serializer(x: Any) -> str:
257
270
 
258
271
 
259
272
  def replace_custom_stringified_objects(obj: Any) -> Any:
273
+ """
274
+ Used to deserialize objects from `json_serializer_default_fn` into their proper form.
275
+ """
260
276
  if isinstance(obj, str):
261
277
  if obj.startswith("DatetimeUTC::"):
262
278
  iso_str = obj[len("DatetimeUTC::") :]
@@ -283,6 +299,9 @@ def json_deserializer(s: str) -> Any:
283
299
 
284
300
 
285
301
  def convert_pydantic_to_dict(value: Any) -> Any:
302
+ """
303
+ Convert Pydantic models to dictionaries, including if they are in nested structures.
304
+ """
286
305
  if isinstance(value, BaseModel):
287
306
  return value.model_dump()
288
307
  elif isinstance(value, dict):
@@ -295,20 +314,25 @@ def convert_pydantic_to_dict(value: Any) -> Any:
295
314
  return value
296
315
 
297
316
 
298
- def convert_to_pydantic(model: Any, data: Any) -> Any:
317
+ def convert_return_type_to_pydantic(return_type: Any, data: Any) -> Any:
318
+ """
319
+ Used to initialize Pydantic models from anything cached that was originally a Pydantic model in the output. Including models in nested structures.
320
+ """
299
321
  # Get the origin and arguments of the model type
300
- origin = get_origin(model)
301
- args = get_args(model)
322
+ origin = get_origin(return_type)
323
+ args = get_args(return_type)
302
324
 
303
325
  # Check if the data is a dictionary
304
326
  if isinstance(data, dict):
305
327
  # If the model has no origin, check if it is a subclass of BaseModel
306
328
  if origin is None:
307
- if inspect.isclass(model) and issubclass(model, BaseModel):
329
+ if inspect.isclass(return_type) and issubclass(return_type, BaseModel):
308
330
  # Convert the dictionary to a Pydantic model
309
- return model(
331
+ return return_type(
310
332
  **{
311
- k: convert_to_pydantic(getattr(model, k, None), v)
333
+ k: convert_return_type_to_pydantic(
334
+ getattr(return_type, k, None), v
335
+ )
312
336
  for k, v in data.items()
313
337
  }
314
338
  )
@@ -319,20 +343,25 @@ def convert_to_pydantic(model: Any, data: Any) -> Any:
319
343
  elif origin is dict:
320
344
  key_type, value_type = args
321
345
  return {
322
- convert_to_pydantic(key_type, k): convert_to_pydantic(value_type, v)
346
+ convert_return_type_to_pydantic(
347
+ key_type, k
348
+ ): convert_return_type_to_pydantic(value_type, v)
323
349
  for k, v in data.items()
324
350
  }
325
351
  else:
326
352
  # If the origin is not a dictionary, return the data as is
327
353
  return data
328
354
  # Check if the data is a list
329
- elif isinstance(data, list):
330
- # If the origin is a list, convert each item
331
- if origin is list:
355
+ elif isinstance(data, (list, tuple)):
356
+ # If the origin is a list or tuple, convert each item
357
+ if origin in {list, tuple}:
332
358
  item_type = args[0]
333
- return [convert_to_pydantic(item_type, item) for item in data]
359
+ converted_items = [
360
+ convert_return_type_to_pydantic(item_type, item) for item in data
361
+ ]
362
+ return type(data)(converted_items)
334
363
  else:
335
- # If the origin is not a list, return the data as is
364
+ # If the origin is not a list or tuple, return the data as is
336
365
  return data
337
366
  else:
338
367
  # If the data is neither a dictionary nor a list, return it as is
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.56.0.dev1859
3
+ Version: 0.56.0.dev1860
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.12
@@ -69,7 +69,7 @@ prediction_market_agent_tooling/tools/betting_strategies/market_moving.py,sha256
69
69
  prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py,sha256=-FUSuQQgjcWSSnoFxnlAyTeilY6raJABJVM2QKkFqAY,438
70
70
  prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py,sha256=THMXwFlskvzbjnX_OiYtDSzI8XVFyULWfP2525_9UGc,429
71
71
  prediction_market_agent_tooling/tools/betting_strategies/utils.py,sha256=kpIb-ci67Vc1Yqqaa-_S4OUkbhWSIYog4_Iwp69HU_k,97
72
- prediction_market_agent_tooling/tools/caches/db_cache.py,sha256=2a1bIDQk1KmVcp6cR8nSDKyT7rWnBQ7oqmqLjw49sps,12196
72
+ prediction_market_agent_tooling/tools/caches/db_cache.py,sha256=QkhtNTs7sjwBhBJpkF_dWO5UZBE8Z1FT03WXskpakQg,13847
73
73
  prediction_market_agent_tooling/tools/caches/inmemory_cache.py,sha256=tGHHd9HCiE_hCCtPtloHZQdDfBuiow9YsqJNYi2Tx_0,499
74
74
  prediction_market_agent_tooling/tools/contract.py,sha256=s3yo8IbXTcvAJcPfLM0_NbgaEsWwLsPmyVnOgyjq_xI,20919
75
75
  prediction_market_agent_tooling/tools/costs.py,sha256=EaAJ7v9laD4VEV3d8B44M4u3_oEO_H16jRVCdoZ93Uw,954
@@ -97,8 +97,8 @@ prediction_market_agent_tooling/tools/tavily/tavily_models.py,sha256=5ldQs1pZe6u
97
97
  prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=Kw2mXNkMTYTEe1MBSTqhQmLoeXtgb6CkmHlcAJvhtqE,3809
98
98
  prediction_market_agent_tooling/tools/utils.py,sha256=W-9SqeCKd51BYMRhDjYPQ7lfNO_zE9EvYpmu2r5WXGA,7163
99
99
  prediction_market_agent_tooling/tools/web3_utils.py,sha256=44W8siSLNQxeib98bbwAe7V5C609NHNlUuxwuWIRDiY,11838
100
- prediction_market_agent_tooling-0.56.0.dev1859.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
101
- prediction_market_agent_tooling-0.56.0.dev1859.dist-info/METADATA,sha256=wjkMWBepYIs-EtTIlQLUXsg4Vkg_XNEop7Pz-l789Po,8114
102
- prediction_market_agent_tooling-0.56.0.dev1859.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
103
- prediction_market_agent_tooling-0.56.0.dev1859.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
104
- prediction_market_agent_tooling-0.56.0.dev1859.dist-info/RECORD,,
100
+ prediction_market_agent_tooling-0.56.0.dev1860.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
101
+ prediction_market_agent_tooling-0.56.0.dev1860.dist-info/METADATA,sha256=D5iUZsQ2RbKbhyVvKEtc9A40BfZTJaJWoEqjLreNF_Q,8114
102
+ prediction_market_agent_tooling-0.56.0.dev1860.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
103
+ prediction_market_agent_tooling-0.56.0.dev1860.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
104
+ prediction_market_agent_tooling-0.56.0.dev1860.dist-info/RECORD,,