nosible 0.1.7__py3-none-any.whl → 0.1.9__py3-none-any.whl

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.
@@ -14,7 +14,7 @@ except ImportError:
14
14
  # --------------------------------------------------------------------------------------------------------------
15
15
 
16
16
 
17
- def json_dumps(obj: object) -> Union[bytes, str]:
17
+ def json_dumps(obj: object) -> str:
18
18
  """
19
19
  Returns a JSON byte-string if using orjson, else a unicode str.
20
20
 
@@ -36,7 +36,37 @@ def json_dumps(obj: object) -> Union[bytes, str]:
36
36
 
37
37
  Examples
38
38
  --------
39
+ # Standard dict serialization (no orjson)
40
+ >>> _use_orjson = False
41
+ >>> json_dumps({"a": 1})
42
+ '{"a":1}'
43
+
44
+ # List serialization
45
+ >>> _use_orjson = False
46
+ >>> json_dumps([1, 2, 3])
47
+ '[1,2,3]'
48
+
49
+ # orjson path returns unicode str
50
+ >>> import orjson
51
+ >>> _use_orjson = True
52
+ >>> orjson.dumps = lambda o: b'{"b":2}'
53
+ >>> json_dumps({"b": 2})
54
+ '{"b":2}'
39
55
 
56
+ # Non-str dict keys are coerced to str when using orjson
57
+ >>> _use_orjson = True
58
+ >>> orjson.dumps = lambda o: b'{"1":"one"}'
59
+ >>> json_dumps({1: "one"})
60
+ '{"1":"one"}'
61
+
62
+ # Error path: un-serializable object
63
+ >>> _use_orjson = False
64
+ >>> class Bad: pass
65
+ >>> try:
66
+ ... json_dumps(Bad())
67
+ ... except RuntimeError as e:
68
+ ... "Failed to serialize object to JSON" in str(e)
69
+ '{"1":"one"}'
40
70
  """
41
71
  try:
42
72
  if _use_orjson:
@@ -81,7 +111,7 @@ def json_loads(s: Union[bytes, str]) -> dict:
81
111
  # Standard library path (disable orjson)
82
112
  >>> _use_orjson = False
83
113
  >>> json_dumps({'a': 1})
84
- '{"a":1}'
114
+ '{"1":"one"}'
85
115
 
86
116
  # orjson path with monkey-patched dumping
87
117
  >>> _use_orjson = True
@@ -115,3 +145,22 @@ def json_loads(s: Union[bytes, str]) -> dict:
115
145
  except Exception as e:
116
146
  # Optionally, you can log the error here
117
147
  raise RuntimeError(f"Failed to deserialize JSON: {e}") from e
