haystack-ml-stack 0.4.5a3__tar.gz → 0.4.7__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 (27) hide show
  1. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/PKG-INFO +1 -1
  2. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/README.md +0 -0
  3. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/pyproject.toml +1 -1
  4. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/__init__.py +0 -0
  5. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/_kafka.py +0 -0
  6. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/_serializers.py +11 -0
  7. haystack_ml_stack-0.4.7/src/haystack_ml_stack/_version.py +1 -0
  8. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/app.py +0 -0
  9. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/cache.py +0 -0
  10. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/dynamo.py +24 -22
  11. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/exceptions.py +0 -0
  12. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/generated/__init__.py +0 -0
  13. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/generated/v1/__init__.py +0 -0
  14. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/generated/v1/features_pb2.py +4 -4
  15. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/generated/v1/features_pb2.pyi +23 -23
  16. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/model_store.py +0 -0
  17. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/settings.py +0 -0
  18. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack/utils.py +15 -0
  19. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack.egg-info/PKG-INFO +1 -1
  20. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack.egg-info/SOURCES.txt +0 -0
  21. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack.egg-info/dependency_links.txt +0 -0
  22. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack.egg-info/requires.txt +0 -0
  23. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/src/haystack_ml_stack.egg-info/top_level.txt +0 -0
  24. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/tests/test_serializers.py +0 -0
  25. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/tests/test_utils.py +0 -0
  26. haystack_ml_stack-0.4.5a3/src/haystack_ml_stack/_version.py +0 -1
  27. {haystack_ml_stack-0.4.5a3 → haystack_ml_stack-0.4.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haystack-ml-stack
3
- Version: 0.4.5a3
3
+ Version: 0.4.7
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.4.5a3"
8
+ version = "0.4.7"
9
9
  description = "Functions related to Haystack ML"
10
10
  readme = "README.md"
11
11
  authors = [{ name = "Oscar Vega", email = "oscar@haystack.tv" }]
@@ -422,6 +422,17 @@ global_playlist_stats_v1_features: list[FeatureRegistryId] = [
422
422
  feature_id="PLAYLIST_CATEGORY_STATS#1D#TV",
423
423
  version="v1",
424
424
  ),
425
+ # Same feature structure but with an updated filter for the source query
426
+ FeatureRegistryId(
427
+ entity_type="GLOBAL",
428
+ feature_id="PLAYLIST_CATEGORY_STATS_V2#1D#MOBILE",
429
+ version="v1",
430
+ ),
431
+ FeatureRegistryId(
432
+ entity_type="GLOBAL",
433
+ feature_id="PLAYLIST_CATEGORY_STATS_V2#1D#TV",
434
+ version="v1",
435
+ ),
425
436
  ]
426
437
 
