bigdata-smart-batching 1.1.0__tar.gz → 1.2.1__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 (21) hide show
  1. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/.gitignore +0 -3
  2. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/CHANGELOG.md +22 -0
  3. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/PKG-INFO +45 -2
  4. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/README.md +44 -1
  5. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/examples/example.py +12 -10
  6. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/pyproject.toml +1 -1
  7. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/src/bigdata_smart_batching/__init__.py +6 -0
  8. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/src/bigdata_smart_batching/search_function.py +174 -35
  9. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/src/bigdata_smart_batching/smart_batching.py +324 -222
  10. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/tests/test_rate_limiter.py +37 -1
  11. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/tests/test_validation.py +99 -0
  12. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/uv.lock +1 -1
  13. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/LICENSE +0 -0
  14. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/Makefile +0 -0
  15. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/docs/smart-batching-architecture.md +0 -0
  16. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/examples/example_overlapping.py +0 -0
  17. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/src/bigdata_smart_batching/output_converter.py +0 -0
  18. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/src/bigdata_smart_batching/smart_batching_config.py +0 -0
  19. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/tests/__init__.py +0 -0
  20. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/tests/test_config.py +0 -0
  21. {bigdata_smart_batching-1.1.0 → bigdata_smart_batching-1.2.1}/tests/test_output_converter.py +0 -0
@@ -110,6 +110,3 @@ Thumbs.db
110
110
  *.csv
111
111
 
112
112
  benchmark/
113
- benchmark.py
114
- scatter_date_relevance.py
115
- simple.py
@@ -5,6 +5,28 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.1] - 2026-05-12
9
+
10
+ ### Fixed
11
+
12
+ - Modified README.md file to add latest changes
13
+
14
+ ## [1.2.0] - 2026-05-12
15
+
16
+ ### Added
17
+
18
+ - Document-level **sentiment** filters: inclusive `min` / `max` range(s), validated with `validate_sentiment_range`, modeled as `SentimentRange` / `SentimentRangeInput`, serialized to `query.filters.sentiment.ranges` on co-mention, volume, and search payloads. `plan_search(..., sentiment=...)` and `sentiment_filter` plumbed through `SmartBatchingPlanner` co-mention and volume flows. Types and validator re-exported from the package root.
19
+ - **Planning throughput controls**: `plan_search(..., requests_per_minute=..., max_workers=...)` and `SmartBatchingPlanner(..., requests_per_minute=..., max_workers=..., rate_limiter=..., concurrency_limiter=...)` so all planner HTTP work shares one sliding-window rate limiter and one concurrency semaphore across internal thread pools.
20
+ - **`exponential_backoff`** helper in `search_function` (used by retry paths; capped via `MAX_RETRY_BACKOFF_SECONDS`).
21
+
22
+ ### Changed
23
+
24
+ - **Retries:** co-mention / volume POST retries now use **shared exponential backoff** for `429`, `403`, timeouts, and other failures, instead of parsing `Retry-After` for `429` and separate ad-hoc `403` handling (constant renamed/clamped to `MAX_RETRY_BACKOFF_SECONDS`).
25
+ - **Volume time-series merge:** when aggregating batch results per date, a **`sentiment`** value on each point is the arithmetic mean of batch sentiments for that date (batches without sentiment for that date are omitted from the mean).
26
+ - **`SlidingWindowRateLimiter`:** window accounting aligned with **ceil-based** `max_per_window` expectations (tests updated for low rates and 450 rpm-style cases).
27
+
28
+
29
+
8
30
  ## [1.1.0] - 2026-04-20
9
31
 
10
32
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bigdata-smart-batching
3
- Version: 1.1.0
3
+ Version: 1.2.1
4
4
  Summary: High-performance semantic search with intelligent company grouping and parallel execution
5
5
  Project-URL: homepage, https://bigdata.com/api
6
6
  Author-email: "Bigdata.com" <support@ravenpack.com>