148
+
149
+
150
+ def print_dict(dict: dict) -> str:
151
+ """
152
+ Print a dictionary in a readable format.
153
+
154
+ Parameters
155
+ ----------
156
+ d : dict
157
+ The dictionary to print.
158
+
159
+ Returns
160
+ -------
161
+ None
162
+ This function does not return anything; it prints the dictionary to stdout.
163
+ """
164
+ if _use_orjson:
165
+ return orjson.dumps(dict, option=orjson.OPT_INDENT_2).decode("utf-8")
166
+ return json.dumps(dict, indent=2)
@@ -0,0 +1,131 @@
1
+ import random
2
+
3
+ COMPANIES = [
4
+ "Apple Inc.",
5
+ "Microsoft Corporation",
6
+ "Amazon.com, Inc.",
7
+ "Alphabet Inc.",
8
+ "Meta Platforms, Inc.",
9
+ "Tesla, Inc.",
10
+ "Berkshire Hathaway Inc.",
11
+ "NVIDIA Corporation",
12
+ "JPMorgan Chase & Co.",
13
+ "Johnson & Johnson",
14
+ "Walmart Inc.",
15
+ "Visa Inc.",
16
+ "Mastercard Incorporated",
17
+ "Procter & Gamble Co.",
18
+ "UnitedHealth Group Incorporated",
19
+ "Bank of America Corporation",
20
+ "Home Depot, Inc.",
21
+ "Nestlé S.A.",
22
+ "Samsung Electronics Co., Ltd.",
23
+ "LVMH Moët Hennessy – Louis Vuitton",
24
+ "ASML Holding N.V.",
25
+ "Exxon Mobil Corporation",
26
+ "Intel Corporation",
27
+ "Pfizer Inc.",
28
+ "The Coca-Cola Company",
29
+ "PepsiCo, Inc.",
30
+ "Chevron Corporation",
31
+ "Merck & Co., Inc.",
32
+ "Novartis International AG",
33
+ "Toyota Motor Corporation",
34
+ "Oracle Corporation",
35
+ "Cisco Systems, Inc.",
36
+ "Adobe Inc.",
37
+ "Salesforce, Inc.",
38
+ "Netflix, Inc.",
39
+ "International Business Machines Corporation (IBM)",
40
+ "The Walt Disney Company",
41
+ "HSBC Holdings plc",
42
+ "McDonald's Corporation",
43
+ "Nike, Inc.",
44
+ "Qualcomm Incorporated",
45
+ "Roche Holding AG",
46
+ "SAP SE",
47
+ "Abbott Laboratories",
48
+ "Costco Wholesale Corporation",
49
+ "Broadcom Inc.",
50
+ "Accenture plc",
51
+ "Chevron Corporation",
52
+ "Texas Instruments Incorporated",
53
+ "Unilever PLC"
54
+ ]
55
+
56
+ THINGS_TO_KNOW = [
57
+ "Company name and branding",
58
+ "Founding date and history",
59
+ "Founders and key executives",
60
+ "Headquarters and global offices",
61
+ "Mission, vision, and values",
62
+ "Core products and services",
63
+ "Business model and revenue streams",
64
+ "Annual revenue and growth rate",
65
+ "Profit margins (gross, operating, net)",
66
+ "Market capitalization and valuation",
67
+ "Key financial ratios (P/E, ROE, ROI)",
68
+ "Stock price history and recent performance",
69
+ "Major investors and shareholder structure",
70
+ "Recent mergers, acquisitions, or divestitures",
71
+ "R&D spending and innovation pipeline",
72
+ "Competitive landscape and main rivals",
73
+ "Market share by region or segment",
74
+ "Customer segments and target markets",
75
+ "Pricing strategy and positioning",
76
+ "Supply chain structure and partners",
77
+ "Distribution channels and logistics",
78
+ "Marketing and advertising strategies",
79
+ "Brand perception and reputation",
80
+ "ESG (Environmental, Social, Governance) scores",
81
+ "Sustainability initiatives and impact",
82
+ "Corporate culture and employee count",
83
+ "Employee satisfaction and turnover rates",
84
+ "Leadership and governance practices",
85
+ "Regulatory environment and compliance record",
86
+ "Key risks and litigation history",
87
+ "Recent news, press releases, and media coverage",
88
+ "Patents, trademarks, and IP portfolio",
89
+ "Digital transformation and tech stack",
90
+ "Website traffic and social media metrics",
91
+ "Mobile app usage and customer reviews",
92
+ "Partnerships and strategic alliances",
93
+ "Future outlook and analyst recommendations",
94
+ ]
95
+
96
+ JOB_TITLES = [
97
+ "Chief Executive Officer (CEO)",
98
+ "Chief Financial Officer (CFO)",
99
+ "Chief Operating Officer (COO)",
100
+ "Chief Technology Officer (CTO)",
101
+ "Chief Marketing Officer (CMO)",
102
+ "Head of Investor Relations",
103
+ "Director of Corporate Strategy",
104
+ "Business Development Manager",
105
+ "Product Manager",
106
+ "Marketing Manager",
107
+ "Brand Manager",
108
+ "Financial Analyst",
109
+ "Equity Research Analyst",
110
+ "Market Research Analyst",
111
+ "Consultant",
112
+ "Venture Capital Associate",
113
+ "Private Equity Associate",
114
+ "Operations Manager",
115
+ "Supply Chain Manager",
116
+ "Human Resources Manager",
117
+ "Sustainability Officer",
118
+ "Compliance Officer",
119
+ "Legal Counsel",
120
+ "Risk Manager",
121
+ "Data Analyst",
122
+ "IT Manager",
123
+ "Sales Director",
124
+ "Account Manager",
125
+ "Customer Success Manager",
126
+ "Public Relations Manager"
127
+ ]
128
+
129
+ def _get_question():
130
+ return (f"I am a {random.choice(JOB_TITLES)} and I want to know {random.choice(THINGS_TO_KNOW)}"
131
+ f"about {random.choice(COMPANIES)}")
@@ -10,40 +10,46 @@ log = logging.getLogger(__name__)
10
10
 
