bigdata-smart-batching 1.3.0__tar.gz → 1.4.0__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 (23) hide show
  1. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/.gitignore +2 -0
  2. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/CHANGELOG.md +13 -0
  3. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/PKG-INFO +3 -2
  4. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/README.md +2 -1
  5. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/examples/example.py +48 -0
  6. bigdata_smart_batching-1.4.0/examples/example_non_determinism.py +97 -0
  7. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/pyproject.toml +1 -1
  8. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/search_function.py +17 -3
  9. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/uv.lock +1 -1
  10. bigdata_smart_batching-1.3.0/plan.json +0 -20473
  11. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/LICENSE +0 -0
  12. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/Makefile +0 -0
  13. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/docs/smart-batching-architecture.md +0 -0
  14. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/examples/example_overlapping.py +0 -0
  15. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/__init__.py +0 -0
  16. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/output_converter.py +0 -0
  17. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/smart_batching.py +0 -0
  18. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/smart_batching_config.py +0 -0
  19. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/__init__.py +0 -0
  20. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/test_config.py +0 -0
  21. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/test_output_converter.py +0 -0
  22. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/test_rate_limiter.py +0 -0
  23. {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/test_validation.py +0 -0
@@ -110,3 +110,5 @@ Thumbs.db
110
110
  *.csv
111
111
 
112
112
  benchmark/
113
+ *.parquet
114
+ *.json
@@ -5,6 +5,19 @@ 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
+
9
+ ## [1.4.0] - 2026-05-20
10
+
11
+ ### Added
12
+
13
+ - `execute_search(..., basket_filtered_entities=True)`: when enabled, each chunk's `entity_ids` list is filtered to only include entity IDs present in the basket's `query.filters.entity.any_of` list, removing co-mentioned entities that were not explicitly targeted by the basket. Entity IDs are always deduplicated.
14
+
15
+ ## [1.3.1] - 2026-05-19
16
+
17
+ ### Fixed
18
+
19
+ - Now we always retry requests, before we were only retrying on 429 and 403 errors.
20
+
8
21
  ## [1.3.0] - 2026-05-12
9
22
 
10
23
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bigdata-smart-batching
3
- Version: 1.3.0
3
+ Version: 1.4.0
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>
@@ -319,6 +319,7 @@ Step 2: EXECUTION
319
319
  | `max_workers` | `int` | `40` | Parallel workers |
320
320
  | `overwrite_chunks_per_basket` | `int \| None` | `None` | Override the proportional chunk allocation |
321
321
  | `second_pass` | `bool` | `False` | Re-query each basket for entities not yet retrieved |
322
+ | `basket_filtered_entities` | `bool` | `False` | When `True`, each chunk's `entity_ids` is filtered to only include IDs present in the basket's `query.filters.entity.any_of` list; entity IDs are always deduplicated |
322
323
 
323
324
  ### Helper Functions
324
325
 
@@ -417,7 +418,7 @@ Ties are resolved using `VOLUME_BUCKET_PRIORITY`: `high > medium > low > very_lo
417
418
  ## Changelog
418
419
 
419
420
  See [CHANGELOG.md](CHANGELOG.md) for release notes. The package is currently at
420
- version **1.2.0**.
421
+ version **1.4.0**.
421
422
 
422
423
  ## License
423
424
 
@@ -303,6 +303,7 @@ Step 2: EXECUTION
303
303
  | `max_workers` | `int` | `40` | Parallel workers |
304
304
  | `overwrite_chunks_per_basket` | `int \| None` | `None` | Override the proportional chunk allocation |
305
305
  | `second_pass` | `bool` | `False` | Re-query each basket for entities not yet retrieved |
306
+ | `basket_filtered_entities` | `bool` | `False` | When `True`, each chunk's `entity_ids` is filtered to only include IDs present in the basket's `query.filters.entity.any_of` list; entity IDs are always deduplicated |
306
307
 
307
308
  ### Helper Functions
308
309
 
@@ -401,7 +402,7 @@ Ties are resolved using `VOLUME_BUCKET_PRIORITY`: `high > medium > low > very_lo
401
402
  ## Changelog
402
403
 
403
404
  See [CHANGELOG.md](CHANGELOG.md) for release notes. The package is currently at
404
- version **1.2.0**.
405
+ version **1.4.0**.
405
406
 
406
407
  ## License
407
408
 
@@ -45,6 +45,54 @@ def main() -> None:
45
45
  )
46
46
  sys.exit(1)
47
47
 