427
438
  user_playlist_stats_v1_features: list[FeatureRegistryId] = [
@@ -0,0 +1 @@
1
+ __version__ = "0.4.7"
@@ -9,7 +9,7 @@ from boto3.dynamodb.types import TypeDeserializer
9
9
 
10
10
  from . import exceptions
11
11
  from ._serializers import FeatureRegistryId, SerializerRegistry
12
- from .utils import _complete_features_for_channels
12
+ from .utils import _complete_features_for_channels, DEFAULT_CHANNELS
13
13
 
14
14
  logger = logging.getLogger(__name__)
15
15
 
@@ -274,7 +274,7 @@ _MOBILE_OS = {"ios", "android", "iphone", "galaxy"}
274
274
 
275
275
 
276
276
  def _get_os_cat(client_os: str) -> str:
277
- normalized = client_os.lower().replace("debug","") if client_os else ""
277
+ normalized = client_os.lower().replace("debug", "") if client_os else ""
278
278
  return "MOBILE" if normalized in _MOBILE_OS else "TV"
279
279
 
280
280
 
@@ -503,16 +503,32 @@ def _process_channels(
503
503
  for stat in playlist_stats_user.values():
504
504
  all_channels.update(stat.data.keys())
505
505
 
506
- # Get not preferred channels to be ignored
506
+ all_channels = all_channels | set(DEFAULT_CHANNELS)
507
+
508
+ # My headlines will stay on top so not part of the candidates
507
509
  # The group labels are also ignored, not real channels for UI
508
- ignore_channels = set(["national_favorite", "local_favorite"])
510
+ ignore_channels = set(["my headlines", "national_favorite", "local_favorite"])
511
+
512
+ # Rule for weather
513
+ if user.get("geo", {}).get("country") != "US":
514
+ ignore_channels.add("weather")
515
+ # Rule for live_es
516
+ if "es" not in user.get("languages", []):
517
+ ignore_channels.add("live_es")
518
+
509
519
  inserted_channel_names = set()
510
520
  if channel_candidates is not None:
511
521
  preferred = set(user.get("preferredChannels", []) or [])
512
522
  for ch in channel_candidates.data:
513
- if ch.category_group in ("national_favorite", "local_favorite"):
514
- if ch.name not in preferred:
515
- ignore_channels.add(ch.name)
523
+ # Rule for authors
524
+ if (
525
+ ch.category_group in ("national_favorite", "local_favorite")
526
+ and ch.name not in preferred
527
+ ):
528
+ ignore_channels.add(ch.name)
529
+ # Rule for shows
530
+ if ch.category_group == "show" and ch.name.split("/")[0] not in preferred:
531
+ ignore_channels.add(ch.name)
516
532
 
517
533
  for ch in channel_candidates.data:
518
534
  if ch.name not in ignore_channels:
@@ -525,21 +541,7 @@ def _process_channels(
525
541
  )
526
542
  inserted_channel_names.add(ch.name)
527
543
 
528
- DEFAULT_CHANNELS = [
529
- "local news",
530
- "science & technology",
531
- "business & finance",
532
- "entertainment news",
533
- "live",
534
- "live_es",
535
- "weather",
536
- "politics",
537
- "international",
538
- "top videos",
539
- "editor picks",
540
- ]
541
-
542
- for name in all_channels | set(DEFAULT_CHANNELS):
544
+ for name in all_channels:
543
545
  if name not in ignore_channels and name not in inserted_channel_names:
544
546
  channels.append(
545
547
  {
@@ -2,7 +2,7 @@
2
2
  # Generated by the protocol buffer compiler. DO NOT EDIT!
3
3
  # NO CHECKED-IN PROTOBUF GENCODE
4
4
  # source: features.proto
5
- # Protobuf Python Version: 6.33.2
5
+ # Protobuf Python Version: 7.34.1
6
6
  """Generated protocol buffer code."""
7
7
  from google.protobuf import descriptor as _descriptor
8
8
  from google.protobuf import descriptor_pool as _descriptor_pool
@@ -11,9 +11,9 @@ from google.protobuf import symbol_database as _symbol_database
11
11
  from google.protobuf.internal import builder as _builder
12
12
  _runtime_version.ValidateProtobufRuntimeVersion(
13
13
  _runtime_version.Domain.PUBLIC,
14
- 6,
15
- 33,
16
- 2,
14
+ 7,
15
+ 34,
16
+ 1,
17
17
  '',
18
18
  'features.proto'
19
19
  )
@@ -7,7 +7,7 @@ from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union
7
7
  DESCRIPTOR: _descriptor.FileDescriptor
8
8
 
9
9
  class EntryContextCounts(_message.Message):
10
- __slots__ = ()
10
+ __slots__ = ("attempts", "watched")
11
11
  ATTEMPTS_FIELD_NUMBER: _ClassVar[int]
12
12
  WATCHED_FIELD_NUMBER: _ClassVar[int]
13
13
  attempts: int
@@ -15,7 +15,7 @@ class EntryContextCounts(_message.Message):
15
15
  def __init__(self, attempts: _Optional[int] = ..., watched: _Optional[int] = ...) -> None: ...
16
16
 
17
17
  class SelectCounts(_message.Message):
18
- __slots__ = ()
18
+ __slots__ = ("total_selects", "total_selects_and_watched", "total_browsed")
19
19
  TOTAL_SELECTS_FIELD_NUMBER: _ClassVar[int]
20
20
  TOTAL_SELECTS_AND_WATCHED_FIELD_NUMBER: _ClassVar[int]
21
21
  TOTAL_BROWSED_FIELD_NUMBER: _ClassVar[int]
@@ -25,7 +25,7 @@ class SelectCounts(_message.Message):
25
25
  def __init__(self, total_selects: _Optional[int] = ..., total_selects_and_watched: _Optional[int] = ..., total_browsed: _Optional[int] = ...) -> None: ...
26
26
 
27
27
  class EntryContextPWatched(_message.Message):
28
- __slots__ = ()
28
+ __slots__ = ("autoplay", "sel_thumb", "choose_next", "ch_swtch", "launch_first_in_session")
29
29
  AUTOPLAY_FIELD_NUMBER: _ClassVar[int]
30
30
  SEL_THUMB_FIELD_NUMBER: _ClassVar[int]
31
31
  CHOOSE_NEXT_FIELD_NUMBER: _ClassVar[int]
@@ -39,7 +39,7 @@ class EntryContextPWatched(_message.Message):
39
39
  def __init__(self, autoplay: _Optional[_Union[EntryContextCounts, _Mapping]] = ..., sel_thumb: _Optional[_Union[EntryContextCounts, _Mapping]] = ..., choose_next: _Optional[_Union[EntryContextCounts, _Mapping]] = ..., ch_swtch: _Optional[_Union[EntryContextCounts, _Mapping]] = ..., launch_first_in_session: _Optional[_Union[EntryContextCounts, _Mapping]] = ...) -> None: ...
40
40
 
41
41
  class PositionPSelect(_message.Message):
42
- __slots__ = ()
42
+ __slots__ = ("first_pos", "second_pos", "third_pos", "rest_pos")
43
43
  FIRST_POS_FIELD_NUMBER: _ClassVar[int]
44
44
  SECOND_POS_FIELD_NUMBER: _ClassVar[int]
45
45
  THIRD_POS_FIELD_NUMBER: _ClassVar[int]
@@ -51,7 +51,7 @@ class PositionPSelect(_message.Message):
51
51
  def __init__(self, first_pos: _Optional[_Union[SelectCounts, _Mapping]] = ..., second_pos: _Optional[_Union[SelectCounts, _Mapping]] = ..., third_pos: _Optional[_Union[SelectCounts, _Mapping]] = ..., rest_pos: _Optional[_Union[SelectCounts, _Mapping]] = ...) -> None: ...
52
52
 
53
53
  class BrowsedDebiasedPositionPSelects(_message.Message):
54
- __slots__ = ()
54
+ __slots__ = ("up_to_4_browsed", "all_browsed")
55
55
  UP_TO_4_BROWSED_FIELD_NUMBER: _ClassVar[int]
56
56
  ALL_BROWSED_FIELD_NUMBER: _ClassVar[int]
57
57
  up_to_4_browsed: PositionPSelect
@@ -59,7 +59,7 @@ class BrowsedDebiasedPositionPSelects(_message.Message):
59
59
  def __init__(self, up_to_4_browsed: _Optional[_Union[PositionPSelect, _Mapping]] = ..., all_browsed: _Optional[_Union[PositionPSelect, _Mapping]] = ...) -> None: ...
60
60
 
61
61
  class PlaylistStatsForGlobal(_message.Message):
62
- __slots__ = ()
62
+ __slots__ = ("watched_count", "not_watched_count", "capped_watched_secs", "capped_not_watched_secs", "watched_secs", "not_watched_secs")
63
63
  WATCHED_COUNT_FIELD_NUMBER: _ClassVar[int]
64
64
  NOT_WATCHED_COUNT_FIELD_NUMBER: _ClassVar[int]
65
65
  CAPPED_WATCHED_SECS_FIELD_NUMBER: _ClassVar[int]
@@ -75,7 +75,7 @@ class PlaylistStatsForGlobal(_message.Message):
75
75
  def __init__(self, watched_count: _Optional[int] = ..., not_watched_count: _Optional[int] = ..., capped_watched_secs: _Optional[float] = ..., capped_not_watched_secs: _Optional[float] = ..., watched_secs: _Optional[float] = ..., not_watched_secs: _Optional[float] = ...) -> None: ...
76
76
 
77
77
  class PlaylistStatsForUser(_message.Message):
78
- __slots__ = ()
78
+ __slots__ = ("total_days", "start_days", "active_days", "total_watched", "capped_total_watched")
79
79
  TOTAL_DAYS_FIELD_NUMBER: _ClassVar[int]
80
80
  START_DAYS_FIELD_NUMBER: _ClassVar[int]
81
81
  ACTIVE_DAYS_FIELD_NUMBER: _ClassVar[int]
@@ -89,7 +89,7 @@ class PlaylistStatsForUser(_message.Message):
89
89
  def __init__(self, total_days: _Optional[int] = ..., start_days: _Optional[int] = ..., active_days: _Optional[int] = ..., total_watched: _Optional[float] = ..., capped_total_watched: _Optional[float] = ...) -> None: ...
90
90
 
91
91
  class Channel(_message.Message):
92
- __slots__ = ()
92
+ __slots__ = ("name", "category_group", "start_date")
93
93
  NAME_FIELD_NUMBER: _ClassVar[int]
94
94
  CATEGORY_GROUP_FIELD_NUMBER: _ClassVar[int]
95
95
  START_DATE_FIELD_NUMBER: _ClassVar[int]
@@ -99,7 +99,7 @@ class Channel(_message.Message):
99
99
  def __init__(self, name: _Optional[str] = ..., category_group: _Optional[str] = ..., start_date: _Optional[int] = ...) -> None: ...
100
100
 
101
101
  class StreamPSelect(_message.Message):
102
- __slots__ = ()
102
+ __slots__ = ("version", "data")
103
103
  VERSION_FIELD_NUMBER: _ClassVar[int]
104
104
  DATA_FIELD_NUMBER: _ClassVar[int]
105
105
  version: int
@@ -107,7 +107,7 @@ class StreamPSelect(_message.Message):
107
107
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Union[BrowsedDebiasedPositionPSelects, _Mapping]] = ...) -> None: ...
108
108
 
109
109
  class StreamPWatched(_message.Message):
110
- __slots__ = ()
110
+ __slots__ = ("version", "data")
111
111
  VERSION_FIELD_NUMBER: _ClassVar[int]
112
112
  DATA_FIELD_NUMBER: _ClassVar[int]
113
113
  version: int
@@ -115,7 +115,7 @@ class StreamPWatched(_message.Message):
115
115
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Union[EntryContextPWatched, _Mapping]] = ...) -> None: ...
116
116
 
