raysdk-py 0.1.0__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.
- raysdk_py/__init__.py +4 -0
- raysdk_py/api.py +607 -0
- raysdk_py/monitors/__init__.py +59 -0
- raysdk_py/monitors/async_base.py +33 -0
- raysdk_py/monitors/async_client.py +117 -0
- raysdk_py/monitors/base.py +111 -0
- raysdk_py/monitors/client.py +116 -0
- raysdk_py/monitors/types.py +555 -0
- raysdk_py/py.typed +0 -0
- raysdk_py/research/__init__.py +23 -0
- raysdk_py/research/async_client.py +117 -0
- raysdk_py/research/base.py +113 -0
- raysdk_py/research/models.py +69 -0
- raysdk_py/research/sync_client.py +109 -0
- raysdk_py/research/utils.py +34 -0
- raysdk_py/utils.py +90 -0
- raysdk_py-0.1.0.dist-info/METADATA +299 -0
- raysdk_py-0.1.0.dist-info/RECORD +21 -0
- raysdk_py-0.1.0.dist-info/WHEEL +5 -0
- raysdk_py-0.1.0.dist-info/licenses/LICENSE +201 -0
- raysdk_py-0.1.0.dist-info/top_level.txt +1 -0
raysdk_py/__init__.py
ADDED
raysdk_py/api.py
ADDED
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib.metadata
|
|
4
|
+
import os
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
from pydantic import BaseModel, ValidationError
|
|
10
|
+
|
|
11
|
+
from .utils import normalize_base_url
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .monitors import AsyncMonitorsClient, MonitorsClient
|
|
15
|
+
from .monitors.types import (
|
|
16
|
+
AnswerResponse,
|
|
17
|
+
FetchResponse,
|
|
18
|
+
HealthResponse,
|
|
19
|
+
SearchFetchRequest,
|
|
20
|
+
SearchResponse,
|
|
21
|
+
)
|
|
22
|
+
from .research import AsyncResearchClient, ResearchClient
|
|
23
|
+
|
|
24
|
+
DEFAULT_BASE_URL = "http://localhost:8000"
|
|
25
|
+
DEFAULT_TIMEOUT = 30.0
|
|
26
|
+
|
|
27
|
+
ModelT = TypeVar("ModelT", bound=BaseModel)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _get_package_version() -> str:
|
|
31
|
+
try:
|
|
32
|
+
return importlib.metadata.version("raysdk-py")
|
|
33
|
+
except importlib.metadata.PackageNotFoundError:
|
|
34
|
+
return "0.1.0"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
DEFAULT_USER_AGENT = f"raysdk-py/{_get_package_version()}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RaySDKError(Exception):
|
|
41
|
+
"""Base exception for the SDK."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class APIConnectionError(RaySDKError):
|
|
45
|
+
"""Raised when the service cannot be reached."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class APITimeoutError(RaySDKError):
|
|
49
|
+
"""Raised when a request exceeds the configured timeout."""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class APIStatusError(RaySDKError):
|
|
53
|
+
"""Raised when the API returns a non-success status code."""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
*,
|
|
58
|
+
status_code: int,
|
|
59
|
+
detail: str,
|
|
60
|
+
url: str,
|
|
61
|
+
payload: object | None = None,
|
|
62
|
+
) -> None:
|
|
63
|
+
self.status_code = status_code
|
|
64
|
+
self.detail = detail
|
|
65
|
+
self.url = url
|
|
66
|
+
self.payload = payload
|
|
67
|
+
super().__init__(f"API request failed with status {status_code}: {detail}")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class APIResponseValidationError(RaySDKError):
|
|
71
|
+
"""Raised when an API response does not match the expected schema."""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
*,
|
|
76
|
+
model_name: str,
|
|
77
|
+
payload: object,
|
|
78
|
+
cause: ValidationError,
|
|
79
|
+
) -> None:
|
|
80
|
+
self.model_name = model_name
|
|
81
|
+
self.payload = payload
|
|
82
|
+
self.cause = cause
|
|
83
|
+
super().__init__(f"failed to validate response as {model_name}: {cause}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _resolve_api_key(api_key: str | None) -> str:
|
|
87
|
+
token = api_key or os.environ.get("RAYSEARCH_API_KEY")
|
|
88
|
+
if token is None or not token.strip():
|
|
89
|
+
raise ValueError(
|
|
90
|
+
"API key must be provided as an argument or in RAYSEARCH_API_KEY."
|
|
91
|
+
)
|
|
92
|
+
return token
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _merge_headers(
|
|
96
|
+
*,
|
|
97
|
+
api_key: str,
|
|
98
|
+
default_headers: Mapping[str, str] | None,
|
|
99
|
+
user_agent: str,
|
|
100
|
+
) -> dict[str, str]:
|
|
101
|
+
headers: dict[str, str] = {
|
|
102
|
+
"Accept": "application/json",
|
|
103
|
+
"Authorization": f"Bearer {api_key}",
|
|
104
|
+
"User-Agent": user_agent,
|
|
105
|
+
}
|
|
106
|
+
if default_headers:
|
|
107
|
+
headers.update(default_headers)
|
|
108
|
+
return headers
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _extract_error_detail(response: httpx.Response) -> tuple[str, object | None]:
|
|
112
|
+
try:
|
|
113
|
+
payload = response.json()
|
|
114
|
+
except ValueError:
|
|
115
|
+
text = response.text.strip()
|
|
116
|
+
return text or response.reason_phrase, None
|
|
117
|
+
|
|
118
|
+
if isinstance(payload, dict):
|
|
119
|
+
detail = payload.get("detail")
|
|
120
|
+
if isinstance(detail, str) and detail.strip():
|
|
121
|
+
return detail, payload
|
|
122
|
+
return response.reason_phrase, payload
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def validate_response_model(model_type: type[ModelT], payload: object) -> ModelT:
|
|
126
|
+
try:
|
|
127
|
+
return model_type.model_validate(payload)
|
|
128
|
+
except ValidationError as exc:
|
|
129
|
+
raise APIResponseValidationError(
|
|
130
|
+
model_name=model_type.__name__,
|
|
131
|
+
payload=payload,
|
|
132
|
+
cause=exc,
|
|
133
|
+
) from exc
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def serialize_request_model(
|
|
137
|
+
model_type: type[ModelT],
|
|
138
|
+
payload: ModelT | Mapping[str, Any],
|
|
139
|
+
) -> dict[str, Any]:
|
|
140
|
+
model = (
|
|
141
|
+
payload
|
|
142
|
+
if isinstance(payload, model_type)
|
|
143
|
+
else model_type.model_validate(payload)
|
|
144
|
+
)
|
|
145
|
+
return model.model_dump(mode="json", exclude_none=True)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _raise_non_json_error(response: httpx.Response, exc: ValueError) -> None:
|
|
149
|
+
try:
|
|
150
|
+
raise ValidationError.from_exception_data(
|
|
151
|
+
title="JSON",
|
|
152
|
+
line_errors=[],
|
|
153
|
+
)
|
|
154
|
+
except ValidationError as validation_error:
|
|
155
|
+
raise APIResponseValidationError(
|
|
156
|
+
model_name="JSON",
|
|
157
|
+
payload=response.text,
|
|
158
|
+
cause=validation_error,
|
|
159
|
+
) from exc
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class SyncAPIClient:
|
|
163
|
+
def __init__(
|
|
164
|
+
self,
|
|
165
|
+
*,
|
|
166
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
167
|
+
api_key: str | None = None,
|
|
168
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
169
|
+
client: httpx.Client | None = None,
|
|
170
|
+
default_headers: Mapping[str, str] | None = None,
|
|
171
|
+
user_agent: str = DEFAULT_USER_AGENT,
|
|
172
|
+
) -> None:
|
|
173
|
+
resolved_api_key = _resolve_api_key(api_key)
|
|
174
|
+
self.base_url = normalize_base_url(base_url)
|
|
175
|
+
self.timeout = timeout
|
|
176
|
+
self.headers = _merge_headers(
|
|
177
|
+
api_key=resolved_api_key,
|
|
178
|
+
default_headers=default_headers,
|
|
179
|
+
user_agent=user_agent,
|
|
180
|
+
)
|
|
181
|
+
self._owns_client = client is None
|
|
182
|
+
self._client = client or httpx.Client(timeout=timeout)
|
|
183
|
+
|
|
184
|
+
def close(self) -> None:
|
|
185
|
+
if self._owns_client:
|
|
186
|
+
self._client.close()
|
|
187
|
+
|
|
188
|
+
def __enter__(self) -> SyncAPIClient:
|
|
189
|
+
return self
|
|
190
|
+
|
|
191
|
+
def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
|
|
192
|
+
del exc_type, exc, tb
|
|
193
|
+
self.close()
|
|
194
|
+
|
|
195
|
+
def _url_for(self, path: str) -> str:
|
|
196
|
+
return f"{self.base_url}{path}"
|
|
197
|
+
|
|
198
|
+
def request(
|
|
199
|
+
self,
|
|
200
|
+
endpoint: str,
|
|
201
|
+
data: Mapping[str, Any] | str | None = None,
|
|
202
|
+
method: str = "POST",
|
|
203
|
+
params: Mapping[str, str | int] | None = None,
|
|
204
|
+
headers: Mapping[str, str] | None = None,
|
|
205
|
+
) -> object:
|
|
206
|
+
url = self._url_for(endpoint)
|
|
207
|
+
request_headers = dict(self.headers)
|
|
208
|
+
if headers:
|
|
209
|
+
request_headers.update(headers)
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
if isinstance(data, str):
|
|
213
|
+
response = self._client.request(
|
|
214
|
+
method,
|
|
215
|
+
url,
|
|
216
|
+
headers=request_headers,
|
|
217
|
+
params=params,
|
|
218
|
+
content=data,
|
|
219
|
+
)
|
|
220
|
+
elif data is not None:
|
|
221
|
+
response = self._client.request(
|
|
222
|
+
method,
|
|
223
|
+
url,
|
|
224
|
+
headers=request_headers,
|
|
225
|
+
params=params,
|
|
226
|
+
json=data,
|
|
227
|
+
)
|
|
228
|
+
else:
|
|
229
|
+
response = self._client.request(
|
|
230
|
+
method,
|
|
231
|
+
url,
|
|
232
|
+
headers=request_headers,
|
|
233
|
+
params=params,
|
|
234
|
+
)
|
|
235
|
+
except httpx.TimeoutException as exc:
|
|
236
|
+
raise APITimeoutError(f"request timed out for {url}") from exc
|
|
237
|
+
except httpx.HTTPError as exc:
|
|
238
|
+
raise APIConnectionError(f"request failed for {url}") from exc
|
|
239
|
+
|
|
240
|
+
if response.is_error:
|
|
241
|
+
detail, payload = _extract_error_detail(response)
|
|
242
|
+
raise APIStatusError(
|
|
243
|
+
status_code=response.status_code,
|
|
244
|
+
detail=detail,
|
|
245
|
+
url=str(response.request.url),
|
|
246
|
+
payload=payload,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
try:
|
|
250
|
+
return response.json()
|
|
251
|
+
except ValueError as exc:
|
|
252
|
+
_raise_non_json_error(response, exc)
|
|
253
|
+
|
|
254
|
+
def request_model(
|
|
255
|
+
self,
|
|
256
|
+
model_type: type[ModelT],
|
|
257
|
+
endpoint: str,
|
|
258
|
+
*,
|
|
259
|
+
data: Mapping[str, Any] | str | None = None,
|
|
260
|
+
method: str = "POST",
|
|
261
|
+
params: Mapping[str, str | int] | None = None,
|
|
262
|
+
headers: Mapping[str, str] | None = None,
|
|
263
|
+
) -> ModelT:
|
|
264
|
+
payload = self.request(
|
|
265
|
+
endpoint,
|
|
266
|
+
data=data,
|
|
267
|
+
method=method,
|
|
268
|
+
params=params,
|
|
269
|
+
headers=headers,
|
|
270
|
+
)
|
|
271
|
+
return validate_response_model(model_type, payload)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class AsyncAPIClient:
|
|
275
|
+
def __init__(
|
|
276
|
+
self,
|
|
277
|
+
*,
|
|
278
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
279
|
+
api_key: str | None = None,
|
|
280
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
281
|
+
client: httpx.AsyncClient | None = None,
|
|
282
|
+
default_headers: Mapping[str, str] | None = None,
|
|
283
|
+
user_agent: str = DEFAULT_USER_AGENT,
|
|
284
|
+
) -> None:
|
|
285
|
+
resolved_api_key = _resolve_api_key(api_key)
|
|
286
|
+
self.base_url = normalize_base_url(base_url)
|
|
287
|
+
self.timeout = timeout
|
|
288
|
+
self.headers = _merge_headers(
|
|
289
|
+
api_key=resolved_api_key,
|
|
290
|
+
default_headers=default_headers,
|
|
291
|
+
user_agent=user_agent,
|
|
292
|
+
)
|
|
293
|
+
self._owns_client = client is None
|
|
294
|
+
self._client = client or httpx.AsyncClient(timeout=timeout)
|
|
295
|
+
|
|
296
|
+
async def aclose(self) -> None:
|
|
297
|
+
if self._owns_client:
|
|
298
|
+
await self._client.aclose()
|
|
299
|
+
|
|
300
|
+
async def __aenter__(self) -> AsyncAPIClient:
|
|
301
|
+
return self
|
|
302
|
+
|
|
303
|
+
async def __aexit__(self, exc_type: object, exc: object, tb: object) -> None:
|
|
304
|
+
del exc_type, exc, tb
|
|
305
|
+
await self.aclose()
|
|
306
|
+
|
|
307
|
+
def _url_for(self, path: str) -> str:
|
|
308
|
+
return f"{self.base_url}{path}"
|
|
309
|
+
|
|
310
|
+
async def async_request(
|
|
311
|
+
self,
|
|
312
|
+
endpoint: str,
|
|
313
|
+
data: Mapping[str, Any] | str | None = None,
|
|
314
|
+
method: str = "POST",
|
|
315
|
+
params: Mapping[str, str | int] | None = None,
|
|
316
|
+
headers: Mapping[str, str] | None = None,
|
|
317
|
+
) -> object:
|
|
318
|
+
url = self._url_for(endpoint)
|
|
319
|
+
request_headers = dict(self.headers)
|
|
320
|
+
if headers:
|
|
321
|
+
request_headers.update(headers)
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
if isinstance(data, str):
|
|
325
|
+
response = await self._client.request(
|
|
326
|
+
method,
|
|
327
|
+
url,
|
|
328
|
+
headers=request_headers,
|
|
329
|
+
params=params,
|
|
330
|
+
content=data,
|
|
331
|
+
)
|
|
332
|
+
elif data is not None:
|
|
333
|
+
response = await self._client.request(
|
|
334
|
+
method,
|
|
335
|
+
url,
|
|
336
|
+
headers=request_headers,
|
|
337
|
+
params=params,
|
|
338
|
+
json=data,
|
|
339
|
+
)
|
|
340
|
+
else:
|
|
341
|
+
response = await self._client.request(
|
|
342
|
+
method,
|
|
343
|
+
url,
|
|
344
|
+
headers=request_headers,
|
|
345
|
+
params=params,
|
|
346
|
+
)
|
|
347
|
+
except httpx.TimeoutException as exc:
|
|
348
|
+
raise APITimeoutError(f"request timed out for {url}") from exc
|
|
349
|
+
except httpx.HTTPError as exc:
|
|
350
|
+
raise APIConnectionError(f"request failed for {url}") from exc
|
|
351
|
+
|
|
352
|
+
if response.is_error:
|
|
353
|
+
detail, payload = _extract_error_detail(response)
|
|
354
|
+
raise APIStatusError(
|
|
355
|
+
status_code=response.status_code,
|
|
356
|
+
detail=detail,
|
|
357
|
+
url=str(response.request.url),
|
|
358
|
+
payload=payload,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
return response.json()
|
|
363
|
+
except ValueError as exc:
|
|
364
|
+
_raise_non_json_error(response, exc)
|
|
365
|
+
|
|
366
|
+
async def request_model(
|
|
367
|
+
self,
|
|
368
|
+
model_type: type[ModelT],
|
|
369
|
+
endpoint: str,
|
|
370
|
+
*,
|
|
371
|
+
data: Mapping[str, Any] | str | None = None,
|
|
372
|
+
method: str = "POST",
|
|
373
|
+
params: Mapping[str, str | int] | None = None,
|
|
374
|
+
headers: Mapping[str, str] | None = None,
|
|
375
|
+
) -> ModelT:
|
|
376
|
+
payload = await self.async_request(
|
|
377
|
+
endpoint,
|
|
378
|
+
data=data,
|
|
379
|
+
method=method,
|
|
380
|
+
params=params,
|
|
381
|
+
headers=headers,
|
|
382
|
+
)
|
|
383
|
+
return validate_response_model(model_type, payload)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
class RaySearch(SyncAPIClient):
|
|
387
|
+
"""Top-level synchronous client for the RaySearch API."""
|
|
388
|
+
|
|
389
|
+
monitors: MonitorsClient
|
|
390
|
+
research: ResearchClient
|
|
391
|
+
|
|
392
|
+
def __init__(
|
|
393
|
+
self,
|
|
394
|
+
api_key: str | None = None,
|
|
395
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
396
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
397
|
+
user_agent: str | None = None,
|
|
398
|
+
client: httpx.Client | None = None,
|
|
399
|
+
default_headers: Mapping[str, str] | None = None,
|
|
400
|
+
) -> None:
|
|
401
|
+
super().__init__(
|
|
402
|
+
base_url=base_url,
|
|
403
|
+
api_key=api_key,
|
|
404
|
+
timeout=timeout,
|
|
405
|
+
client=client,
|
|
406
|
+
default_headers=default_headers,
|
|
407
|
+
user_agent=user_agent or DEFAULT_USER_AGENT,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
from .monitors import MonitorsClient
|
|
411
|
+
from .research import ResearchClient
|
|
412
|
+
|
|
413
|
+
self.monitors = MonitorsClient(self)
|
|
414
|
+
self.research = ResearchClient(self)
|
|
415
|
+
|
|
416
|
+
def healthz(self) -> HealthResponse:
|
|
417
|
+
return self.monitors.healthz()
|
|
418
|
+
|
|
419
|
+
def search(
|
|
420
|
+
self,
|
|
421
|
+
query: str,
|
|
422
|
+
*,
|
|
423
|
+
user_location: str,
|
|
424
|
+
additional_queries: list[str] | None = None,
|
|
425
|
+
mode: str = "auto",
|
|
426
|
+
max_results: int | None = None,
|
|
427
|
+
start_published_date: str | None = None,
|
|
428
|
+
end_published_date: str | None = None,
|
|
429
|
+
include_domains: list[str] | None = None,
|
|
430
|
+
exclude_domains: list[str] | None = None,
|
|
431
|
+
include_text: list[str] | None = None,
|
|
432
|
+
exclude_text: list[str] | None = None,
|
|
433
|
+
moderation: bool = True,
|
|
434
|
+
fetchs: SearchFetchRequest | dict[str, Any],
|
|
435
|
+
) -> SearchResponse:
|
|
436
|
+
return self.monitors.search(
|
|
437
|
+
query,
|
|
438
|
+
user_location=user_location,
|
|
439
|
+
additional_queries=additional_queries,
|
|
440
|
+
mode=mode,
|
|
441
|
+
max_results=max_results,
|
|
442
|
+
start_published_date=start_published_date,
|
|
443
|
+
end_published_date=end_published_date,
|
|
444
|
+
include_domains=include_domains,
|
|
445
|
+
exclude_domains=exclude_domains,
|
|
446
|
+
include_text=include_text,
|
|
447
|
+
exclude_text=exclude_text,
|
|
448
|
+
moderation=moderation,
|
|
449
|
+
fetchs=fetchs,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
def fetch(
|
|
453
|
+
self,
|
|
454
|
+
urls: str | list[str],
|
|
455
|
+
*,
|
|
456
|
+
crawl_mode: str = "fallback",
|
|
457
|
+
crawl_timeout: float | None = None,
|
|
458
|
+
content: bool | dict[str, Any] = False,
|
|
459
|
+
abstracts: bool | dict[str, Any] = False,
|
|
460
|
+
subpages: dict[str, Any] | None = None,
|
|
461
|
+
overview: bool | dict[str, Any] = False,
|
|
462
|
+
others: dict[str, Any] | None = None,
|
|
463
|
+
) -> FetchResponse:
|
|
464
|
+
return self.monitors.fetch(
|
|
465
|
+
urls,
|
|
466
|
+
crawl_mode=crawl_mode,
|
|
467
|
+
crawl_timeout=crawl_timeout,
|
|
468
|
+
content=content,
|
|
469
|
+
abstracts=abstracts,
|
|
470
|
+
subpages=subpages,
|
|
471
|
+
overview=overview,
|
|
472
|
+
others=others,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
def answer(
|
|
476
|
+
self,
|
|
477
|
+
query: str,
|
|
478
|
+
*,
|
|
479
|
+
json_schema: dict[str, Any] | None = None,
|
|
480
|
+
content: bool = False,
|
|
481
|
+
) -> AnswerResponse:
|
|
482
|
+
return self.monitors.answer(
|
|
483
|
+
query,
|
|
484
|
+
json_schema=json_schema,
|
|
485
|
+
content=content,
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
class AsyncRaySearch(AsyncAPIClient):
|
|
490
|
+
"""Top-level asynchronous client for the RaySearch API."""
|
|
491
|
+
|
|
492
|
+
monitors: AsyncMonitorsClient
|
|
493
|
+
research: AsyncResearchClient
|
|
494
|
+
|
|
495
|
+
def __init__(
|
|
496
|
+
self,
|
|
497
|
+
api_key: str | None = None,
|
|
498
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
499
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
500
|
+
user_agent: str | None = None,
|
|
501
|
+
client: httpx.AsyncClient | None = None,
|
|
502
|
+
default_headers: Mapping[str, str] | None = None,
|
|
503
|
+
) -> None:
|
|
504
|
+
super().__init__(
|
|
505
|
+
base_url=base_url,
|
|
506
|
+
api_key=api_key,
|
|
507
|
+
timeout=timeout,
|
|
508
|
+
client=client,
|
|
509
|
+
default_headers=default_headers,
|
|
510
|
+
user_agent=user_agent or DEFAULT_USER_AGENT,
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
from .monitors import AsyncMonitorsClient
|
|
514
|
+
from .research import AsyncResearchClient
|
|
515
|
+
|
|
516
|
+
self.monitors = AsyncMonitorsClient(self)
|
|
517
|
+
self.research = AsyncResearchClient(self)
|
|
518
|
+
|
|
519
|
+
async def healthz(self) -> HealthResponse:
|
|
520
|
+
return await self.monitors.healthz()
|
|
521
|
+
|
|
522
|
+
async def search(
|
|
523
|
+
self,
|
|
524
|
+
query: str,
|
|
525
|
+
*,
|
|
526
|
+
user_location: str,
|
|
527
|
+
additional_queries: list[str] | None = None,
|
|
528
|
+
mode: str = "auto",
|
|
529
|
+
max_results: int | None = None,
|
|
530
|
+
start_published_date: str | None = None,
|
|
531
|
+
end_published_date: str | None = None,
|
|
532
|
+
include_domains: list[str] | None = None,
|
|
533
|
+
exclude_domains: list[str] | None = None,
|
|
534
|
+
include_text: list[str] | None = None,
|
|
535
|
+
exclude_text: list[str] | None = None,
|
|
536
|
+
moderation: bool = True,
|
|
537
|
+
fetchs: SearchFetchRequest | dict[str, Any],
|
|
538
|
+
) -> SearchResponse:
|
|
539
|
+
return await self.monitors.search(
|
|
540
|
+
query,
|
|
541
|
+
user_location=user_location,
|
|
542
|
+
additional_queries=additional_queries,
|
|
543
|
+
mode=mode,
|
|
544
|
+
max_results=max_results,
|
|
545
|
+
start_published_date=start_published_date,
|
|
546
|
+
end_published_date=end_published_date,
|
|
547
|
+
include_domains=include_domains,
|
|
548
|
+
exclude_domains=exclude_domains,
|
|
549
|
+
include_text=include_text,
|
|
550
|
+
exclude_text=exclude_text,
|
|
551
|
+
moderation=moderation,
|
|
552
|
+
fetchs=fetchs,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
async def fetch(
|
|
556
|
+
self,
|
|
557
|
+
urls: str | list[str],
|
|
558
|
+
*,
|
|
559
|
+
crawl_mode: str = "fallback",
|
|
560
|
+
crawl_timeout: float | None = None,
|
|
561
|
+
content: bool | dict[str, Any] = False,
|
|
562
|
+
abstracts: bool | dict[str, Any] = False,
|
|
563
|
+
subpages: dict[str, Any] | None = None,
|
|
564
|
+
overview: bool | dict[str, Any] = False,
|
|
565
|
+
others: dict[str, Any] | None = None,
|
|
566
|
+
) -> FetchResponse:
|
|
567
|
+
return await self.monitors.fetch(
|
|
568
|
+
urls,
|
|
569
|
+
crawl_mode=crawl_mode,
|
|
570
|
+
crawl_timeout=crawl_timeout,
|
|
571
|
+
content=content,
|
|
572
|
+
abstracts=abstracts,
|
|
573
|
+
subpages=subpages,
|
|
574
|
+
overview=overview,
|
|
575
|
+
others=others,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
async def answer(
|
|
579
|
+
self,
|
|
580
|
+
query: str,
|
|
581
|
+
*,
|
|
582
|
+
json_schema: dict[str, Any] | None = None,
|
|
583
|
+
content: bool = False,
|
|
584
|
+
) -> AnswerResponse:
|
|
585
|
+
return await self.monitors.answer(
|
|
586
|
+
query,
|
|
587
|
+
json_schema=json_schema,
|
|
588
|
+
content=content,
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
__all__ = [
|
|
593
|
+
"APIConnectionError",
|
|
594
|
+
"APIResponseValidationError",
|
|
595
|
+
"APIStatusError",
|
|
596
|
+
"APITimeoutError",
|
|
597
|
+
"AsyncAPIClient",
|
|
598
|
+
"AsyncRaySearch",
|
|
599
|
+
"DEFAULT_BASE_URL",
|
|
600
|
+
"DEFAULT_TIMEOUT",
|
|
601
|
+
"DEFAULT_USER_AGENT",
|
|
602
|
+
"RaySDKError",
|
|
603
|
+
"RaySearch",
|
|
604
|
+
"SyncAPIClient",
|
|
605
|
+
"serialize_request_model",
|
|
606
|
+
"validate_response_model",
|
|
607
|
+
]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Monitor-style API client modules for RaySearch."""
|
|
2
|
+
|
|
3
|
+
from .async_client import AsyncMonitorsClient
|
|
4
|
+
from .client import MonitorsClient
|
|
5
|
+
from .types import (
|
|
6
|
+
AnswerCitation,
|
|
7
|
+
AnswerRequest,
|
|
8
|
+
AnswerResponse,
|
|
9
|
+
CrawlMode,
|
|
10
|
+
FetchAbstractsRequest,
|
|
11
|
+
FetchContentDetail,
|
|
12
|
+
FetchContentRequest,
|
|
13
|
+
FetchContentTag,
|
|
14
|
+
FetchErrorTag,
|
|
15
|
+
FetchOthersRequest,
|
|
16
|
+
FetchOthersResult,
|
|
17
|
+
FetchOverviewRequest,
|
|
18
|
+
FetchRequest,
|
|
19
|
+
FetchResponse,
|
|
20
|
+
FetchResultItem,
|
|
21
|
+
FetchStatusError,
|
|
22
|
+
FetchStatusItem,
|
|
23
|
+
FetchSubpagesRequest,
|
|
24
|
+
FetchSubpagesResult,
|
|
25
|
+
HealthResponse,
|
|
26
|
+
SearchFetchRequest,
|
|
27
|
+
SearchMode,
|
|
28
|
+
SearchRequest,
|
|
29
|
+
SearchResponse,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"AnswerCitation",
|
|
34
|
+
"AnswerRequest",
|
|
35
|
+
"AnswerResponse",
|
|
36
|
+
"AsyncMonitorsClient",
|
|
37
|
+
"CrawlMode",
|
|
38
|
+
"FetchAbstractsRequest",
|
|
39
|
+
"FetchContentDetail",
|
|
40
|
+
"FetchContentRequest",
|
|
41
|
+
"FetchContentTag",
|
|
42
|
+
"FetchErrorTag",
|
|
43
|
+
"FetchOthersRequest",
|
|
44
|
+
"FetchOthersResult",
|
|
45
|
+
"FetchOverviewRequest",
|
|
46
|
+
"FetchRequest",
|
|
47
|
+
"FetchResponse",
|
|
48
|
+
"FetchResultItem",
|
|
49
|
+
"FetchStatusError",
|
|
50
|
+
"FetchStatusItem",
|
|
51
|
+
"FetchSubpagesRequest",
|
|
52
|
+
"FetchSubpagesResult",
|
|
53
|
+
"HealthResponse",
|
|
54
|
+
"MonitorsClient",
|
|
55
|
+
"SearchFetchRequest",
|
|
56
|
+
"SearchMode",
|
|
57
|
+
"SearchRequest",
|
|
58
|
+
"SearchResponse",
|
|
59
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Base async client classes for the RaySearch monitor-style endpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..api import AsyncRaySearch
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AsyncMonitorsBaseClient:
|
|
13
|
+
"""Base client for asynchronous monitor-style API operations."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, client: AsyncRaySearch) -> None:
|
|
16
|
+
self._client = client
|
|
17
|
+
|
|
18
|
+
async def request(
|
|
19
|
+
self,
|
|
20
|
+
endpoint: str,
|
|
21
|
+
method: str = "POST",
|
|
22
|
+
data: Mapping[str, Any] | str | None = None,
|
|
23
|
+
params: dict[str, str | int] | None = None,
|
|
24
|
+
) -> object:
|
|
25
|
+
return await self._client.async_request(
|
|
26
|
+
endpoint,
|
|
27
|
+
data=data,
|
|
28
|
+
method=method,
|
|
29
|
+
params=params,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__all__ = ["AsyncMonitorsBaseClient"]
|