11
11
  PLAN_RATE_LIMITS = {
12
12
  "test": {
13
- # Per minute limit, then per day.
14
- "visit": [(1, 60), (10, 24 * 3600)],
15
- "slow": [(1, 60), (10, 24 * 3600)],
16
- "fast": [(10, 60), (100, 24 * 3600)],
13
+ # Per minute limit, then per month.
14
+ "visit": [(60, 60), (300, 24 * 3600 * 30)],
15
+ "slow": [(60, 60), (300, 24 * 3600 * 30)],
16
+ "fast": [(60, 60), (3000, 24 * 3600 * 30)],
17
17
  },
18
18
  "basic": {
19
- "visit": [(1, 60), (100, 24 * 3600)],
20
- "slow": [(1, 60), (100, 24 * 3600)],
21
- "fast": [(10, 60), (1000, 24 * 3600)],
19
+ "visit": [(60, 60), (1400, 24 * 3600 * 30)],
20
+ "slow": [(60, 60), (1400, 24 * 3600 * 30)],
21
+ "fast": [(60, 60), (14_000, 24 * 3600 * 30)],
22
22
  },
23
23
  "pro": {
24
- "visit": [(1, 60), (250, 24 * 3600)],
25
- "slow": [(1, 60), (250, 24 * 3600)],
26
- "fast": [(10, 60), (5_000, 24 * 3600)],
24
+ "visit": [(60, 60), (6700, 24 * 3600 * 30)],
25
+ "slow": [(60, 60), (6700, 24 * 3600 * 30)],
26
+ "fast": [(60, 60), (67_000, 24 * 3600 * 30)],
27
27
  },
28
28
  "pro+": {
29
- "visit": [(2, 60), (500, 24 * 3600)],
30
- "slow": [(2, 60), (500, 24 * 3600)],
31
- "fast": [(10, 60), (10_000, 24 * 3600)],
29
+ "visit": [(60, 60), (32_000, 24 * 3600 * 30)],
30
+ "slow": [(60, 60), (32_000, 24 * 3600 * 30)],
31
+ "fast": [(60, 60), (320_000, 24 * 3600 * 30)],
32
32
  },
33
33
  "bus": {
34
- "visit": [(2, 60), (1000, 24 * 3600)],
35
- "slow": [(2, 60), (1000, 24 * 3600)],
36
- "fast": [(35, 60), (50_000, 24 * 3600)],
34
+ "visit": [(60, 60), (200_000, 24 * 3600 * 30)],
35
+ "slow": [(60, 60), (200_000, 24 * 3600 * 30)],
36
+ "fast": [(60, 60), (2_000_000, 24 * 3600 * 30)],
37
37
  },
38
38
  "bus+": {
39
- "visit": [(3, 60), (2000, 24 * 3600)],
40
- "slow": [(3, 60), (2000, 24 * 3600)],
41
- "fast": [(100, 60), (100_000, 24 * 3600)],
39
+ "visit": [(60, 60), (500_000, 24 * 3600 * 30)],
40
+ "slow": [(60, 60), (500_000, 24 * 3600 * 30)],
41
+ "fast": [(120, 60), (5_000_000, 24 * 3600 * 30)],
42
42
  },
43
43
  "ent": {
44
- "visit": [(5, 60), (5000, 24 * 3600)],
45
- "slow": [(5, 60), (5000, 24 * 3600)],
46
- "fast": [(400, 60), (500_000, 24 * 3600)],
44
+ "visit": [(60, 60), (1_500_000, 24 * 3600 * 30)],
45
+ "slow": [(60, 60), (1_500_000, 24 * 3600 * 30)],
46
+ "fast": [(360, 60), (15_000_000, 24 * 3600 * 30)],
47
+ },
48
+ # This plan is used for testing in the package
49
+ "chat": {
50
+ "visit": [(60, 60), (1_500_000, 24 * 3600 * 30)],
51
+ "slow": [(60, 60), (1_500_000, 24 * 3600 * 30)],
52
+ "fast": [(360, 60), (15_000_000, 24 * 3600 * 30)],
47
53
  },
48
54
  }