117
117
  class UserPWatched(_message.Message):
118
- __slots__ = ()
118
+ __slots__ = ("version", "data")
119
119
  VERSION_FIELD_NUMBER: _ClassVar[int]
120
120
  DATA_FIELD_NUMBER: _ClassVar[int]
121
121
  version: int
@@ -123,9 +123,9 @@ class UserPWatched(_message.Message):
123
123
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Union[EntryContextPWatched, _Mapping]] = ...) -> None: ...
124
124
 
125
125
  class UserPersonalizingPWatched(_message.Message):
126
- __slots__ = ()
126
+ __slots__ = ("version", "data")
127
127
  class DataEntry(_message.Message):
128
- __slots__ = ()
128
+ __slots__ = ("key", "value")
129
129
  KEY_FIELD_NUMBER: _ClassVar[int]
130
130
  VALUE_FIELD_NUMBER: _ClassVar[int]
131
131
  key: str
@@ -138,7 +138,7 @@ class UserPersonalizingPWatched(_message.Message):
138
138
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Mapping[str, EntryContextPWatched]] = ...) -> None: ...
139
139
 
140
140
  class UserPSelect(_message.Message):
141
- __slots__ = ()
141
+ __slots__ = ("version", "data")
142
142
  VERSION_FIELD_NUMBER: _ClassVar[int]