48
+ if False:
49
+ #Compare the results_raw parquet files to see if they are the same but just counting the number of ids
50
+ import pandas as pd
51
+ files = [x for x in os.listdir(".") if x.startswith("results_raw_") and x.endswith("_100pct_basket_6_without_sentiment.parquet")]
52
+ results_dfs = []
53
+ for file in files:
54
+ df = pd.read_parquet(file)
55
+ df_rows = []
56
+ for row in df.to_dict(orient="records"):
57
+ df_rows.append({
58
+ 'doc_id': row['id'],
59
+ 'chunk_cnum': [i['cnum'] for i in row['chunks']],
60
+ })
61
+ results_temp = pd.DataFrame(df_rows)
62
+ # Expand chunks into rows keeping document id, and chunk number
63
+ results_temp = results_temp.explode('chunk_cnum')
64
+ results_temp = results_temp.drop_duplicates()
65
+ #results_temp.sort_values(by=['doc_id', 'chunk_cnum']).to_csv(f"results_raw_{file.split('_')[2]}_100pct_basket_6.csv", index=False)
66
+ results_dfs.append(results_temp)
67
+
68
+ # Merge using id as the key
69
+ merged = results_dfs[0].merge(results_dfs[1], on=['doc_id', 'chunk_cnum'], how='outer', indicator=True)
70
+ print(f"% overlapping chunks: {(len(merged[merged._merge!='both'])/2)/len(merged)*100:.2f}%")
71
+ print('a')
72
+
73
+
74
+ plan = load_plan("pricing_power_evidence.json")
75
+ # Set the query.ranking.params.content_diversification to False
76
+ for basket in plan['baskets']:
77
+ basket['query']['ranking_params']['content_diversification']['enabled'] = False
78
+ #Remove sentiment filter, do not set it to None, remove the key instead
79
+ basket['query']['filters'].pop('sentiment', None)
80
+
81
+ # Keep only basket basket_6_high_20250924_20251022
82
+ plan['baskets'] = [basket for basket in plan['baskets'] if basket['basket_id'] == "basket_6_high_20250924_20251022"]
83
+ results_raw = execute_search(
84
+ search_plan=plan,
85
+ chunk_percentage=1,
86
+ requests_per_minute=450,
87
+ basket_filtered_entities=True,
88
+ )
89
+
90
+ # Store the results in a file but in parquet format
91
+ import json
92
+ import pandas as pd
93
+ df = pd.DataFrame(results_raw)
94
+ df.to_parquet(f"results_raw_{time.time()}_100pct_basket_6_without_sentiment.parquet")
95
+
48
96
  plans = []
49
97
  results_dfs = []
50
98
  for i in range(1):
