haystack-ml-stack 0.3.2__tar.gz → 0.3.4__tar.gz

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.
Files changed (24) hide show
  1. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/PKG-INFO +1 -1
  2. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/pyproject.toml +1 -1
  3. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/__init__.py +1 -1
  4. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/app.py +18 -2
  5. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/utils.py +2 -2
  6. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack.egg-info/PKG-INFO +1 -1
  7. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/tests/test_utils.py +127 -0
  8. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/README.md +0 -0
  9. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/setup.cfg +0 -0
  10. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/_serializers.py +0 -0
  11. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/cache.py +0 -0
  12. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/dynamo.py +0 -0
  13. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/exceptions.py +0 -0
  14. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/generated/__init__.py +0 -0
  15. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/generated/v1/__init__.py +0 -0
  16. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/generated/v1/features_pb2.py +0 -0
  17. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/generated/v1/features_pb2.pyi +0 -0
  18. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/model_store.py +0 -0
  19. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack/settings.py +0 -0
  20. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack.egg-info/SOURCES.txt +0 -0
  21. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack.egg-info/dependency_links.txt +0 -0
  22. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack.egg-info/requires.txt +0 -0
  23. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/src/haystack_ml_stack.egg-info/top_level.txt +0 -0
  24. {haystack_ml_stack-0.3.2 → haystack_ml_stack-0.3.4}/tests/test_serializers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haystack-ml-stack
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Functions related to Haystack ML
5
5
  Author-email: Oscar Vega <oscar@haystack.tv>
6
6
  License: MIT
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "haystack-ml-stack"
8
- version = "0.3.2"
8
+ version = "0.3.4"
9
9
  description = "Functions related to Haystack ML"
10
10
  readme = "README.md"
11
11
  authors = [{ name = "Oscar Vega", email = "oscar@haystack.tv" }]
@@ -11,4 +11,4 @@ from ._serializers import SerializerRegistry, FeatureRegistryId
11
11
 
12
12
  __all__ = [*__all__, "SerializerRegistry", "FeatureRegistryId"]
13
13
 
14
- __version__ = "0.3.2"
14
+ __version__ = "0.3.4"
@@ -6,6 +6,8 @@ from http import HTTPStatus
6
6
  from typing import Any, Dict, List, Optional
7
7
  import time
8
8
  from contextlib import asynccontextmanager, AsyncExitStack
9
+ import traceback
10
+ import json
9
11
 
10
12
  import aiobotocore.session
11
13
  from aiobotocore.config import AioConfig
@@ -160,6 +162,7 @@ def create_app(
160
162
  "user_cache_size": len(user_features_cache),
161
163
  "model_name": state.get("model_name"),
162
164
  "stream_features": state.get("stream_features", []),
165
+ "user_features": state.get("user_features", []),
163
166
  }
164
167
 
165
168
  @app.post("/score", status_code=HTTPStatus.OK)