143
143
  DATA_FIELD_NUMBER: _ClassVar[int]
144
144
  version: int
@@ -146,9 +146,9 @@ class UserPSelect(_message.Message):
146
146
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Union[BrowsedDebiasedPositionPSelects, _Mapping]] = ...) -> None: ...
147
147
 
148
148
  class UserPersonalizingPSelect(_message.Message):
149
- __slots__ = ()
149
+ __slots__ = ("version", "data")
150
150
  class DataEntry(_message.Message):
151
- __slots__ = ()
151
+ __slots__ = ("key", "value")
152
152
  KEY_FIELD_NUMBER: _ClassVar[int]
153
153
  VALUE_FIELD_NUMBER: _ClassVar[int]
154
154
  key: str
@@ -161,9 +161,9 @@ class UserPersonalizingPSelect(_message.Message):
161
161
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Mapping[str, BrowsedDebiasedPositionPSelects]] = ...) -> None: ...
162
162
 
163
163
  class StreamSimilarityScores(_message.Message):
164
- __slots__ = ()
164
+ __slots__ = ("version", "data")
165
165
  class DataEntry(_message.Message):
166
- __slots__ = ()
166
+ __slots__ = ("key", "value")
167
167
  KEY_FIELD_NUMBER: _ClassVar[int]