@@ -29,6 +29,8 @@ This module provides a two-step pipeline for efficient semantic search:
29
29
  - **Parallel execution** -- rate-limited concurrent requests with semaphore-controlled concurrency.
30
30
  - **Proportional sampling** -- retrieve a percentage of results while preserving the distribution across baskets.
31
31
  - **Category filtering** -- restrict searches to specific document categories (news, transcripts, filings, ...).
32
+ - **Sentiment filtering** -- restrict searches to one or more inclusive document-level sentiment ranges.
33
+ - **Throughput controls** -- shared sliding-window rate limiter and concurrency semaphore across all planning HTTP work, with shared exponential backoff on `429` / `403` / timeout retries.
32
34
  - **Deterministic plans** -- every request stays within service limits; plans can be saved and replayed.
33
35
  - **Scalable** -- handles universes with 10,000+ companies efficiently.
34
36
 
@@ -162,6 +164,41 @@ Valid category values are exposed via `VALID_CATEGORY_VALUES` and include:
162
164
  `news_public`, `podcasts`, `research`, `research_academic_journals`,
163
165
  `research_investment_research`, `transcripts`.
164
166
 
167
+ ### Sentiment Filtering
168
+
169
+ Restrict searches to documents whose sentiment falls inside one or more
170
+ inclusive `min` / `max` ranges using the `sentiment` argument. It accepts a
171
+ single `SentimentRange` / dict, or a list of them (multiple disjoint ranges).
172
+ The filter is emitted as `query.filters.sentiment.ranges` on co-mention,
173
+ volume, and search payloads.
174
+
175
+ ```python
176
+ from bigdata_smart_batching import SentimentRange, plan_search
177
+
178
+ plan = plan_search(
179
+ universe="id_name_mapping_us_top_3000.csv",
180
+ start_date="2023-01-01",
181
+ end_date="2023-12-31",
182
+ text="earnings revenue profit",
183
+ sentiment=SentimentRange(min=0.3, max=1.0),
184
+ )
185
+
186
+ plan = plan_search(
187
+ universe="id_name_mapping_us_top_3000.csv",
188
+ start_date="2023-01-01",
189
+ end_date="2023-12-31",
190
+ text="earnings revenue profit",
191
+ sentiment=[
192
+ {"min": -1.0, "max": -0.5},
193
+ {"min": 0.5, "max": 1.0},
194
+ ],
195
+ )
196
+ ```
197
+
198
+ Each range must satisfy `min <= max`. Use
199
+ `validate_sentiment_range(sentiment)` to validate and normalize input into the
200
+ list-of-dicts shape the API expects.
201
+
165
202
  ### Save and Load Plans
166
203
 