49
55
 
@@ -121,7 +127,7 @@ class RateLimiter:
121
127
  --------
122
128
  >>> rl = RateLimiter(1, 10.0)
123
129
  >>> rl.acquire() # first call always passes
124
- >>> # second call within 10 seconds will block until the window resets
130
+ >>> # Second call within 10 seconds will block until the window resets
125
131
  >>> start = time.monotonic(); rl.acquire(); end = time.monotonic()
126
132
  >>> end - start >= 10.0
127
133
  True
@@ -160,7 +166,7 @@ class RateLimiter:
160
166
  >>> rl = RateLimiter(1, 10.0)
161
167
  >>> rl.try_acquire()
162
168
  True
163
- >>> # immediately calling again will fail
169
+ >>> # Immediately calling again will fail
164
170
  >>> rl.try_acquire()
165
171
  False
166
172
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nosible
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Python client for the NOSIBLE Search API
5
5
  Home-page: https://github.com/NosibleAI/nosible
6
6
  Author: Stuart Reid, Matthew Dicks, Richard Taylor, Gareth Warburton
@@ -77,26 +77,11 @@ pip install nosible
77
77
  * tantivy
78
78
  * openai
79
79
 
80
- ### ⚙️ Configuration
81
-
82
- You can specify a custom base URL for all endpoints (e.g., OpenRouter, Google, or your own proxy):
83
-
84
- ```python
85
- from nosible import Nosible
86
-
87
- client = Nosible(
88
- nosible_api_key="basic|abcd1234...",
89
- llm_api_key="sk-...",
90
- base_url="https://api.openrouter.ai/v1"
91
- )
92
- ```
93
-
94
80
  ### 🔑 Authentication
95
81
 
