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.
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/.gitignore +2 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/CHANGELOG.md +13 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/PKG-INFO +3 -2
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/README.md +2 -1
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/examples/example.py +48 -0
- bigdata_smart_batching-1.4.0/examples/example_non_determinism.py +97 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/pyproject.toml +1 -1
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/search_function.py +17 -3
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/uv.lock +1 -1
- bigdata_smart_batching-1.3.0/plan.json +0 -20473
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/LICENSE +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/Makefile +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/docs/smart-batching-architecture.md +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/examples/example_overlapping.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/__init__.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/output_converter.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/smart_batching.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/src/bigdata_smart_batching/smart_batching_config.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/__init__.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/test_config.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/test_output_converter.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/test_rate_limiter.py +0 -0
- {bigdata_smart_batching-1.3.0 → bigdata_smart_batching-1.4.0}/tests/test_validation.py +0 -0
|
@@ -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
|
+
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.
|
|
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.
|
|
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
|
+
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
|
-
|
|
1159
|
-
|
|
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
|
-
|
|
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(
|