167
204
  ```python
@@ -226,7 +263,10 @@ Step 2: EXECUTION
226
263
  | `min_period_days` | `int` | `30` | Minimum days per sub-period |
227
264
  | `min_entities_per_basket` | `int` | `1` | Lower bound on entities per basket |
228
265
  | `category` | `CategoryFilter \| dict \| None` | `None` | Optional category filter (`mode` + `values`) |
266
+ | `sentiment` | `SentimentRange \| dict \| list \| None` | `None` | Optional sentiment filter: one inclusive `{min, max}` range or a list of disjoint ranges |
229
267
  | `content_diversification` | `bool` | `True` | Enable/disable content diversification in ranking |
268
+ | `requests_per_minute` | `int` | `100` | Global rate limit shared across all planning HTTP work (co-mention + volume) |
269
+ | `max_workers` | `int` | `40` | Maximum concurrent in-flight planning requests (shared semaphore) |
230
270
 
231
271
  ### `execute_search()`
232
272
 
@@ -249,6 +289,7 @@ Step 2: EXECUTION
249
289
  - `load_universe_from_csv(csv_path)` -- load entity IDs from a CSV only.
250
290
  - `save_plan(plan, path)` / `load_plan(path)` -- persist plans as JSON.
251
291
  - `validate_category_filter(category)` -- validate and normalize a category filter to the API dict shape.
292
+ - `validate_sentiment_range(sentiment)` -- validate and normalize sentiment range(s) into the list-of-`{min, max}` dicts emitted as `filters.sentiment.ranges`.
252
293
  - `build_comention_payload(...)`, `build_volume_payload(...)`, `build_search_query(...)` -- low-level payload builders used by the planner; useful if you need to construct custom requests.
253
294
 
254
295
  ### Public Types
@@ -256,6 +297,8 @@ Step 2: EXECUTION
256
297
  - `CategoryFilter` -- frozen dataclass with `mode: CategoryMode` and `values: tuple[str, ...]`.
257
298
  - `CategoryMode` -- `StrEnum` with `INCLUDE` and `EXCLUDE` values.
258
299
  - `CategoryFilterInput` -- type alias: `CategoryFilter | dict[str, str | list[str]]`.
300
+ - `SentimentRange` -- frozen dataclass with inclusive `min: float` and `max: float`.
301
+ - `SentimentRangeInput` -- type alias for one `SentimentRange` / `{min, max}` dict, or a non-empty list of either.
259
302
  - `UniverseInput` -- type alias: `str | list[str]`.
260
303
  - `VALID_CATEGORY_VALUES` -- `frozenset[str]` of allowed category strings.
261
304
 
@@ -331,7 +374,7 @@ Ties are resolved using `VOLUME_BUCKET_PRIORITY`: `high > medium > low > very_lo
331
374
  ## Changelog
332
375
 
333
376
  See [CHANGELOG.md](CHANGELOG.md) for release notes. The package is currently at
334
- version **1.0.1**.
377
+ version **1.2.0**.
335
378
 
336
379
  ## License
337
380
 
@@ -13,6 +13,8 @@ This module provides a two-step pipeline for efficient semantic search:
13
13
  - **Parallel execution** -- rate-limited concurrent requests with semaphore-controlled concurrency.
14
14
  - **Proportional sampling** -- retrieve a percentage of results while preserving the distribution across baskets.
15
15
  - **Category filtering** -- restrict searches to specific document categories (news, transcripts, filings, ...).
16
+ - **Sentiment filtering** -- restrict searches to one or more inclusive document-level sentiment ranges.
17
+ - **Throughput controls** -- shared sliding-window rate limiter and concurrency semaphore across all planning HTTP work, with shared exponential backoff on `429` / `403` / timeout retries.
16
18
  - **Deterministic plans** -- every request stays within service limits; plans can be saved and replayed.
17
19
  - **Scalable** -- handles universes with 10,000+ companies efficiently.
18
20
 
@@ -146,6 +148,41 @@ Valid category values are exposed via `VALID_CATEGORY_VALUES` and include:
146
148
  `news_public`, `podcasts`, `research`, `research_academic_journals`,
147
149
  `research_investment_research`, `transcripts`.
148
150
 
151
+ ### Sentiment Filtering
152
+
153
+ Restrict searches to documents whose sentiment falls inside one or more
154
+ inclusive `min` / `max` ranges using the `sentiment` argument. It accepts a
155
+ single `SentimentRange` / dict, or a list of them (multiple disjoint ranges).
156
+ The filter is emitted as `query.filters.sentiment.ranges` on co-mention,
157
+ volume, and search payloads.
158
+
159
+ ```python
160
+ from bigdata_smart_batching import SentimentRange, plan_search
161
+
162
+ plan = plan_search(
163
+ universe="id_name_mapping_us_top_3000.csv",
164
+ start_date="2023-01-01",
165
+ end_date="2023-12-31",
166
+ text="earnings revenue profit",
167
+ sentiment=SentimentRange(min=0.3, max=1.0),
168
+ )
169
+
170
+ plan = plan_search(
171
+ universe="id_name_mapping_us_top_3000.csv",
172
+ start_date="2023-01-01",
173
+ end_date="2023-12-31",
174
+ text="earnings revenue profit",
175
+ sentiment=[
176
+ {"min": -1.0, "max": -0.5},
177
+ {"min": 0.5, "max": 1.0},
178
+ ],
179
+ )
180
+ ```
181
+
182
+ Each range must satisfy `min <= max`. Use
183
+ `validate_sentiment_range(sentiment)` to validate and normalize input into the
184
+ list-of-dicts shape the API expects.
185
+
149
186
  ### Save and Load Plans