@@ -0,0 +1,97 @@
1
+ """Execute a single Bigdata search request for basket_6_high_20250924_20251022.
2
+
3
+ Uses only the requests library (no smart-batching helpers). Run twice and compare
4
+ responses to observe search non-determinism.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import pandas as pd
10
+ import requests
11
+
12
+ SEARCH_ENDPOINT = "/v1/search"
13
+ DEFAULT_API_BASE_URL = "https://api.bigdata.com"
14
+
15
+ QUERY = {
16
+ "auto_enrich_filters": False,
17
+ "filters": {
18
+ "timestamp": {
19
+ "start": "2025-09-24T00:00:00Z",
20
+ "end": "2025-10-22T23:59:59Z"
21
+ },
22
+ "entity": {
23
+ "any_of": [
24
+ "12A3A3",
25
+ "619882",
26
+ "9196A2",
27
+ "58CA9A",
28
+ "50070E",
29
+ "E8846E",
30
+ "E09E2B",
31
+ "990AD0",
32
+ "4A6F00",
33
+ "0157B1"
34
+ ],
35
+ "search_in": "BODY"
36
+ },
37
+ "category": {
38
+ "mode": "INCLUDE",
39
+ "values": [
40
+ "news_premium",
41
+ "transcripts"
42
+ ]
43
+ },
44
+ "source": {
45
+ "mode": "EXCLUDE",
46
+ "values": [
47
+ "DFF004"
48
+ ]
49
+ },
50
+ "sentiment": {"ranges": [{"min": 0.1,"max": 1.0}]}
51
+ },
52
+ "ranking_params": {
53
+ "source_boost": 1,
54
+ "freshness_boost": 0,
55
+ "reranker": {
56
+ "enabled": False
57
+ },
58
+ "content_diversification": {"enabled": True}
59
+ },
60
+ "max_chunks": 1000,
61
+ "text": "The company demonstrates pricing power, raises prices without losing volume, or maintains price discipline against competitors."
62
+ }
63
+
64
+ # Query from pricing_power_evidence.json plan, basket basket_6_high_20250924_20251022
65
+
66
+
67
+ def main() -> None:
68
+
69
+ api_key = "bd_v1_UPdNGgq44wBO9XR8THfiGvHd7Km9TCIvDmnT5AiatA9T56"
70
+ url = "https://api.bigdata.com/v1/search"
71
+ headers = {"X-API-KEY": api_key, "Content-Type": "application/json"}
72
+ payload = {"query": QUERY}
73
+
74
+ # Execute the query 10 times and store the results in a df, counting the number of times a doc_id and chunk_cnum appears
75
+ dfs = []
76
+ for i in range(2):
77
+ df_rows = []
78
+ response = requests.post(url, headers=headers, json=payload, timeout=120)
79
+ results = response.json().get("results", [])
80
+ for result in results:
81
+ df_rows.append({
82
+ 'doc_id': result['id'],
83
+ 'chunk_cnum': [i['cnum'] for i in result['chunks']],
84
+ })
85
+ df = pd.DataFrame(df_rows)
86
+ # Expand chunks into rows keeping document id, and chunk number
87
+ df = df.explode('chunk_cnum')
88
+ df = df.drop_duplicates().reset_index(drop=True)
89
+ dfs.append(df)
90
+
91
+ all_df = pd.concat(dfs)
92
+ all_df = all_df.groupby(['doc_id', 'chunk_cnum']).size().reset_index(name='count')
93
+ print(all_df)
94
+
95
+
96
+ if __name__ == "__main__":
97
+ main()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bigdata-smart-batching"
3
- version = "1.3.0"
3
+ version = "1.4.0"
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" }]
@@ -1155,8 +1155,10 @@ def make_search_request(
1155
1155
  retry_wait = exponential_backoff(retry_count)
1156
1156
  retry_count += 1
1157
1157
  else:
1158
- logger.error(f"API error {response.status_code}: {response.text[:200]}")
1159
- return None
1158
+ retry_wait = exponential_backoff(retry_count)
1159
+ retry_count += 1
1160
+ #logger.error(f"API error {response.status_code}: {response.text[:200]}")
1161
+ #return None
1160
1162
 
1161
1163
  except requests.exceptions.Timeout:
1162
1164
  retry_wait = exponential_backoff(retry_count)
@@ -1245,6 +1247,7 @@ def execute_search(
1245
1247
  second_pass: bool = False,
1246
1248
  rate_limiter: SlidingWindowRateLimiter | None = None,
1247
1249
  concurrency_limiter: ConcurrencySemaphore | None = None,
1250
+ basket_filtered_entities: bool = False,
1248
1251
  ) -> list[dict]:
1249
1252
  """
1250
1253
  Execute search with proportional sampling.
@@ -1268,6 +1271,9 @@ def execute_search(
1268
1271
  concurrency_limiter: Optional pre-built :class:`ConcurrencySemaphore` to
1269
1272
  share with other components. When provided, ``max_workers`` controls
1270
1273
  only the executor's worker count for basket fan-out.
1274
+ basket_filtered_entities: If True, each chunk's ``entity_ids`` will be
1275
+ filtered to only include entity IDs present in the basket's
1276
+ ``query.filters.entity.any_of`` list.
1271
1277
 
1272
1278
  Returns:
1273
1279
  List of document dictionaries (each containing a chunks array).
@@ -1420,7 +1426,15 @@ def execute_search(
1420
1426
  entity_ids = [reporting_entities] if reporting_entities else []
1421
1427
 
1422
1428
  # Store entity information (always set, even if empty)
1423
- chunk["entity_ids"] = entity_ids
1429
+ if basket_filtered_entities:
1430
+ basket_entity_ids = set(
1431
+ basket_info.get("query", {})
1432
+ .get("filters", {})
1433
+ .get("entity", {})
1434
+ .get("any_of", [])
1435
+ )
1436
+ entity_ids = [eid for eid in entity_ids if eid in basket_entity_ids]
1437
+ chunk["entity_ids"] = list(set(entity_ids))
1424
1438
  chunk["primary_entity_id"] = entity_ids[0] if entity_ids else None
1425
1439
 
1426
1440
  logger.info(
@@ -12,7 +12,7 @@ resolution-markers = [
12
12
 
13
13
  [[package]]
14
14
  name = "bigdata-smart-batching"
15
- version = "1.3.0"
15
+ version = "1.3.1"
16
16
  source = { editable = "." }
17
17
  dependencies = [
18
18
  { name = "matplotlib" },