@@ -172,10 +175,23 @@ def create_app(
172
175
 
173
176
  try:
174
177
  data = await request.json()
175
- except Exception as e:
178
+ except json.JSONDecodeError as e:
179
+ body = await request.body()
180
+ logger.error(
181
+ "Received malformed json. Raw body: %s\n%s",
182
+ body.decode(errors="replace"),
183
+ traceback.format_exc(),
184
+ )
176
185
  raise HTTPException(
177
186
  status_code=HTTPStatus.BAD_REQUEST, detail="Invalid JSON payload"
178
187
  ) from e
188
+ except Exception as e:
189
+ logger.error(
190
+ "Unexpected exception when parsing request.\n %s", traceback.format_exc()
191
+ )
192
+ raise HTTPException(
193
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Unknown exception"
194
+ ) from e
179
195
  query_params = {}
180
196
  for k in request.query_params.keys():
181
197
  values = request.query_params.getlist(k)
@@ -253,7 +269,7 @@ def create_app(
253
269
  model_output = model["predict"](model_input, model["params"])
254
270
  predict_end = time.perf_counter_ns()
255
271
  except Exception as e:
256
- logger.error("Model prediction failed: %s", e)
272
+ logger.error("Model prediction failed: \n%s", traceback.format_exc())
257
273
  raise HTTPException(
258
274
  status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
259
275
  detail="Model prediction failed",
@@ -204,7 +204,7 @@ def device_watched_count_cleanups(
204
204
 
205
205
  counts_obj: StreamPWatched = stream.get(
206
206
  f"PWATCHED#24H#{device_type}", StreamPWatched()
207
- )
207
+ ).data
208
208
  if out is None:
209
209
  out = {}
210
210
  out = _cleanup_entry_context_counts(
@@ -336,7 +336,7 @@ def user_pwatched_cleanup(
336
336
  "launch_first_in_session",
337
337
  ]
338
338
  _validate_pwatched_entry_context(entry_contexts)
339
- counts_obj = user.get("PWATCHED#6M", UserPWatched())
339
+ counts_obj = user.get("PWATCHED#6M", UserPWatched()).data
340
340
  out = _cleanup_entry_context_counts(
341
341
  counts_obj=counts_obj,
342
342
  entry_contexts=entry_contexts,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haystack-ml-stack
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Functions related to Haystack ML
5
5
  Author-email: Oscar Vega <oscar@haystack.tv>
6
6
  License: MIT
@@ -372,6 +372,58 @@ def test_stream_pwatched_cleanup():
372
372
  assert (actual == expected).all()
373
373
 
374
374
 
375
+ def test_device_stream_pwatched_cleanup():
376
+ stream_pwatched_data = {
377
+ "version": 1,
378
+ "data": {
379
+ "autoplay": {"attempts": 1, "watched": 1},
380
+ "ch_swtch": {"attempts": 2, "watched": 0},
381
+ },
382
+ }
383
+ stream_pwatched = features_pb2_v1.StreamPWatched()
384
+ ProtoParseDict(js_dict=stream_pwatched_data, message=stream_pwatched)
385
+ stream = {"PWATCHED#24H#TV": stream_pwatched}
386
+ out = {}
387
+ utils.device_watched_count_cleanups(
388
+ stream=stream,
389
+ entry_contexts=["autoplay", "ch_swtch", "sel_thumb"],
390
+ device_type="TV",
391
+ out=out,
392
+ )
393
+ expected = pd.Series(
394
+ {
395
+ "STREAM_AUTOPLAY_TV_24H_TOTAL_ATTEMPTS": 1,
396
+ "STREAM_AUTOPLAY_TV_24H_TOTAL_WATCHED": 1,
397
+ "STREAM_CH_SWTCH_TV_24H_TOTAL_ATTEMPTS": 2,
398
+ "STREAM_CH_SWTCH_TV_24H_TOTAL_WATCHED": 0,
399
+ "STREAM_SEL_THUMB_TV_24H_TOTAL_ATTEMPTS": 0,
400
+ "STREAM_SEL_THUMB_TV_24H_TOTAL_WATCHED": 0,
401
+ }
402
+ )
403
+ actual = pd.Series(out).loc[expected.index]
404
+ assert (actual == expected).all()
405
+ stream = {"PWATCHED#24H#MOBILE": stream_pwatched}
406
+ out = {}
407
+ utils.device_watched_count_cleanups(
408
+ stream=stream,
409
+ entry_contexts=["autoplay", "ch_swtch", "sel_thumb"],
410
+ device_type="MOBILE",
411
+ out=out,
412
+ )
413
+ expected = pd.Series(
414
+ {
415
+ "STREAM_AUTOPLAY_MOBILE_24H_TOTAL_ATTEMPTS": 1,
416
+ "STREAM_AUTOPLAY_MOBILE_24H_TOTAL_WATCHED": 1,
417
+ "STREAM_CH_SWTCH_MOBILE_24H_TOTAL_ATTEMPTS": 2,
418
+ "STREAM_CH_SWTCH_MOBILE_24H_TOTAL_WATCHED": 0,
419
+ "STREAM_SEL_THUMB_MOBILE_24H_TOTAL_ATTEMPTS": 0,
420
+ "STREAM_SEL_THUMB_MOBILE_24H_TOTAL_WATCHED": 0,
421
+ }
422
+ )
423
+ actual = pd.Series(out).loc[expected.index]
424
+ assert (actual == expected).all()
425
+
426
+
375
427
  def test_stream_global_pselect_cleanup():
376
428
  stream_pselect_data = {
377
429
  "version": 1,
@@ -508,3 +560,78 @@ def test_stream_similarity_top_category_functions():
508
560
  assert all(
509
561
  actual_key == expected_key for actual_key, expected_key in zip(actual, expected)
510
562
  )
563
+
564
+
565
+ def test_user_pwatched_cleanup():
566
+ user_pwatched_data = {
567
+ "version": 1,
568
+ "data": {
569
+ "sel_thumb": {"attempts": 1, "watched": 1},
570
+ "ch_swtch": {"attempts": 2, "watched": 0},
571
+ },
572
+ }
573
+ user_pwatched_msg = features_pb2_v1.UserPWatched()
574
+ ProtoParseDict(js_dict=user_pwatched_data, message=user_pwatched_msg)
575
+ user = {"PWATCHED#6M": user_pwatched_msg}
576
+ out = {}
577
+ utils.user_pwatched_cleanup(
578
+ user=user, entry_contexts=["autoplay", "sel_thumb", "ch_swtch"], out=out
579
+ )
580
+ expected = pd.Series(
581
+ {
582
+ "USER_AUTOPLAY_6M_TOTAL_ATTEMPTS": 0,
583
+ "USER_AUTOPLAY_6M_TOTAL_WATCHED": 0,
584
+ "USER_SEL_THUMB_6M_TOTAL_ATTEMPTS": 1,
585
+ "USER_SEL_THUMB_6M_TOTAL_WATCHED": 1,
586
+ "USER_CH_SWTCH_6M_TOTAL_ATTEMPTS": 2,
587
+ "USER_CH_SWTCH_6M_TOTAL_WATCHED": 0,
588
+ }
589
+ )
590
+ actual = pd.Series(out).loc[expected.index]
591
+ assert (expected == actual).all()
592
+
593
+
594
+ def test_user_pselect_cleanup():
595
+ user_pselect_data = {
596
+ "version": 1,
597
+ "data": {
598
+ "all_browsed": {
599
+ "first_pos": {
600
+ "total_selects": 0,
601
+ "total_selects_and_watched": 0,
602
+ "total_browsed": 1,
603
+ },
604
+ "rest_pos": {
605
+ "total_selects": 2,
606
+ "total_selects_and_watched": 2,
607
+ "total_browsed": 1,
608
+ },
609
+ },
610
+ "up_to_4_browsed": {
611
+ "first_pos": {
612
+ "total_selects": 0,
613
+ "total_selects_and_watched": 0,
614
+ "total_browsed": 1,
615
+ },
616
+ "rest_pos": {
617
+ "total_selects": 2,
618
+ "total_selects_and_watched": 2,
619
+ "total_browsed": 0,
620
+ },
621
+ },
622
+ },
623
+ }
624
+ user_pselect_msg = features_pb2_v1.UserPSelect()
625
+ ProtoParseDict(js_dict=user_pselect_data, message=user_pselect_msg)
626
+ user = {"PSELECT#6M": user_pselect_msg}
627
+ out = {}
628
+ utils.user_pselect_cleanup(user=user, position_debiasing="up_to_4_browsed", out=out)
629
+ expected = pd.Series(
630
+ {
631
+ "USER_6M_TOTAL_BROWSED_UP_TO_4_BROWSED": 1,
632
+ "USER_6M_TOTAL_SELECTS_UP_TO_4_BROWSED": 2,
633
+ "USER_6M_TOTAL_SELECTS_AND_WATCHED_UP_TO_4_BROWSED": 2,
634
+ }
635
+ )
636
+ actual = pd.Series(out).loc[expected.index]
637
+ assert (actual == expected).all()