96
- 1. Sign in to nosible.ai and grab your free API key.
82
+ 1. Sign in to [NOSIBLE.AI](https://www.nosible.ai/) and grab your free API key.
97
83
  2. Set it as an environment variable or pass directly:
98
84
 
99
-
100
85
  On Windows
101
86
 
102
87
  ```powershell
@@ -250,7 +235,7 @@ with Nosible(nosible_api_key="basic|abcd1234...") as client:
250
235
 
251
236
  #### Sentiment Analysis
252
237
 
253
- Compute sentiment for a single result (Uses GPT-4o; requires LLM API key):
238
+ Compute sentiment for a single result (uses GPT-4o; requires an LLM API key):
254
239
 
255
240
  ```python
256
241
  from nosible import Nosible
@@ -297,6 +282,10 @@ with Nosible(nosible_api_key="basic|abcd1234...") as client:
297
282
  rs_ndjson = ResultSet.from_ndjson("all_news.ndjson")
298
283
  ```
299
284
 
285
+ #### More Examples
286
+
287
+ For more examples, checkout `/examples` for in-depth usage of the NOSIBLE Client Package
288
+
300
289
  ### 📡 Swagger Docs
301
290
 
302
291
  You can find online endpoints to the NOSIBLE Search API Swagger Docs
@@ -309,33 +298,8 @@ Inspect your current limits at runtime:
309
298
  ```python
310
299
  client.get_ratelimits()
311
300
  ```
312
- Default limits by plan:
313
-
314
- | Plan | Period | Fast Searches | URL Visits | Slow Searches | Cost | CPM |
315
- |----------------|------------|---------------|------------|---------------|----------|------|
316
- | **Free** | Monthly | 3,000 | 300 | 300 | \$0 | $0 |
317
- | | Daily | 100 | 10 | 10 | | |
318
- | | Per-Minute | 10 | 1 | 1 | | |
319
- | **Basic** | Monthly | 30,000 | 3,000 | 3,000 | \$120 | $4 |
320
- | | Daily | 1,000 | 100 | 100 | | |
321
- | | Per-Minute | 10 | 1 | 1 | | |
322
- | **Pro** | Monthly | 150,000 | 7,500 | 7,500 | \$450 | $3 |
323
- | | Daily | 5,000 | 250 | 250 | | |
324
- | | Per-Minute | 10 | 1 | 1 | | |
325
- | **Pro+** | Monthly | 300,000 | 15,000 | 15,000 | \$750 | $2.5 |
326
- | | Daily | 10,000 | 500 | 500 | | |
327
- | | Per-Minute | 10 | 2 | 1 | | |
328
- | **Business** | Monthly | 1,500,000 | 30,000 | 30,000 | \$3,000 | $2 |
329
- | | Daily | 50,000 | 1,000 | 1,000 | | |
330
- | | Per-Minute | 35 | 2 | 2 | | |
331
- | **Business+** | Monthly | 3,000,000 | 60,000 | 60,000 | \$4,500 | $1.5 |
332
- | | Daily | 100,000 | 2,000 | 2,000 | | |
333
- | | Per-Minute | 100 | 3 | 3 | | |
334
- | **Enterprise** | Monthly | 15,000,000 | 150,000 | 150,000 | \$15,000 | $1 |
335
- | | Daily | 500,000 | 5,000 | 5,000 | | |
336
- | | Per-Minute | 400 | 5 | 5 | | |
337
-
338
- *All endpoints are automatically throttled
301
+
302
+ Or you can view them on the [docs](https://nosible-py.readthedocs.io/en/latest/rate_limits.html).
339
303
 
340
304
  ---
341
305
 
@@ -0,0 +1,17 @@
1
+ nosible/__init__.py,sha256=11QmG9Wjprp_zB0VnPxGjqKwHmaoB0hoT8AGO6cGVMM,1426
2
+ nosible/nosible_client.py,sha256=LbbzQHSvINnHRetIlAekXbZAWe_yF4JaNsjqDbYVsuw,70281
3
+ nosible/classes/result.py,sha256=mSMMKSo3EoZCSPYM5PGezsBQQYgW0jFnDtpOwZOAT3s,17251
4
+ nosible/classes/result_set.py,sha256=T-AYXeKgEy5fqEo4gGBlzqszo9F-XgczwspLF-Nm0Jk,52164
5
+ nosible/classes/search.py,sha256=hmIgK4pFcbCgjLypEWY0s8I6l914_xppNyTV4PWN-vo,10608
6
+ nosible/classes/search_set.py,sha256=4L2qO_IKqlUebvxNJERxjXNiUKtolw7JhgFtt1QqEHk,9790
7
+ nosible/classes/snippet.py,sha256=5Av0cXjOL-8X6H8oFunC36DUyWiFLLsl7FfPJ7cYwVU,4988
8
+ nosible/classes/snippet_set.py,sha256=1SZUEq6zRcBT3N8BKbFPml5pt_WSCybIjMpTw8PTz4E,5093
9
+ nosible/classes/web_page.py,sha256=fe-cfDL-AmQRMdBO37jWh1K6BsH_K_VWBBfBzFRV3KM,7149
10
+ nosible/utils/json_tools.py,sha256=UB8xuk9UoLDd4XD-M0CuuuKToOfMOtPmd7b5gT-SHaY,4550
11
+ nosible/utils/question_builder.py,sha256=S3eCb0Dsg0_1kyUmp2hxYnxH4Yq_tUi-HkB8owUB60E,4063
12
+ nosible/utils/rate_limiter.py,sha256=qr0Tg-3wVcw95FyQv3gbZhbf-_QY9zKdkIE4FSLFSBo,5400
13
+ nosible-0.1.9.dist-info/licenses/LICENSE,sha256=8ifsV4DrsiKi8KVBFy8SBb3KXPXhofE3pYq07q1TSCQ,1117
14
+ nosible-0.1.9.dist-info/METADATA,sha256=ka83CR6XMeSaGVNQNig0VE6aCCWdjrow6dKpf6RLbuE,9486
15
+ nosible-0.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ nosible-0.1.9.dist-info/top_level.txt,sha256=mOconHuKcNJ1jTAj3DapQP_xB8YOmjTMyHg5txKH3uA,8
17
+ nosible-0.1.9.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- nosible/__init__.py,sha256=11QmG9Wjprp_zB0VnPxGjqKwHmaoB0hoT8AGO6cGVMM,1426
2
- nosible/nosible_client.py,sha256=xBl-P0239quaL5H3vz3SJp-rTNIpQWR0ElyrVOtJxZQ,68850
3
- nosible/classes/result.py,sha256=EqhdO4qkSfflE1DSRYnNZpOBJ3F2ZGyJNN5KOOj4MkQ,17929
4
- nosible/classes/result_set.py,sha256=26NLzUlKOhsb4TpIpEROCSYiJtRXrsrrJmXzVEa8DgA,51148
5
- nosible/classes/search.py,sha256=BwP65_NMRL6XmGgiXz6LGRuR50wMABUHI-CmaAaLLrk,10594
6
- nosible/classes/search_set.py,sha256=q4OWMy_ptRjc9sc_FMEYhMjEg5ZAS6Wr3bFAyt0F_Rw,9136
7
- nosible/classes/snippet.py,sha256=SEYuRpV0QGWNHLGPhyTO0kjOv4QrCrlH1-9X_DAuAYs,5114
8
- nosible/classes/snippet_set.py,sha256=lWWayADvWhzzYwJKtVkLOL0guFCnvZp82VQkiC_ESkk,4941
9
- nosible/classes/web_page.py,sha256=DJnO7NYVkrm4V244oUuWVJVHe4SBQycxqC2T3WLFR54,9017
10
- nosible/utils/json_tools.py,sha256=pG1ANpclklc01DWJfoinnDA3M0XX2JhyTVd_5qK3xnA,3308
11
- nosible/utils/rate_limiter.py,sha256=N8-GsueWb6VbfKu4UI94lM6yyLlZY-fxzTJ9NQdhZW4,4986
12
- nosible-0.1.7.dist-info/licenses/LICENSE,sha256=8ifsV4DrsiKi8KVBFy8SBb3KXPXhofE3pYq07q1TSCQ,1117
13
- nosible-0.1.7.dist-info/METADATA,sha256=Jx-85WeeaHeRdKfqw-2VL-JxTxWg6h3cyqm2G-DlmVw,11803
14
- nosible-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- nosible-0.1.7.dist-info/top_level.txt,sha256=mOconHuKcNJ1jTAj3DapQP_xB8YOmjTMyHg5txKH3uA,8
16
- nosible-0.1.7.dist-info/RECORD,,