150
187
 
151
188
  ```python
@@ -210,7 +247,10 @@ Step 2: EXECUTION
210
247
  | `min_period_days` | `int` | `30` | Minimum days per sub-period |
211
248
  | `min_entities_per_basket` | `int` | `1` | Lower bound on entities per basket |
212
249
  | `category` | `CategoryFilter \| dict \| None` | `None` | Optional category filter (`mode` + `values`) |
250
+ | `sentiment` | `SentimentRange \| dict \| list \| None` | `None` | Optional sentiment filter: one inclusive `{min, max}` range or a list of disjoint ranges |
213
251
  | `content_diversification` | `bool` | `True` | Enable/disable content diversification in ranking |
252
+ | `requests_per_minute` | `int` | `100` | Global rate limit shared across all planning HTTP work (co-mention + volume) |
253
+ | `max_workers` | `int` | `40` | Maximum concurrent in-flight planning requests (shared semaphore) |
214
254
 
215
255
  ### `execute_search()`
216
256
 
@@ -233,6 +273,7 @@ Step 2: EXECUTION
233
273
  - `load_universe_from_csv(csv_path)` -- load entity IDs from a CSV only.
234
274
  - `save_plan(plan, path)` / `load_plan(path)` -- persist plans as JSON.
235
275
  - `validate_category_filter(category)` -- validate and normalize a category filter to the API dict shape.
276
+ - `validate_sentiment_range(sentiment)` -- validate and normalize sentiment range(s) into the list-of-`{min, max}` dicts emitted as `filters.sentiment.ranges`.
236
277
  - `build_comention_payload(...)`, `build_volume_payload(...)`, `build_search_query(...)` -- low-level payload builders used by the planner; useful if you need to construct custom requests.
237
278
 
238
279
  ### Public Types
@@ -240,6 +281,8 @@ Step 2: EXECUTION
240
281
  - `CategoryFilter` -- frozen dataclass with `mode: CategoryMode` and `values: tuple[str, ...]`.
241
282
  - `CategoryMode` -- `StrEnum` with `INCLUDE` and `EXCLUDE` values.
242
283
  - `CategoryFilterInput` -- type alias: `CategoryFilter | dict[str, str | list[str]]`.
284
+ - `SentimentRange` -- frozen dataclass with inclusive `min: float` and `max: float`.
285
+ - `SentimentRangeInput` -- type alias for one `SentimentRange` / `{min, max}` dict, or a non-empty list of either.
243
286
  - `UniverseInput` -- type alias: `str | list[str]`.
244
287
  - `VALID_CATEGORY_VALUES` -- `frozenset[str]` of allowed category strings.
245
288
 
@@ -315,7 +358,7 @@ Ties are resolved using `VOLUME_BUCKET_PRIORITY`: `high > medium > low > very_lo
315
358
  ## Changelog
316
359
 
317
360
  See [CHANGELOG.md](CHANGELOG.md) for release notes. The package is currently at
318
- version **1.0.1**.
361
+ version **1.2.0**.
319
362
 
320
363
  ## License
321
364
 
@@ -55,21 +55,23 @@ def main() -> None:
55
55
  plan = load_plan("plan.json")
56
56
  else:
57
57
  plan = plan_search(
58
- #universe=universe_csv,
59
- universe=['54F271'],
58
+ universe=universe_csv,
59
+ #universe=['54F271'],
60
60
  start_date="2025-01-01",
61
- end_date="2025-12-12",
62
- text="Decline in customer confidence in the company",
61
+ end_date="2025-12-31",
62
+ text="Company has significant pricing power",
63
63
  volume_query_mode="iterative",
64
64
  apply_volume_splits=True,
65
- min_period_days=30,
66
- min_entities_per_basket=10,
65
+ min_period_days=120,
66
+ min_entities_per_basket=20,
67
67
  # Optional: filter by category to restrict document sources.
68
68
  # Accepts a CategoryFilter instance or a plain dict:
69
69
  category={"mode": "INCLUDE", "values": ["news_premium"]},
70
70
  content_diversification=False,
71
+ sentiment=[{"min": -1, "max": -0.5},{"min": 0.5, "max": 1}],
71
72
  )
72
73
  plans.append(plan)
74
+ save_plan(plan, "plan.json")
73
75
  print(f"Chunk upper bound estimate: {plan['chunk_upper_bound_estimate']:,}")
74
76
 
75
77
  # Export plan
@@ -78,12 +80,12 @@ def main() -> None:
78
80
  print(f"Plan execution time: {plan_elapsed_s:.3f}s")
79
81
 
80
82
  # Step 2: Execute search with 10% of total chunks (preserves distribution)
81
- if True:
83
+ if False:
82
84
  search_t0 = time.perf_counter()
83
85
  results_raw = execute_search(
84
86
  search_plan=plan,
85
87
  chunk_percentage=1,
86
- requests_per_minute=400,
88
+ requests_per_minute=300,
87
89
  )
88
90
  search_elapsed_s = time.perf_counter() - search_t0
89
91
  print(f"Search execution time: {search_elapsed_s:.3f}s")
@@ -108,10 +110,10 @@ def main() -> None:
108
110
  #print(df.head())
109
111
  #results_dfs.append(results)
110
112
  # Merge dataframe 0 and 1 into a single dataframe using id and chunk index as keys using a full outer join
111
- print(plans[0]==plans[1])
113
+ #print(plans[0]==plans[1])
112
114
  #merged = results_dfs[0].merge(results_dfs[1], on=['doc_id', 'chunk_cnum'], how='outer', indicator=True)
113
115
  #print(f"% overlapping chunks: {(len(merged[merged._merge!='both'])/2)/len(merged)*100:.2f}%")
114
- print('a')
116
+ #print('a')
115
117
 
116
118
 
117
119
  if __name__ == "__main__":
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bigdata-smart-batching"
3
- version = "1.1.0"
3
+ version = "1.2.1"
4
4
  description = "High-performance semantic search with intelligent company grouping and parallel execution"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Bigdata.com", email = "support@ravenpack.com" }]
@@ -13,6 +13,8 @@ from .search_function import (
13
13
  CategoryFilter,
14
14
  CategoryFilterInput,
15
15
  CategoryMode,
16
+ SentimentRange,
17
+ SentimentRangeInput,
16
18
  UniverseInput,
17
19
  build_comention_payload,
18
20
  build_search_query,
@@ -25,12 +27,15 @@ from .search_function import (
25
27
  resolve_universe,
26
28
  save_plan,
27
29
  validate_category_filter,
30
+ validate_sentiment_range,
28
31
  )
29
32
 
30
33
  __all__ = [
31
34
  "CategoryFilter",
32
35
  "CategoryFilterInput",
33
36
  "CategoryMode",
37
+ "SentimentRange",
38
+ "SentimentRangeInput",
34
39
  "UniverseInput",
35
40
  "VALID_CATEGORY_VALUES",
36
41
  "build_comention_payload",
@@ -45,4 +50,5 @@ __all__ = [
45
50
  "resolve_universe",
46
51
  "save_plan",
47
52
  "validate_category_filter",
53
+ "validate_sentiment_range",
48
54
  ]