168
168
  VALUE_FIELD_NUMBER: _ClassVar[int]
169
169
  key: str
@@ -176,9 +176,9 @@ class StreamSimilarityScores(_message.Message):
176
176
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Mapping[str, float]] = ...) -> None: ...
177
177
 
178
178
  class GlobalPlaylistStats(_message.Message):
179
- __slots__ = ()
179
+ __slots__ = ("version", "data")
180
180
  class DataEntry(_message.Message):
181
- __slots__ = ()
181
+ __slots__ = ("key", "value")
182
182
  KEY_FIELD_NUMBER: _ClassVar[int]
183
183
  VALUE_FIELD_NUMBER: _ClassVar[int]
184
184
  key: str
@@ -191,9 +191,9 @@ class GlobalPlaylistStats(_message.Message):
191
191
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Mapping[str, PlaylistStatsForGlobal]] = ...) -> None: ...
192
192
 
193
193
  class UserPlaylistStats(_message.Message):
194
- __slots__ = ()
194
+ __slots__ = ("version", "data")
195
195
  class DataEntry(_message.Message):
196
- __slots__ = ()
196
+ __slots__ = ("key", "value")
197
197
  KEY_FIELD_NUMBER: _ClassVar[int]
198
198
  VALUE_FIELD_NUMBER: _ClassVar[int]
199
199
  key: str
@@ -206,7 +206,7 @@ class UserPlaylistStats(_message.Message):
206
206
  def __init__(self, version: _Optional[int] = ..., data: _Optional[_Mapping[str, PlaylistStatsForUser]] = ...) -> None: ...
207
207
 
208
208
  class GlobalChannels(_message.Message):
209
- __slots__ = ()
209
+ __slots__ = ("version", "data")
210
210
  VERSION_FIELD_NUMBER: _ClassVar[int]
211
211
  DATA_FIELD_NUMBER: _ClassVar[int]
212
212
  version: int
@@ -679,6 +679,21 @@ def _validate_pwatched_entry_context(entry_contexts: list[str]):
679
679
  raise ValueError(f"Invalid entry contexts found: {invalid_contexts}")
680
680
 
681
681
 
682
+ DEFAULT_CHANNELS = [
683
+ "local news",
684
+ "science & technology",
685
+ "business & finance",
686
+ "entertainment news",
687
+ "live",
688
+ "live_es",
689
+ "weather",
690
+ "politics",
691
+ "international",
692
+ "top videos",
693
+ "editor picks",
694
+ ]
695
+
696
+
682
697
  def _complete_features_for_channels(
683
698
  channels: list[dict],
684
699
  user_features: dict[str, UserPlaylistStats],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haystack-ml-stack
3
- Version: 0.4.5a3
3
+ Version: 0.4.7
4
4
  Summary: Functions related to Haystack ML
5
5
  Author-email: Oscar Vega <oscar@haystack.tv>
6
6
  License: MIT
@@ -1 +0,0 @@
1
- __version__ = "0.4.5a3"