rairos 0.2.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.
- rairos-0.2.0/PKG-INFO +165 -0
- rairos-0.2.0/README.md +136 -0
- rairos-0.2.0/pyproject.toml +56 -0
- rairos-0.2.0/rairos/__init__.py +32 -0
- rairos-0.2.0/rairos/client.py +392 -0
- rairos-0.2.0/rairos/exceptions.py +181 -0
- rairos-0.2.0/rairos.egg-info/PKG-INFO +165 -0
- rairos-0.2.0/rairos.egg-info/SOURCES.txt +10 -0
- rairos-0.2.0/rairos.egg-info/dependency_links.txt +1 -0
- rairos-0.2.0/rairos.egg-info/requires.txt +8 -0
- rairos-0.2.0/rairos.egg-info/top_level.txt +1 -0
- rairos-0.2.0/setup.cfg +4 -0
rairos-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rairos
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for Rairos API
|
|
5
|
+
Author-email: Rairos Team <team@rairos.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://rairos.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.rairos.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/shushuzn/Rairos
|
|
10
|
+
Project-URL: Changelog, https://github.com/shushuzn/Rairos/blob/main/sdks/python/CHANGELOG.md
|
|
11
|
+
Keywords: rairos,api,research,ai
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: requests>=2.28.0
|
|
23
|
+
Requires-Dist: urllib3>=2.0.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
27
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
29
|
+
|
|
30
|
+
# Rairos Python SDK
|
|
31
|
+
|
|
32
|
+
[](https://pypi.org/project/rairos/)
|
|
33
|
+
[](https://pypi.org/project/rairos/)
|
|
34
|
+
[](https://github.com/shushuzn/Rairos/blob/main/sdks/python/pyproject.toml)
|
|
35
|
+
|
|
36
|
+
Python SDK for the Rairos API platform.
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# From PyPI (coming soon)
|
|
42
|
+
pip install rairos
|
|
43
|
+
|
|
44
|
+
# From source
|
|
45
|
+
cd sdks/python && pip install -e . && cd ../..
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from rairos import RairosClient
|
|
52
|
+
|
|
53
|
+
# Initialize client
|
|
54
|
+
client = RairosClient(api_key="your_api_key_here")
|
|
55
|
+
|
|
56
|
+
# Search papers
|
|
57
|
+
results = client.search_papers(query="machine learning", page=1, per_page=20)
|
|
58
|
+
print(results)
|
|
59
|
+
|
|
60
|
+
# Detect research gaps (requires Pro tier)
|
|
61
|
+
gaps = client.detect_gap(query="neural network architecture")
|
|
62
|
+
print(gaps)
|
|
63
|
+
|
|
64
|
+
# Get usage statistics
|
|
65
|
+
usage = client.get_usage()
|
|
66
|
+
print(usage)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Authentication
|
|
70
|
+
|
|
71
|
+
You can provide your API key in three ways:
|
|
72
|
+
|
|
73
|
+
1. **Environment variable** (recommended):
|
|
74
|
+
```bash
|
|
75
|
+
export RAIROS_API_KEY=your_api_key_here
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
2. **Constructor parameter**:
|
|
79
|
+
```python
|
|
80
|
+
client = RairosClient(api_key="your_api_key_here")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
3. **Register/Login** to get a new API key:
|
|
84
|
+
```python
|
|
85
|
+
# Register
|
|
86
|
+
auth = client.register(email="user@example.com", password="secure_password")
|
|
87
|
+
|
|
88
|
+
# Login
|
|
89
|
+
auth = client.login(email="user@example.com", password="secure_password")
|
|
90
|
+
print(auth["api_key"]) # Use this key
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Reference
|
|
94
|
+
|
|
95
|
+
### Papers
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# Search papers
|
|
99
|
+
results = client.search_papers(query="quantum computing")
|
|
100
|
+
|
|
101
|
+
# Get specific paper
|
|
102
|
+
paper = client.get_paper(paper_id="uuid-here")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Gap Detection (Pro tier)
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Detect research gaps
|
|
109
|
+
gaps = client.detect_gap(
|
|
110
|
+
query="What are the gaps in transformer architecture research?",
|
|
111
|
+
categories=["cs.AI", "cs.LG"]
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Research (Team tier)
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Run automated research
|
|
119
|
+
results = client.run_research(
|
|
120
|
+
query="Analyze the state of AI safety research in 2025"
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Subscriptions
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# Get available tiers
|
|
128
|
+
tiers = client.get_tiers()
|
|
129
|
+
|
|
130
|
+
# Create checkout session
|
|
131
|
+
checkout = client.create_checkout(
|
|
132
|
+
price_id="price_pro_monthly",
|
|
133
|
+
success_url="https://yourapp.com/success",
|
|
134
|
+
cancel_url="https://yourapp.com/pricing"
|
|
135
|
+
)
|
|
136
|
+
print(checkout["checkout_url"]) # Redirect user to this URL
|
|
137
|
+
|
|
138
|
+
# Get subscription status
|
|
139
|
+
status = client.get_subscription_status()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Error Handling
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from rairos import (
|
|
146
|
+
RairosClient,
|
|
147
|
+
RairosError,
|
|
148
|
+
AuthenticationError,
|
|
149
|
+
RateLimitError
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
client = RairosClient(api_key="invalid_key")
|
|
154
|
+
results = client.search_papers(query="test")
|
|
155
|
+
except AuthenticationError:
|
|
156
|
+
print("Invalid API key")
|
|
157
|
+
except RateLimitError:
|
|
158
|
+
print("Rate limit exceeded - wait and retry")
|
|
159
|
+
except RairosError as e:
|
|
160
|
+
print(f"API error: {e.message}")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT License
|
rairos-0.2.0/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Rairos Python SDK
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/rairos/)
|
|
4
|
+
[](https://pypi.org/project/rairos/)
|
|
5
|
+
[](https://github.com/shushuzn/Rairos/blob/main/sdks/python/pyproject.toml)
|
|
6
|
+
|
|
7
|
+
Python SDK for the Rairos API platform.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# From PyPI (coming soon)
|
|
13
|
+
pip install rairos
|
|
14
|
+
|
|
15
|
+
# From source
|
|
16
|
+
cd sdks/python && pip install -e . && cd ../..
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from rairos import RairosClient
|
|
23
|
+
|
|
24
|
+
# Initialize client
|
|
25
|
+
client = RairosClient(api_key="your_api_key_here")
|
|
26
|
+
|
|
27
|
+
# Search papers
|
|
28
|
+
results = client.search_papers(query="machine learning", page=1, per_page=20)
|
|
29
|
+
print(results)
|
|
30
|
+
|
|
31
|
+
# Detect research gaps (requires Pro tier)
|
|
32
|
+
gaps = client.detect_gap(query="neural network architecture")
|
|
33
|
+
print(gaps)
|
|
34
|
+
|
|
35
|
+
# Get usage statistics
|
|
36
|
+
usage = client.get_usage()
|
|
37
|
+
print(usage)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Authentication
|
|
41
|
+
|
|
42
|
+
You can provide your API key in three ways:
|
|
43
|
+
|
|
44
|
+
1. **Environment variable** (recommended):
|
|
45
|
+
```bash
|
|
46
|
+
export RAIROS_API_KEY=your_api_key_here
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. **Constructor parameter**:
|
|
50
|
+
```python
|
|
51
|
+
client = RairosClient(api_key="your_api_key_here")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. **Register/Login** to get a new API key:
|
|
55
|
+
```python
|
|
56
|
+
# Register
|
|
57
|
+
auth = client.register(email="user@example.com", password="secure_password")
|
|
58
|
+
|
|
59
|
+
# Login
|
|
60
|
+
auth = client.login(email="user@example.com", password="secure_password")
|
|
61
|
+
print(auth["api_key"]) # Use this key
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API Reference
|
|
65
|
+
|
|
66
|
+
### Papers
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# Search papers
|
|
70
|
+
results = client.search_papers(query="quantum computing")
|
|
71
|
+
|
|
72
|
+
# Get specific paper
|
|
73
|
+
paper = client.get_paper(paper_id="uuid-here")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Gap Detection (Pro tier)
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Detect research gaps
|
|
80
|
+
gaps = client.detect_gap(
|
|
81
|
+
query="What are the gaps in transformer architecture research?",
|
|
82
|
+
categories=["cs.AI", "cs.LG"]
|
|
83
|
+
)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Research (Team tier)
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
# Run automated research
|
|
90
|
+
results = client.run_research(
|
|
91
|
+
query="Analyze the state of AI safety research in 2025"
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Subscriptions
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# Get available tiers
|
|
99
|
+
tiers = client.get_tiers()
|
|
100
|
+
|
|
101
|
+
# Create checkout session
|
|
102
|
+
checkout = client.create_checkout(
|
|
103
|
+
price_id="price_pro_monthly",
|
|
104
|
+
success_url="https://yourapp.com/success",
|
|
105
|
+
cancel_url="https://yourapp.com/pricing"
|
|
106
|
+
)
|
|
107
|
+
print(checkout["checkout_url"]) # Redirect user to this URL
|
|
108
|
+
|
|
109
|
+
# Get subscription status
|
|
110
|
+
status = client.get_subscription_status()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Error Handling
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from rairos import (
|
|
117
|
+
RairosClient,
|
|
118
|
+
RairosError,
|
|
119
|
+
AuthenticationError,
|
|
120
|
+
RateLimitError
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
client = RairosClient(api_key="invalid_key")
|
|
125
|
+
results = client.search_papers(query="test")
|
|
126
|
+
except AuthenticationError:
|
|
127
|
+
print("Invalid API key")
|
|
128
|
+
except RateLimitError:
|
|
129
|
+
print("Rate limit exceeded - wait and retry")
|
|
130
|
+
except RairosError as e:
|
|
131
|
+
print(f"API error: {e.message}")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT License
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rairos"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Python SDK for Rairos API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Rairos Team", email = "team@rairos.ai"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["rairos", "api", "research", "ai"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"requests>=2.28.0",
|
|
28
|
+
"urllib3>=2.0.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
dev = [
|
|
33
|
+
"pytest>=7.0.0",
|
|
34
|
+
"pytest-asyncio>=0.21.0",
|
|
35
|
+
"black>=23.0.0",
|
|
36
|
+
"mypy>=1.0.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://rairos.ai"
|
|
41
|
+
Documentation = "https://docs.rairos.ai"
|
|
42
|
+
Repository = "https://github.com/shushuzn/Rairos"
|
|
43
|
+
Changelog = "https://github.com/shushuzn/Rairos/blob/main/sdks/python/CHANGELOG.md"
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.packages.find]
|
|
46
|
+
where = ["."]
|
|
47
|
+
include = ["rairos*"]
|
|
48
|
+
|
|
49
|
+
[tool.black]
|
|
50
|
+
line-length = 88
|
|
51
|
+
target-version = ['py38', 'py39', 'py310', 'py311']
|
|
52
|
+
|
|
53
|
+
[tool.mypy]
|
|
54
|
+
python_version = "3.8"
|
|
55
|
+
warn_return_any = true
|
|
56
|
+
warn_unused_configs = true
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Rairos API Python SDK
|
|
2
|
+
|
|
3
|
+
A Python SDK for the Rairos API platform.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__version__ = "0.2.0"
|
|
7
|
+
|
|
8
|
+
from .client import RairosClient
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
RairosError,
|
|
11
|
+
AuthenticationError,
|
|
12
|
+
RateLimitError,
|
|
13
|
+
ValidationError,
|
|
14
|
+
NotFoundError,
|
|
15
|
+
ForbiddenError,
|
|
16
|
+
ServerError,
|
|
17
|
+
PaymentError,
|
|
18
|
+
raise_from_response,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"RairosClient",
|
|
23
|
+
"RairosError",
|
|
24
|
+
"AuthenticationError",
|
|
25
|
+
"RateLimitError",
|
|
26
|
+
"ValidationError",
|
|
27
|
+
"NotFoundError",
|
|
28
|
+
"ForbiddenError",
|
|
29
|
+
"ServerError",
|
|
30
|
+
"PaymentError",
|
|
31
|
+
"raise_from_response",
|
|
32
|
+
]
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""Rairos API Client
|
|
2
|
+
|
|
3
|
+
Main client for interacting with the Rairos API.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
from typing import Optional, Dict, Any, List
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
RairosError,
|
|
11
|
+
AuthenticationError,
|
|
12
|
+
RateLimitError,
|
|
13
|
+
ValidationError,
|
|
14
|
+
NotFoundError,
|
|
15
|
+
ForbiddenError,
|
|
16
|
+
ServerError,
|
|
17
|
+
PaymentError,
|
|
18
|
+
raise_from_response,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RairosClient:
|
|
23
|
+
"""Python SDK for Rairos API.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
>>> client = RairosClient(api_key="your-api-key")
|
|
27
|
+
>>> usage = client.get_usage()
|
|
28
|
+
>>> papers = client.search_papers(query="machine learning")
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
BASE_URL = os.environ.get("RAIROS_API_URL", "https://api.rairos.ai/api/v1")
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
api_key: Optional[str] = None,
|
|
36
|
+
max_retries: int = 3,
|
|
37
|
+
retry_delay: float = 1.0,
|
|
38
|
+
timeout: Optional[int] = 30
|
|
39
|
+
):
|
|
40
|
+
"""Initialize the Rairos client.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
api_key: Your Rairos API key. If not provided, will look for RAIROS_API_KEY env var.
|
|
44
|
+
max_retries: Maximum number of retries for failed requests (default: 3)
|
|
45
|
+
retry_delay: Initial delay between retries in seconds (default: 1.0)
|
|
46
|
+
timeout: Request timeout in seconds (default: 30)
|
|
47
|
+
"""
|
|
48
|
+
self.api_key = api_key or os.environ.get("RAIROS_API_KEY")
|
|
49
|
+
if not self.api_key:
|
|
50
|
+
raise AuthenticationError("API key is required. Pass api_key or set RAIROS_API_KEY env var.")
|
|
51
|
+
|
|
52
|
+
self.max_retries = max_retries
|
|
53
|
+
self.retry_delay = retry_delay
|
|
54
|
+
self.timeout = timeout
|
|
55
|
+
|
|
56
|
+
def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
57
|
+
"""Make an HTTP request to the Rairos API with automatic retry.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
method: HTTP method (GET, POST, etc.)
|
|
61
|
+
endpoint: API endpoint path
|
|
62
|
+
**kwargs: Additional arguments passed to requests.request
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Parsed JSON response
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
AuthenticationError: If API key is invalid
|
|
69
|
+
RateLimitError: If rate limit is exceeded
|
|
70
|
+
RairosError: For other errors
|
|
71
|
+
"""
|
|
72
|
+
import requests
|
|
73
|
+
from requests.adapters import HTTPAdapter
|
|
74
|
+
from urllib3.util.retry import Retry
|
|
75
|
+
|
|
76
|
+
url = f"{self.BASE_URL}{endpoint}"
|
|
77
|
+
headers = {"Authorization": f"Bearer {self.api_key}"}
|
|
78
|
+
headers.update(kwargs.pop("headers", {}))
|
|
79
|
+
|
|
80
|
+
timeout = kwargs.pop("timeout", self.timeout)
|
|
81
|
+
|
|
82
|
+
session = requests.Session()
|
|
83
|
+
retry_strategy = Retry(
|
|
84
|
+
total=self.max_retries,
|
|
85
|
+
backoff_factor=1,
|
|
86
|
+
status_forcelist=[429, 500, 502, 503, 504],
|
|
87
|
+
allowed_methods=["HEAD", "GET", "OPTIONS", "POST"],
|
|
88
|
+
raise_on_status=False
|
|
89
|
+
)
|
|
90
|
+
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
91
|
+
session.mount("http://", adapter)
|
|
92
|
+
session.mount("https://", adapter)
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
response = session.request(
|
|
96
|
+
method,
|
|
97
|
+
url,
|
|
98
|
+
headers=headers,
|
|
99
|
+
timeout=timeout,
|
|
100
|
+
**kwargs
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if response.status_code == 401:
|
|
104
|
+
raise AuthenticationError(
|
|
105
|
+
"Invalid or expired API key",
|
|
106
|
+
details={"endpoint": endpoint}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if response.status_code == 429:
|
|
110
|
+
error_data = {}
|
|
111
|
+
try:
|
|
112
|
+
error_data = response.json()
|
|
113
|
+
except ValueError:
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
limit = error_data.get("error", {}).get("limit")
|
|
117
|
+
reset_at = error_data.get("error", {}).get("reset_at")
|
|
118
|
+
|
|
119
|
+
raise RateLimitError(
|
|
120
|
+
"Rate limit exceeded. Please retry after the reset time.",
|
|
121
|
+
limit=limit,
|
|
122
|
+
reset_at=reset_at,
|
|
123
|
+
details=error_data
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if not response.ok:
|
|
127
|
+
error_data = {}
|
|
128
|
+
try:
|
|
129
|
+
error_data = response.json()
|
|
130
|
+
except ValueError:
|
|
131
|
+
error_data = {"error": {"message": response.text or "Unknown error"}}
|
|
132
|
+
|
|
133
|
+
raise_from_response(response.status_code, error_data)
|
|
134
|
+
|
|
135
|
+
if response.content:
|
|
136
|
+
return response.json()
|
|
137
|
+
return {}
|
|
138
|
+
|
|
139
|
+
except requests.exceptions.Timeout:
|
|
140
|
+
raise RairosError(
|
|
141
|
+
f"Request timed out after {timeout}s: {endpoint}",
|
|
142
|
+
code="TIMEOUT",
|
|
143
|
+
details={"endpoint": endpoint, "timeout": timeout}
|
|
144
|
+
)
|
|
145
|
+
except requests.exceptions.ConnectionError as e:
|
|
146
|
+
raise RairosError(
|
|
147
|
+
f"Connection failed: {str(e)}",
|
|
148
|
+
code="CONNECTION_ERROR",
|
|
149
|
+
details={"endpoint": endpoint}
|
|
150
|
+
)
|
|
151
|
+
except RairosError:
|
|
152
|
+
raise
|
|
153
|
+
except Exception as e:
|
|
154
|
+
raise RairosError(
|
|
155
|
+
f"Request failed: {str(e)}",
|
|
156
|
+
code="UNKNOWN_ERROR",
|
|
157
|
+
details={"endpoint": endpoint, "error": str(e)}
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Authentication
|
|
161
|
+
def register(self, email: str, password: str) -> Dict[str, Any]:
|
|
162
|
+
"""Register a new user.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
email: User email
|
|
166
|
+
password: User password (min 8 characters)
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Auth response with user_id and API key
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
ValidationError: If email or password is invalid
|
|
173
|
+
"""
|
|
174
|
+
return self._request(
|
|
175
|
+
"POST",
|
|
176
|
+
"/auth/register",
|
|
177
|
+
json={"email": email, "password": password}
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def login(self, email: str, password: str) -> Dict[str, Any]:
|
|
181
|
+
"""Login and get a new API key.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
email: User email
|
|
185
|
+
password: User password
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Auth response with user_id and API key
|
|
189
|
+
"""
|
|
190
|
+
return self._request(
|
|
191
|
+
"POST",
|
|
192
|
+
"/auth/login",
|
|
193
|
+
json={"email": email, "password": password}
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# API Keys
|
|
197
|
+
def list_keys(self) -> List[Dict[str, Any]]:
|
|
198
|
+
"""List all API keys for the current user.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
List of API key objects
|
|
202
|
+
"""
|
|
203
|
+
return self._request("GET", "/keys")
|
|
204
|
+
|
|
205
|
+
def create_key(self, name: Optional[str] = None) -> Dict[str, Any]:
|
|
206
|
+
"""Create a new API key.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
name: Optional name for the key
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
New API key details including the raw key (only shown once)
|
|
213
|
+
"""
|
|
214
|
+
return self._request(
|
|
215
|
+
"POST",
|
|
216
|
+
"/keys",
|
|
217
|
+
json={"name": name} if name else {}
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def rotate_key(
|
|
221
|
+
self,
|
|
222
|
+
key_id: str,
|
|
223
|
+
grace_period_hours: int = 24
|
|
224
|
+
) -> Dict[str, Any]:
|
|
225
|
+
"""Rotate an API key with a grace period.
|
|
226
|
+
|
|
227
|
+
The old key remains valid during the grace period.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
key_id: ID of the key to rotate
|
|
231
|
+
grace_period_hours: Hours until old key expires (default: 24)
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
New key details and old key expiration time
|
|
235
|
+
"""
|
|
236
|
+
return self._request(
|
|
237
|
+
"POST",
|
|
238
|
+
"/keys/rotate",
|
|
239
|
+
json={
|
|
240
|
+
"key_id": key_id,
|
|
241
|
+
"grace_period_hours": grace_period_hours
|
|
242
|
+
}
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Usage
|
|
246
|
+
def get_usage(self) -> Dict[str, Any]:
|
|
247
|
+
"""Get current API usage statistics.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Usage statistics including tier, requests used/remaining
|
|
251
|
+
"""
|
|
252
|
+
return self._request("GET", "/usage")
|
|
253
|
+
|
|
254
|
+
def get_usage_dashboard(self) -> Dict[str, Any]:
|
|
255
|
+
"""Get detailed usage dashboard with breakdowns.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Detailed usage stats including endpoint breakdown and trends
|
|
259
|
+
"""
|
|
260
|
+
return self._request("GET", "/usage/dashboard")
|
|
261
|
+
|
|
262
|
+
# Papers
|
|
263
|
+
def search_papers(
|
|
264
|
+
self,
|
|
265
|
+
query: Optional[str] = None,
|
|
266
|
+
page: int = 1,
|
|
267
|
+
per_page: int = 20
|
|
268
|
+
) -> Dict[str, Any]:
|
|
269
|
+
"""Search papers.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
query: Search query (searches title and abstract)
|
|
273
|
+
page: Page number (default: 1)
|
|
274
|
+
per_page: Results per page (default: 20, max: 100)
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Paginated paper results
|
|
278
|
+
"""
|
|
279
|
+
params = {"page": page, "per_page": per_page}
|
|
280
|
+
if query:
|
|
281
|
+
params["q"] = query
|
|
282
|
+
|
|
283
|
+
return self._request("GET", "/papers/search", params=params)
|
|
284
|
+
|
|
285
|
+
def get_paper(self, paper_id: str) -> Dict[str, Any]:
|
|
286
|
+
"""Get a specific paper by ID.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
paper_id: Paper UUID
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Paper details
|
|
293
|
+
"""
|
|
294
|
+
return self._request("GET", f"/papers/{paper_id}")
|
|
295
|
+
|
|
296
|
+
# Gap Detection (Pro+)
|
|
297
|
+
def detect_gap(self, query: str, **kwargs) -> Dict[str, Any]:
|
|
298
|
+
"""Detect research gaps for a query. Requires Pro tier or higher.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
query: Research query to analyze
|
|
302
|
+
**kwargs: Additional parameters for gap detection
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Gap detection results with identified gaps
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
ForbiddenError: If tier is insufficient
|
|
309
|
+
"""
|
|
310
|
+
return self._request(
|
|
311
|
+
"POST",
|
|
312
|
+
"/gap/detect",
|
|
313
|
+
json={"query": query, **kwargs}
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Research (Team+)
|
|
317
|
+
def run_research(self, query: str, **kwargs) -> Dict[str, Any]:
|
|
318
|
+
"""Run automated research. Requires Team tier or higher.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
query: Research query
|
|
322
|
+
**kwargs: Additional parameters for research
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Research results
|
|
326
|
+
|
|
327
|
+
Raises:
|
|
328
|
+
ForbiddenError: If tier is insufficient
|
|
329
|
+
"""
|
|
330
|
+
return self._request(
|
|
331
|
+
"POST",
|
|
332
|
+
"/research/run",
|
|
333
|
+
json={"query": query, **kwargs}
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# Subscriptions
|
|
337
|
+
def get_tiers(self) -> Dict[str, Any]:
|
|
338
|
+
"""Get available subscription tiers.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
List of available tiers with pricing
|
|
342
|
+
"""
|
|
343
|
+
return self._request("GET", "/tiers")
|
|
344
|
+
|
|
345
|
+
def create_checkout(
|
|
346
|
+
self,
|
|
347
|
+
price_id: str,
|
|
348
|
+
success_url: str = "https://rairos.ai/success",
|
|
349
|
+
cancel_url: str = "https://rairos.ai/pricing"
|
|
350
|
+
) -> Dict[str, Any]:
|
|
351
|
+
"""Create a Stripe checkout session for subscription.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
price_id: Stripe price ID for the tier
|
|
355
|
+
success_url: URL to redirect on success
|
|
356
|
+
cancel_url: URL to redirect on cancel
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Checkout URL and session ID
|
|
360
|
+
"""
|
|
361
|
+
return self._request(
|
|
362
|
+
"POST",
|
|
363
|
+
"/subscription/checkout",
|
|
364
|
+
json={
|
|
365
|
+
"price_id": price_id,
|
|
366
|
+
"success_url": success_url,
|
|
367
|
+
"cancel_url": cancel_url
|
|
368
|
+
}
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
def create_portal(self, return_url: str = "https://rairos.ai/dashboard") -> Dict[str, Any]:
|
|
372
|
+
"""Create a Stripe customer portal session.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
return_url: URL to redirect after using the portal
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Portal URL
|
|
379
|
+
"""
|
|
380
|
+
return self._request(
|
|
381
|
+
"POST",
|
|
382
|
+
"/subscription/portal",
|
|
383
|
+
json={"return_url": return_url}
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
def get_subscription_status(self) -> Dict[str, Any]:
|
|
387
|
+
"""Get current subscription status.
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
Subscription status including tier and Stripe info
|
|
391
|
+
"""
|
|
392
|
+
return self._request("GET", "/subscription/status")
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Rairos SDK Exceptions."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RairosError(Exception):
|
|
8
|
+
"""Base exception for Rairos SDK errors.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
message: Error message
|
|
12
|
+
code: Error code (e.g., 'RATE_LIMITED', 'AUTH')
|
|
13
|
+
status_code: HTTP status code if available
|
|
14
|
+
details: Additional error details from API
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
message: str,
|
|
20
|
+
code: str = "ERROR",
|
|
21
|
+
status_code: Optional[int] = None,
|
|
22
|
+
details: Optional[dict] = None
|
|
23
|
+
):
|
|
24
|
+
super().__init__(message)
|
|
25
|
+
self.message = message
|
|
26
|
+
self.code = code
|
|
27
|
+
self.status_code = status_code
|
|
28
|
+
self.details = details or {}
|
|
29
|
+
|
|
30
|
+
def __str__(self) -> str:
|
|
31
|
+
if self.code != "ERROR":
|
|
32
|
+
return f"[{self.code}] {self.message}"
|
|
33
|
+
return self.message
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AuthenticationError(RairosError):
|
|
37
|
+
"""Raised when authentication fails (invalid or expired API key).
|
|
38
|
+
|
|
39
|
+
HTTP Status: 401
|
|
40
|
+
Error Codes: AUTH, INVALID_API_KEY, UNAUTHORIZED
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str = "Authentication failed", details: Optional[dict] = None):
|
|
44
|
+
super().__init__(message, code="AUTH", status_code=401, details=details)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class RateLimitError(RairosError):
|
|
48
|
+
"""Raised when rate limit is exceeded.
|
|
49
|
+
|
|
50
|
+
HTTP Status: 429
|
|
51
|
+
Error Code: RATE_LIMITED
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
limit: The rate limit that was exceeded
|
|
55
|
+
reset_at: When the rate limit will reset
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
message: str = "Rate limit exceeded",
|
|
61
|
+
limit: Optional[int] = None,
|
|
62
|
+
reset_at: Optional[datetime] = None,
|
|
63
|
+
details: Optional[dict] = None
|
|
64
|
+
):
|
|
65
|
+
super().__init__(message, code="RATE_LIMITED", status_code=429, details=details)
|
|
66
|
+
self.limit = limit
|
|
67
|
+
self.reset_at = reset_at
|
|
68
|
+
|
|
69
|
+
def __str__(self) -> str:
|
|
70
|
+
parts = [f"[RATE_LIMITED] {self.message}"]
|
|
71
|
+
if self.limit:
|
|
72
|
+
parts.append(f"Limit: {self.limit}")
|
|
73
|
+
if self.reset_at:
|
|
74
|
+
parts.append(f"Resets at: {self.reset_at.isoformat()}")
|
|
75
|
+
return " | ".join(parts)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ValidationError(RairosError):
|
|
79
|
+
"""Raised when request validation fails.
|
|
80
|
+
|
|
81
|
+
HTTP Status: 400
|
|
82
|
+
Error Codes: VALIDATION_ERROR, BAD_REQUEST
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self, message: str = "Validation failed", details: Optional[dict] = None):
|
|
86
|
+
super().__init__(message, code="VALIDATION_ERROR", status_code=400, details=details)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class NotFoundError(RairosError):
|
|
90
|
+
"""Raised when a resource is not found.
|
|
91
|
+
|
|
92
|
+
HTTP Status: 404
|
|
93
|
+
Error Codes: NOT_FOUND
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(self, message: str = "Resource not found", details: Optional[dict] = None):
|
|
97
|
+
super().__init__(message, code="NOT_FOUND", status_code=404, details=details)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ForbiddenError(RairosError):
|
|
101
|
+
"""Raised when access to a resource is forbidden (insufficient tier).
|
|
102
|
+
|
|
103
|
+
HTTP Status: 403
|
|
104
|
+
Error Codes: FORBIDDEN, INSUFFICIENT_PERMISSIONS
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, message: str = "Access forbidden", details: Optional[dict] = None):
|
|
108
|
+
super().__init__(message, code="FORBIDDEN", status_code=403, details=details)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class ServerError(RairosError):
|
|
112
|
+
"""Raised when an internal server error occurs.
|
|
113
|
+
|
|
114
|
+
HTTP Status: 500+
|
|
115
|
+
Error Codes: INTERNAL_ERROR, DATABASE_ERROR, SERVER_ERROR
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
def __init__(self, message: str = "Internal server error", details: Optional[dict] = None):
|
|
119
|
+
super().__init__(message, code="SERVER_ERROR", status_code=500, details=details)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class PaymentError(RairosError):
|
|
123
|
+
"""Raised when a payment-related error occurs.
|
|
124
|
+
|
|
125
|
+
HTTP Status: 402
|
|
126
|
+
Error Codes: PAYMENT_ERROR
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def __init__(self, message: str = "Payment error", details: Optional[dict] = None):
|
|
130
|
+
super().__init__(message, code="PAYMENT_ERROR", status_code=402, details=details)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def parse_error_response(response_data: dict) -> tuple[str, dict]:
|
|
134
|
+
"""Parse error response from API.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Tuple of (error_code, error_details)
|
|
138
|
+
"""
|
|
139
|
+
error = response_data.get("error", {})
|
|
140
|
+
code = error.get("code", "ERROR")
|
|
141
|
+
details = {
|
|
142
|
+
"limit": error.get("limit"),
|
|
143
|
+
"reset_at": error.get("reset_at"),
|
|
144
|
+
}
|
|
145
|
+
return code, details
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def raise_from_response(status_code: int, response_data: dict):
|
|
149
|
+
"""Raise appropriate exception from API error response.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
status_code: HTTP status code
|
|
153
|
+
response_data: Parsed JSON response
|
|
154
|
+
|
|
155
|
+
Raises:
|
|
156
|
+
Appropriate RairosError subclass
|
|
157
|
+
"""
|
|
158
|
+
code, details = parse_error_response(response_data)
|
|
159
|
+
message = response_data.get("error", {}).get("message", "Unknown error")
|
|
160
|
+
|
|
161
|
+
error_mapping = {
|
|
162
|
+
400: (ValidationError, "VALIDATION_ERROR"),
|
|
163
|
+
401: (AuthenticationError, "AUTH"),
|
|
164
|
+
403: (ForbiddenError, "FORBIDDEN"),
|
|
165
|
+
404: (NotFoundError, "NOT_FOUND"),
|
|
166
|
+
402: (PaymentError, "PAYMENT_ERROR"),
|
|
167
|
+
429: (RateLimitError, "RATE_LIMITED"),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if status_code in error_mapping:
|
|
171
|
+
exc_class, expected_code = error_mapping[status_code]
|
|
172
|
+
if status_code == 429 and details.get("limit"):
|
|
173
|
+
reset_at_str = details.get("reset_at")
|
|
174
|
+
reset_at = datetime.fromisoformat(reset_at_str.replace("Z", "+00:00")) if reset_at_str else None
|
|
175
|
+
raise exc_class(message, limit=details["limit"], reset_at=reset_at, details=details)
|
|
176
|
+
raise exc_class(message, details=details)
|
|
177
|
+
|
|
178
|
+
if status_code >= 500:
|
|
179
|
+
raise ServerError(message, details=details)
|
|
180
|
+
|
|
181
|
+
raise RairosError(message, code=code, status_code=status_code, details=details)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rairos
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for Rairos API
|
|
5
|
+
Author-email: Rairos Team <team@rairos.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://rairos.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.rairos.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/shushuzn/Rairos
|
|
10
|
+
Project-URL: Changelog, https://github.com/shushuzn/Rairos/blob/main/sdks/python/CHANGELOG.md
|
|
11
|
+
Keywords: rairos,api,research,ai
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: requests>=2.28.0
|
|
23
|
+
Requires-Dist: urllib3>=2.0.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
27
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
29
|
+
|
|
30
|
+
# Rairos Python SDK
|
|
31
|
+
|
|
32
|
+
[](https://pypi.org/project/rairos/)
|
|
33
|
+
[](https://pypi.org/project/rairos/)
|
|
34
|
+
[](https://github.com/shushuzn/Rairos/blob/main/sdks/python/pyproject.toml)
|
|
35
|
+
|
|
36
|
+
Python SDK for the Rairos API platform.
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# From PyPI (coming soon)
|
|
42
|
+
pip install rairos
|
|
43
|
+
|
|
44
|
+
# From source
|
|
45
|
+
cd sdks/python && pip install -e . && cd ../..
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from rairos import RairosClient
|
|
52
|
+
|
|
53
|
+
# Initialize client
|
|
54
|
+
client = RairosClient(api_key="your_api_key_here")
|
|
55
|
+
|
|
56
|
+
# Search papers
|
|
57
|
+
results = client.search_papers(query="machine learning", page=1, per_page=20)
|
|
58
|
+
print(results)
|
|
59
|
+
|
|
60
|
+
# Detect research gaps (requires Pro tier)
|
|
61
|
+
gaps = client.detect_gap(query="neural network architecture")
|
|
62
|
+
print(gaps)
|
|
63
|
+
|
|
64
|
+
# Get usage statistics
|
|
65
|
+
usage = client.get_usage()
|
|
66
|
+
print(usage)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Authentication
|
|
70
|
+
|
|
71
|
+
You can provide your API key in three ways:
|
|
72
|
+
|
|
73
|
+
1. **Environment variable** (recommended):
|
|
74
|
+
```bash
|
|
75
|
+
export RAIROS_API_KEY=your_api_key_here
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
2. **Constructor parameter**:
|
|
79
|
+
```python
|
|
80
|
+
client = RairosClient(api_key="your_api_key_here")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
3. **Register/Login** to get a new API key:
|
|
84
|
+
```python
|
|
85
|
+
# Register
|
|
86
|
+
auth = client.register(email="user@example.com", password="secure_password")
|
|
87
|
+
|
|
88
|
+
# Login
|
|
89
|
+
auth = client.login(email="user@example.com", password="secure_password")
|
|
90
|
+
print(auth["api_key"]) # Use this key
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Reference
|
|
94
|
+
|
|
95
|
+
### Papers
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# Search papers
|
|
99
|
+
results = client.search_papers(query="quantum computing")
|
|
100
|
+
|
|
101
|
+
# Get specific paper
|
|
102
|
+
paper = client.get_paper(paper_id="uuid-here")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Gap Detection (Pro tier)
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Detect research gaps
|
|
109
|
+
gaps = client.detect_gap(
|
|
110
|
+
query="What are the gaps in transformer architecture research?",
|
|
111
|
+
categories=["cs.AI", "cs.LG"]
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Research (Team tier)
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Run automated research
|
|
119
|
+
results = client.run_research(
|
|
120
|
+
query="Analyze the state of AI safety research in 2025"
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Subscriptions
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# Get available tiers
|
|
128
|
+
tiers = client.get_tiers()
|
|
129
|
+
|
|
130
|
+
# Create checkout session
|
|
131
|
+
checkout = client.create_checkout(
|
|
132
|
+
price_id="price_pro_monthly",
|
|
133
|
+
success_url="https://yourapp.com/success",
|
|
134
|
+
cancel_url="https://yourapp.com/pricing"
|
|
135
|
+
)
|
|
136
|
+
print(checkout["checkout_url"]) # Redirect user to this URL
|
|
137
|
+
|
|
138
|
+
# Get subscription status
|
|
139
|
+
status = client.get_subscription_status()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Error Handling
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from rairos import (
|
|
146
|
+
RairosClient,
|
|
147
|
+
RairosError,
|
|
148
|
+
AuthenticationError,
|
|
149
|
+
RateLimitError
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
client = RairosClient(api_key="invalid_key")
|
|
154
|
+
results = client.search_papers(query="test")
|
|
155
|
+
except AuthenticationError:
|
|
156
|
+
print("Invalid API key")
|
|
157
|
+
except RateLimitError:
|
|
158
|
+
print("Rate limit exceeded - wait and retry")
|
|
159
|
+
except RairosError as e:
|
|
160
|
+
print(f"API error: {e.message}")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rairos
|
rairos-0.2.0/setup.cfg
ADDED