webcrawlerapi 2.1.0__tar.gz → 2.1.2__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.
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/PKG-INFO +1 -1
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/setup.py +1 -1
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/webcrawlerapi/__init__.py +2 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/webcrawlerapi/client.py +126 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/webcrawlerapi/models.py +25 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/webcrawlerapi.egg-info/PKG-INFO +1 -1
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/README.md +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/pyproject.toml +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/setup.cfg +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/tests/__init__.py +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/tests/test_client.py +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/tests/test_models.py +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/webcrawlerapi.egg-info/SOURCES.txt +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/webcrawlerapi.egg-info/dependency_links.txt +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/webcrawlerapi.egg-info/requires.txt +0 -0
- {webcrawlerapi-2.1.0 → webcrawlerapi-2.1.2}/webcrawlerapi.egg-info/top_level.txt +0 -0
|
@@ -6,6 +6,7 @@ import requests
|
|
|
6
6
|
|
|
7
7
|
from .models import (
|
|
8
8
|
Action,
|
|
9
|
+
AgentRun,
|
|
9
10
|
CrawlResponse,
|
|
10
11
|
Job,
|
|
11
12
|
JobMarkdownResponse,
|
|
@@ -17,6 +18,7 @@ from .models import (
|
|
|
17
18
|
|
|
18
19
|
CRAWLER_VERSION = "v1"
|
|
19
20
|
SCRAPER_VERSION = "v2"
|
|
21
|
+
AGENT_TERMINAL_STATUSES = {"done", "error", "canceled"}
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
class WebCrawlerAPI:
|
|
@@ -355,6 +357,130 @@ class WebCrawlerAPI:
|
|
|
355
357
|
|
|
356
358
|
return self.get_job_markdown_content(job.id)
|
|
357
359
|
|
|
360
|
+
def run_agent_async(
|
|
361
|
+
self,
|
|
362
|
+
prompt: str,
|
|
363
|
+
max_spend_usd: float,
|
|
364
|
+
urls: Optional[List[str]] = None,
|
|
365
|
+
seed_urls_only: Optional[bool] = None,
|
|
366
|
+
output_schema: Optional[Dict[str, Any]] = None,
|
|
367
|
+
model: Optional[str] = None,
|
|
368
|
+
) -> AgentRun:
|
|
369
|
+
"""
|
|
370
|
+
Start a new agent run asynchronously.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
prompt (str): Natural-language task for the agent to complete
|
|
374
|
+
max_spend_usd (float): Maximum spend for this run in USD
|
|
375
|
+
urls (list, optional): Optional seed URLs the agent can use while running the task
|
|
376
|
+
seed_urls_only (bool, optional): If True, the agent only uses the provided seed URLs
|
|
377
|
+
output_schema (dict, optional): Optional JSON schema for structured agent output
|
|
378
|
+
model (str, optional): Model to use for the run. Omit to use the API default.
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
AgentRun: The created agent run
|
|
382
|
+
|
|
383
|
+
Raises:
|
|
384
|
+
WebCrawlerApiError: If the API returns an error response
|
|
385
|
+
requests.exceptions.RequestException: If the HTTP request fails
|
|
386
|
+
"""
|
|
387
|
+
payload: Dict[str, Any] = {"prompt": prompt, "max_spend_usd": max_spend_usd}
|
|
388
|
+
|
|
389
|
+
if urls is not None:
|
|
390
|
+
payload["urls"] = urls
|
|
391
|
+
if seed_urls_only is not None:
|
|
392
|
+
payload["seed_urls_only"] = seed_urls_only
|
|
393
|
+
if output_schema is not None:
|
|
394
|
+
payload["output_schema"] = output_schema
|
|
395
|
+
if model is not None:
|
|
396
|
+
payload["model"] = model
|
|
397
|
+
|
|
398
|
+
response = self.session.post(
|
|
399
|
+
urljoin(self.base_url, f"/{CRAWLER_VERSION}/agent"), json=payload
|
|
400
|
+
)
|
|
401
|
+
if not response.ok:
|
|
402
|
+
self._raise_for_error(response)
|
|
403
|
+
return AgentRun(response.json())
|
|
404
|
+
|
|
405
|
+
def get_agent_job(self, job_id: str) -> AgentRun:
|
|
406
|
+
"""
|
|
407
|
+
Get the status and result of a specific agent run.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
job_id (str): The unique identifier of the agent run
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
AgentRun: The agent run status and result
|
|
414
|
+
|
|
415
|
+
Raises:
|
|
416
|
+
WebCrawlerApiError: If the API returns an error response
|
|
417
|
+
requests.exceptions.RequestException: If the HTTP request fails
|
|
418
|
+
"""
|
|
419
|
+
response = self.session.get(
|
|
420
|
+
urljoin(self.base_url, f"/{CRAWLER_VERSION}/agent/job/{job_id}"),
|
|
421
|
+
headers={"Cache-Control": "no-cache, no-store, must-revalidate"},
|
|
422
|
+
)
|
|
423
|
+
if not response.ok:
|
|
424
|
+
self._raise_for_error(response)
|
|
425
|
+
return AgentRun(response.json())
|
|
426
|
+
|
|
427
|
+
def run_agent(
|
|
428
|
+
self,
|
|
429
|
+
prompt: str,
|
|
430
|
+
max_spend_usd: float,
|
|
431
|
+
urls: Optional[List[str]] = None,
|
|
432
|
+
seed_urls_only: Optional[bool] = None,
|
|
433
|
+
output_schema: Optional[Dict[str, Any]] = None,
|
|
434
|
+
model: Optional[str] = None,
|
|
435
|
+
max_polls: int = 100,
|
|
436
|
+
) -> AgentRun:
|
|
437
|
+
"""
|
|
438
|
+
Start a new agent run and wait for its completion.
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
prompt (str): Natural-language task for the agent to complete
|
|
442
|
+
max_spend_usd (float): Maximum spend for this run in USD
|
|
443
|
+
urls (list, optional): Optional seed URLs the agent can use while running the task
|
|
444
|
+
seed_urls_only (bool, optional): If True, the agent only uses the provided seed URLs
|
|
445
|
+
output_schema (dict, optional): Optional JSON schema for structured agent output
|
|
446
|
+
model (str, optional): Model to use for the run. Omit to use the API default.
|
|
447
|
+
max_polls (int): Maximum number of status checks before raising a timeout (default: 100)
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
AgentRun: The final agent run state after completion
|
|
451
|
+
|
|
452
|
+
Raises:
|
|
453
|
+
WebCrawlerApiError: If the API returns an error response or the run times out
|
|
454
|
+
requests.exceptions.RequestException: If any API request fails
|
|
455
|
+
"""
|
|
456
|
+
run = self.run_agent_async(
|
|
457
|
+
prompt=prompt,
|
|
458
|
+
max_spend_usd=max_spend_usd,
|
|
459
|
+
urls=urls,
|
|
460
|
+
seed_urls_only=seed_urls_only,
|
|
461
|
+
output_schema=output_schema,
|
|
462
|
+
model=model,
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
if not run.id:
|
|
466
|
+
raise WebCrawlerApiError(
|
|
467
|
+
"invalid_response", "Failed to fetch agent job status"
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
if run.status in AGENT_TERMINAL_STATUSES:
|
|
471
|
+
return run
|
|
472
|
+
|
|
473
|
+
for _ in range(max_polls):
|
|
474
|
+
time.sleep(2)
|
|
475
|
+
agent_job = self.get_agent_job(run.id)
|
|
476
|
+
if agent_job.status in AGENT_TERMINAL_STATUSES:
|
|
477
|
+
return agent_job
|
|
478
|
+
|
|
479
|
+
raise WebCrawlerApiError(
|
|
480
|
+
"timeout",
|
|
481
|
+
"Agent run took too long, please retry or increase the number of polling retries",
|
|
482
|
+
)
|
|
483
|
+
|
|
358
484
|
def scrape_async(
|
|
359
485
|
self,
|
|
360
486
|
url: str,
|
|
@@ -94,6 +94,31 @@ class ScrapeResponseError:
|
|
|
94
94
|
status: Optional[str] = None
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
class AgentRun:
|
|
98
|
+
"""Represents an agent run job."""
|
|
99
|
+
|
|
100
|
+
TERMINAL_STATUSES = {"done", "error", "canceled"}
|
|
101
|
+
|
|
102
|
+
def __init__(self, data: Dict[str, Any]):
|
|
103
|
+
self.id: str = data["id"]
|
|
104
|
+
self.status: str = data["status"]
|
|
105
|
+
self.prompt: str = data["prompt"]
|
|
106
|
+
self.model: str = data["model"]
|
|
107
|
+
self.data: Optional[Any] = data.get("data")
|
|
108
|
+
self.error_reason: Optional[str] = data.get("error_reason")
|
|
109
|
+
self.balance_used_usd: float = data.get("balance_used_usd", 0)
|
|
110
|
+
self.max_spend_usd: Optional[float] = data.get("max_spend_usd")
|
|
111
|
+
self.urls: Optional[List[str]] = data.get("urls")
|
|
112
|
+
self.created_at: datetime = parse_datetime(data["created_at"])
|
|
113
|
+
self.updated_at: datetime = parse_datetime(data["updated_at"])
|
|
114
|
+
self.success: bool = data.get("success", False)
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def is_terminal(self) -> bool:
|
|
118
|
+
"""Check if the agent run is in a terminal state (done, error, or canceled)."""
|
|
119
|
+
return self.status in self.TERMINAL_STATUSES
|
|
120
|
+
|
|
121
|
+
|
|
97
122
|
@dataclass
|
|
98
123
|
class Action:
|
|
99
124
|
"""Base class for actions that can be performed during crawling."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|