predev-api 0.11.1__tar.gz → 1.0.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.
- {predev_api-0.11.1/predev_api.egg-info → predev_api-1.0.0}/PKG-INFO +172 -13
- {predev_api-0.11.1 → predev_api-1.0.0}/README.md +171 -12
- {predev_api-0.11.1 → predev_api-1.0.0}/predev_api/__init__.py +1 -1
- {predev_api-0.11.1 → predev_api-1.0.0}/predev_api/client.py +181 -5
- {predev_api-0.11.1 → predev_api-1.0.0/predev_api.egg-info}/PKG-INFO +172 -13
- {predev_api-0.11.1 → predev_api-1.0.0}/setup.py +1 -1
- {predev_api-0.11.1 → predev_api-1.0.0}/LICENSE +0 -0
- {predev_api-0.11.1 → predev_api-1.0.0}/MANIFEST.in +0 -0
- {predev_api-0.11.1 → predev_api-1.0.0}/predev_api/exceptions.py +0 -0
- {predev_api-0.11.1 → predev_api-1.0.0}/predev_api.egg-info/SOURCES.txt +0 -0
- {predev_api-0.11.1 → predev_api-1.0.0}/predev_api.egg-info/dependency_links.txt +0 -0
- {predev_api-0.11.1 → predev_api-1.0.0}/predev_api.egg-info/requires.txt +0 -0
- {predev_api-0.11.1 → predev_api-1.0.0}/predev_api.egg-info/top_level.txt +0 -0
- {predev_api-0.11.1 → predev_api-1.0.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: predev-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Python client for the Pre.dev Architect API - Generate comprehensive software specifications
|
|
5
5
|
Home-page: https://github.com/predotdev/predev-api
|
|
6
6
|
Author: Pre.dev
|
|
@@ -38,18 +38,27 @@ Dynamic: requires-dist
|
|
|
38
38
|
Dynamic: requires-python
|
|
39
39
|
Dynamic: summary
|
|
40
40
|
|
|
41
|
-
# pre.dev
|
|
41
|
+
# pre.dev API — Python Client
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
Python client for the [Pre.dev API](https://docs.pre.dev) — AI-powered software specs + browser automation.
|
|
44
44
|
|
|
45
45
|
## Features
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
47
|
+
**Specs**
|
|
48
|
+
- 🚀 **Fast Spec**: Comprehensive specifications for MVPs and prototypes
|
|
49
|
+
- 🔍 **Deep Spec**: Ultra-detailed specifications for complex systems
|
|
50
|
+
- ⚡ **Async Spec**: Non-blocking async methods with status polling
|
|
51
|
+
- 📄 File upload support (PDFs, docs, images as reference context)
|
|
52
|
+
|
|
53
|
+
**Browser automation (NEW)**
|
|
54
|
+
- 🌐 **Browser Tasks**: Scrape, fill forms, navigate pages with structured JSON output
|
|
55
|
+
- 📡 **SSE streaming**: Watch execution live — screenshots, plans, actions
|
|
56
|
+
- ⏱ **Async mode**: Fire-and-forget, poll for progress
|
|
57
|
+
- 🔁 **Retrieval**: Get any past task with full timeline for audit/replay
|
|
58
|
+
|
|
59
|
+
**Quality of life**
|
|
60
|
+
- ✨ Full type hints
|
|
61
|
+
- 🛡 Custom exceptions for auth / rate limit / API errors
|
|
53
62
|
|
|
54
63
|
## Installation
|
|
55
64
|
|
|
@@ -357,10 +366,6 @@ class SpecResponse:
|
|
|
357
366
|
|
|
358
367
|
# Integration URLs (when completed)
|
|
359
368
|
predevUrl: Optional[str] = None # Link to pre.dev project
|
|
360
|
-
lovableUrl: Optional[str] = None # Link to generate with Lovable
|
|
361
|
-
cursorUrl: Optional[str] = None # Link to generate with Cursor
|
|
362
|
-
v0Url: Optional[str] = None # Link to generate with v0
|
|
363
|
-
boltUrl: Optional[str] = None # Link to generate with Bolt
|
|
364
369
|
|
|
365
370
|
# Documentation (when doc_urls provided)
|
|
366
371
|
zippedDocsUrls: Optional[List[ZippedDocsUrl]] = None
|
|
@@ -494,3 +499,157 @@ For more information about the Pre.dev Architect API, visit:
|
|
|
494
499
|
## Support
|
|
495
500
|
|
|
496
501
|
For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/predotdev/predev-api).
|
|
502
|
+
|
|
503
|
+
## Browser Tasks
|
|
504
|
+
|
|
505
|
+
Run browser automation — navigate, interact, and/or extract data from any web page. Each task navigates a URL, performs actions, and optionally returns typed JSON.
|
|
506
|
+
|
|
507
|
+
### Quick start
|
|
508
|
+
|
|
509
|
+
```python
|
|
510
|
+
from predev_api import PredevAPI
|
|
511
|
+
|
|
512
|
+
client = PredevAPI(api_key="your_api_key")
|
|
513
|
+
|
|
514
|
+
result = client.browser_agent([
|
|
515
|
+
{
|
|
516
|
+
"url": "https://news.ycombinator.com",
|
|
517
|
+
"instruction": "Extract the top 5 stories",
|
|
518
|
+
"output": {
|
|
519
|
+
"type": "object",
|
|
520
|
+
"properties": {
|
|
521
|
+
"stories": {
|
|
522
|
+
"type": "array",
|
|
523
|
+
"items": {
|
|
524
|
+
"type": "object",
|
|
525
|
+
"properties": {
|
|
526
|
+
"title": {"type": "string"},
|
|
527
|
+
"points": {"type": "number"},
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
}
|
|
534
|
+
])
|
|
535
|
+
|
|
536
|
+
for story in result["results"][0]["data"]["stories"]:
|
|
537
|
+
print(f"{story['title']} ({story['points']} pts)")
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Three execution modes
|
|
541
|
+
|
|
542
|
+
#### 1. Sync (default) — wait for completion
|
|
543
|
+
|
|
544
|
+
```python
|
|
545
|
+
result = client.browser_agent([
|
|
546
|
+
{"url": "https://example.com", "output": {"type": "object", "properties": {"heading": {"type": "string"}}}}
|
|
547
|
+
])
|
|
548
|
+
print(result["results"][0]["data"]) # {'heading': 'Example Domain'}
|
|
549
|
+
print(result["totalCreditsUsed"]) # 0.1
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
#### 2. Stream (`stream=True`) — live timeline via SSE
|
|
553
|
+
|
|
554
|
+
Yields events as the agent runs. Good for showing progress in a UI.
|
|
555
|
+
|
|
556
|
+
```python
|
|
557
|
+
for msg in client.browser_agent(tasks, stream=True):
|
|
558
|
+
e, d = msg["event"], msg["data"]
|
|
559
|
+
if e == "task_event":
|
|
560
|
+
# navigation | screenshot | plan | action | validation | done
|
|
561
|
+
print(f"[{d['type']}]", d.get("data"))
|
|
562
|
+
elif e == "task_result":
|
|
563
|
+
print(f"Task {d['taskIndex']} done:", d.get("data"))
|
|
564
|
+
elif e == "done":
|
|
565
|
+
print("Batch complete:", d["totalCreditsUsed"], "credits")
|
|
566
|
+
elif e == "error":
|
|
567
|
+
print("Batch error:", d)
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
#### 3. Async (`run_async=True`) — fire-and-forget, poll for progress
|
|
571
|
+
|
|
572
|
+
Returns the batch ID immediately. Use for long-running batches or background jobs.
|
|
573
|
+
|
|
574
|
+
```python
|
|
575
|
+
import time
|
|
576
|
+
|
|
577
|
+
r = client.browser_agent(tasks, run_async=True)
|
|
578
|
+
# {"id": "batch_abc", "status": "processing", "completed": 0, "total": 3}
|
|
579
|
+
|
|
580
|
+
while True:
|
|
581
|
+
state = client.get_browser_agent(r["id"])
|
|
582
|
+
print(f"{state['completed']}/{state['total']}")
|
|
583
|
+
for done in state["results"]:
|
|
584
|
+
print(f" ✓ {done['url']} -> {done.get('data')}")
|
|
585
|
+
if state["status"] == "completed":
|
|
586
|
+
break
|
|
587
|
+
time.sleep(1)
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Task shapes
|
|
591
|
+
|
|
592
|
+
Each task's behavior is determined by which fields are set:
|
|
593
|
+
|
|
594
|
+
| Fields | Shape | Example |
|
|
595
|
+
|---|---|---|
|
|
596
|
+
| `url` + `output` | **Scrape** | Extract structured data from a page |
|
|
597
|
+
| `url` + `instruction` | **Act** | Click, navigate, search |
|
|
598
|
+
| `url` + `instruction` + `input` | **Form fill** | Fill & submit a form |
|
|
599
|
+
| `url` + `instruction` + `output` | **Act + extract** | Navigate then extract data |
|
|
600
|
+
|
|
601
|
+
### Retrieving a batch with the full timeline
|
|
602
|
+
|
|
603
|
+
Every task records navigation, screenshots, LLM plans, actions, validations. Retrieve for audit, replay, or debugging. Screenshots are uploaded to a CDN during execution — retrieved events contain `data["url"]`, not base64.
|
|
604
|
+
|
|
605
|
+
```python
|
|
606
|
+
details = client.get_browser_agent(batch_id, include_events=True)
|
|
607
|
+
for result in details["results"]:
|
|
608
|
+
for ev in result.get("events", []):
|
|
609
|
+
if ev["type"] == "screenshot":
|
|
610
|
+
# ev["data"]["url"] is a permanent CDN URL. Use directly in an <img>.
|
|
611
|
+
print(f"Iter {ev.get('iteration')} screenshot:", ev["data"]["url"])
|
|
612
|
+
elif ev["type"] == "plan":
|
|
613
|
+
print(f"Iter {ev.get('iteration')} plan: {ev['data'].get('notes')}")
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
> **Note:** The live SSE stream (`stream=True`) still sends screenshots inline as base64 (`ev["data"]["base64"]`) so live UIs render instantly. The retrieval path (`get_browser_agent`) always returns CDN URLs.
|
|
617
|
+
|
|
618
|
+
### Parallel batch example
|
|
619
|
+
|
|
620
|
+
```python
|
|
621
|
+
# Scrape 100 URLs with 20 browsers in parallel
|
|
622
|
+
urls = [...100 urls...]
|
|
623
|
+
result = client.browser_agent(
|
|
624
|
+
[{"url": u, "output": {"type": "object", "properties": {"title": {"type": "string"}}}} for u in urls],
|
|
625
|
+
concurrency=20
|
|
626
|
+
)
|
|
627
|
+
print(f"{result['completed']}/{result['total']} done in {result['totalCreditsUsed']} credits")
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Browser task methods
|
|
631
|
+
|
|
632
|
+
| Method | Returns | Use when |
|
|
633
|
+
|---|---|---|
|
|
634
|
+
| `browser_agent(tasks, concurrency=N)` | dict | Default — wait for completion |
|
|
635
|
+
| `browser_agent(tasks, stream=True)` | iterator of SSE dicts | Live UI showing execution timeline |
|
|
636
|
+
| `browser_agent(tasks, run_async=True)` | dict (empty results, returned immediately) | Long batches, background jobs |
|
|
637
|
+
| `get_browser_agent(batch_id, include_events=False)` | dict | Poll progress or retrieve a completed batch |
|
|
638
|
+
|
|
639
|
+
### Task result statuses
|
|
640
|
+
|
|
641
|
+
| Status | Meaning |
|
|
642
|
+
|---|---|
|
|
643
|
+
| `SUCCESS` | Task completed, data extracted |
|
|
644
|
+
| `BLOCKED` | Page blocked automation (bot detection) |
|
|
645
|
+
| `TIMEOUT` | Task exceeded time limit |
|
|
646
|
+
| `LOOP` | Agent detected it was stuck in a loop |
|
|
647
|
+
| `ERROR` | Unexpected error |
|
|
648
|
+
| `NO_TARGET` | Could not find target elements |
|
|
649
|
+
| `CAPTCHA_FAILED` | CAPTCHA solve failed |
|
|
650
|
+
|
|
651
|
+
### Pricing
|
|
652
|
+
|
|
653
|
+
- Minimum: **0.1 credits per task** ($0.01)
|
|
654
|
+
- 10x margin on underlying LLM + sandbox compute
|
|
655
|
+
- 1 credit = $0.10
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
# pre.dev
|
|
1
|
+
# pre.dev API — Python Client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Python client for the [Pre.dev API](https://docs.pre.dev) — AI-powered software specs + browser automation.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
7
|
+
**Specs**
|
|
8
|
+
- 🚀 **Fast Spec**: Comprehensive specifications for MVPs and prototypes
|
|
9
|
+
- 🔍 **Deep Spec**: Ultra-detailed specifications for complex systems
|
|
10
|
+
- ⚡ **Async Spec**: Non-blocking async methods with status polling
|
|
11
|
+
- 📄 File upload support (PDFs, docs, images as reference context)
|
|
12
|
+
|
|
13
|
+
**Browser automation (NEW)**
|
|
14
|
+
- 🌐 **Browser Tasks**: Scrape, fill forms, navigate pages with structured JSON output
|
|
15
|
+
- 📡 **SSE streaming**: Watch execution live — screenshots, plans, actions
|
|
16
|
+
- ⏱ **Async mode**: Fire-and-forget, poll for progress
|
|
17
|
+
- 🔁 **Retrieval**: Get any past task with full timeline for audit/replay
|
|
18
|
+
|
|
19
|
+
**Quality of life**
|
|
20
|
+
- ✨ Full type hints
|
|
21
|
+
- 🛡 Custom exceptions for auth / rate limit / API errors
|
|
13
22
|
|
|
14
23
|
## Installation
|
|
15
24
|
|
|
@@ -317,10 +326,6 @@ class SpecResponse:
|
|
|
317
326
|
|
|
318
327
|
# Integration URLs (when completed)
|
|
319
328
|
predevUrl: Optional[str] = None # Link to pre.dev project
|
|
320
|
-
lovableUrl: Optional[str] = None # Link to generate with Lovable
|
|
321
|
-
cursorUrl: Optional[str] = None # Link to generate with Cursor
|
|
322
|
-
v0Url: Optional[str] = None # Link to generate with v0
|
|
323
|
-
boltUrl: Optional[str] = None # Link to generate with Bolt
|
|
324
329
|
|
|
325
330
|
# Documentation (when doc_urls provided)
|
|
326
331
|
zippedDocsUrls: Optional[List[ZippedDocsUrl]] = None
|
|
@@ -454,3 +459,157 @@ For more information about the Pre.dev Architect API, visit:
|
|
|
454
459
|
## Support
|
|
455
460
|
|
|
456
461
|
For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/predotdev/predev-api).
|
|
462
|
+
|
|
463
|
+
## Browser Tasks
|
|
464
|
+
|
|
465
|
+
Run browser automation — navigate, interact, and/or extract data from any web page. Each task navigates a URL, performs actions, and optionally returns typed JSON.
|
|
466
|
+
|
|
467
|
+
### Quick start
|
|
468
|
+
|
|
469
|
+
```python
|
|
470
|
+
from predev_api import PredevAPI
|
|
471
|
+
|
|
472
|
+
client = PredevAPI(api_key="your_api_key")
|
|
473
|
+
|
|
474
|
+
result = client.browser_agent([
|
|
475
|
+
{
|
|
476
|
+
"url": "https://news.ycombinator.com",
|
|
477
|
+
"instruction": "Extract the top 5 stories",
|
|
478
|
+
"output": {
|
|
479
|
+
"type": "object",
|
|
480
|
+
"properties": {
|
|
481
|
+
"stories": {
|
|
482
|
+
"type": "array",
|
|
483
|
+
"items": {
|
|
484
|
+
"type": "object",
|
|
485
|
+
"properties": {
|
|
486
|
+
"title": {"type": "string"},
|
|
487
|
+
"points": {"type": "number"},
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
}
|
|
494
|
+
])
|
|
495
|
+
|
|
496
|
+
for story in result["results"][0]["data"]["stories"]:
|
|
497
|
+
print(f"{story['title']} ({story['points']} pts)")
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Three execution modes
|
|
501
|
+
|
|
502
|
+
#### 1. Sync (default) — wait for completion
|
|
503
|
+
|
|
504
|
+
```python
|
|
505
|
+
result = client.browser_agent([
|
|
506
|
+
{"url": "https://example.com", "output": {"type": "object", "properties": {"heading": {"type": "string"}}}}
|
|
507
|
+
])
|
|
508
|
+
print(result["results"][0]["data"]) # {'heading': 'Example Domain'}
|
|
509
|
+
print(result["totalCreditsUsed"]) # 0.1
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
#### 2. Stream (`stream=True`) — live timeline via SSE
|
|
513
|
+
|
|
514
|
+
Yields events as the agent runs. Good for showing progress in a UI.
|
|
515
|
+
|
|
516
|
+
```python
|
|
517
|
+
for msg in client.browser_agent(tasks, stream=True):
|
|
518
|
+
e, d = msg["event"], msg["data"]
|
|
519
|
+
if e == "task_event":
|
|
520
|
+
# navigation | screenshot | plan | action | validation | done
|
|
521
|
+
print(f"[{d['type']}]", d.get("data"))
|
|
522
|
+
elif e == "task_result":
|
|
523
|
+
print(f"Task {d['taskIndex']} done:", d.get("data"))
|
|
524
|
+
elif e == "done":
|
|
525
|
+
print("Batch complete:", d["totalCreditsUsed"], "credits")
|
|
526
|
+
elif e == "error":
|
|
527
|
+
print("Batch error:", d)
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
#### 3. Async (`run_async=True`) — fire-and-forget, poll for progress
|
|
531
|
+
|
|
532
|
+
Returns the batch ID immediately. Use for long-running batches or background jobs.
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
import time
|
|
536
|
+
|
|
537
|
+
r = client.browser_agent(tasks, run_async=True)
|
|
538
|
+
# {"id": "batch_abc", "status": "processing", "completed": 0, "total": 3}
|
|
539
|
+
|
|
540
|
+
while True:
|
|
541
|
+
state = client.get_browser_agent(r["id"])
|
|
542
|
+
print(f"{state['completed']}/{state['total']}")
|
|
543
|
+
for done in state["results"]:
|
|
544
|
+
print(f" ✓ {done['url']} -> {done.get('data')}")
|
|
545
|
+
if state["status"] == "completed":
|
|
546
|
+
break
|
|
547
|
+
time.sleep(1)
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Task shapes
|
|
551
|
+
|
|
552
|
+
Each task's behavior is determined by which fields are set:
|
|
553
|
+
|
|
554
|
+
| Fields | Shape | Example |
|
|
555
|
+
|---|---|---|
|
|
556
|
+
| `url` + `output` | **Scrape** | Extract structured data from a page |
|
|
557
|
+
| `url` + `instruction` | **Act** | Click, navigate, search |
|
|
558
|
+
| `url` + `instruction` + `input` | **Form fill** | Fill & submit a form |
|
|
559
|
+
| `url` + `instruction` + `output` | **Act + extract** | Navigate then extract data |
|
|
560
|
+
|
|
561
|
+
### Retrieving a batch with the full timeline
|
|
562
|
+
|
|
563
|
+
Every task records navigation, screenshots, LLM plans, actions, validations. Retrieve for audit, replay, or debugging. Screenshots are uploaded to a CDN during execution — retrieved events contain `data["url"]`, not base64.
|
|
564
|
+
|
|
565
|
+
```python
|
|
566
|
+
details = client.get_browser_agent(batch_id, include_events=True)
|
|
567
|
+
for result in details["results"]:
|
|
568
|
+
for ev in result.get("events", []):
|
|
569
|
+
if ev["type"] == "screenshot":
|
|
570
|
+
# ev["data"]["url"] is a permanent CDN URL. Use directly in an <img>.
|
|
571
|
+
print(f"Iter {ev.get('iteration')} screenshot:", ev["data"]["url"])
|
|
572
|
+
elif ev["type"] == "plan":
|
|
573
|
+
print(f"Iter {ev.get('iteration')} plan: {ev['data'].get('notes')}")
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
> **Note:** The live SSE stream (`stream=True`) still sends screenshots inline as base64 (`ev["data"]["base64"]`) so live UIs render instantly. The retrieval path (`get_browser_agent`) always returns CDN URLs.
|
|
577
|
+
|
|
578
|
+
### Parallel batch example
|
|
579
|
+
|
|
580
|
+
```python
|
|
581
|
+
# Scrape 100 URLs with 20 browsers in parallel
|
|
582
|
+
urls = [...100 urls...]
|
|
583
|
+
result = client.browser_agent(
|
|
584
|
+
[{"url": u, "output": {"type": "object", "properties": {"title": {"type": "string"}}}} for u in urls],
|
|
585
|
+
concurrency=20
|
|
586
|
+
)
|
|
587
|
+
print(f"{result['completed']}/{result['total']} done in {result['totalCreditsUsed']} credits")
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Browser task methods
|
|
591
|
+
|
|
592
|
+
| Method | Returns | Use when |
|
|
593
|
+
|---|---|---|
|
|
594
|
+
| `browser_agent(tasks, concurrency=N)` | dict | Default — wait for completion |
|
|
595
|
+
| `browser_agent(tasks, stream=True)` | iterator of SSE dicts | Live UI showing execution timeline |
|
|
596
|
+
| `browser_agent(tasks, run_async=True)` | dict (empty results, returned immediately) | Long batches, background jobs |
|
|
597
|
+
| `get_browser_agent(batch_id, include_events=False)` | dict | Poll progress or retrieve a completed batch |
|
|
598
|
+
|
|
599
|
+
### Task result statuses
|
|
600
|
+
|
|
601
|
+
| Status | Meaning |
|
|
602
|
+
|---|---|
|
|
603
|
+
| `SUCCESS` | Task completed, data extracted |
|
|
604
|
+
| `BLOCKED` | Page blocked automation (bot detection) |
|
|
605
|
+
| `TIMEOUT` | Task exceeded time limit |
|
|
606
|
+
| `LOOP` | Agent detected it was stuck in a loop |
|
|
607
|
+
| `ERROR` | Unexpected error |
|
|
608
|
+
| `NO_TARGET` | Could not find target elements |
|
|
609
|
+
| `CAPTCHA_FAILED` | CAPTCHA solve failed |
|
|
610
|
+
|
|
611
|
+
### Pricing
|
|
612
|
+
|
|
613
|
+
- Minimum: **0.1 credits per task** ($0.01)
|
|
614
|
+
- 10x margin on underlying LLM + sandbox compute
|
|
615
|
+
- 1 credit = $0.10
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Client for the Pre.dev Architect API
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Optional, Dict, Any, Literal, List, Union, BinaryIO
|
|
5
|
+
from typing import Optional, Dict, Any, Literal, List, Union, BinaryIO, Iterator
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
+
import json
|
|
7
8
|
import requests
|
|
8
9
|
from .exceptions import PredevAPIError, AuthenticationError, RateLimitError
|
|
9
10
|
|
|
@@ -199,10 +200,6 @@ class SpecResponse:
|
|
|
199
200
|
architectureInfographicUrl: Optional[str] = None
|
|
200
201
|
|
|
201
202
|
predevUrl: Optional[str] = None
|
|
202
|
-
lovableUrl: Optional[str] = None
|
|
203
|
-
cursorUrl: Optional[str] = None
|
|
204
|
-
v0Url: Optional[str] = None
|
|
205
|
-
boltUrl: Optional[str] = None
|
|
206
203
|
|
|
207
204
|
zippedDocsUrls: Optional[List['ZippedDocsUrl']] = None
|
|
208
205
|
|
|
@@ -611,6 +608,185 @@ class PredevAPI:
|
|
|
611
608
|
except requests.RequestException as e:
|
|
612
609
|
raise PredevAPIError(f"Request failed: {str(e)}") from e
|
|
613
610
|
|
|
611
|
+
def get_browser_agent(
|
|
612
|
+
self,
|
|
613
|
+
batch_id: str,
|
|
614
|
+
include_events: bool = False
|
|
615
|
+
) -> Dict[str, Any]:
|
|
616
|
+
"""
|
|
617
|
+
Get the status + results of a browser-agent batch by ID.
|
|
618
|
+
|
|
619
|
+
Works for both in-progress and completed batches - use with run_async=True
|
|
620
|
+
submissions to poll for progress. Set include_events=True to get the
|
|
621
|
+
full timeline (screenshots, plans, actions, validations) for each task.
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
batch_id: The batch ID returned from browser_agent()
|
|
625
|
+
include_events: Include full event timeline (can be large due to screenshots)
|
|
626
|
+
|
|
627
|
+
Returns:
|
|
628
|
+
Dict with id, total, completed, results, totalCost, totalCreditsUsed, status
|
|
629
|
+
"""
|
|
630
|
+
qs = "?includeEvents=true" if include_events else ""
|
|
631
|
+
url = f"{self.base_url}/browser-agent/{batch_id}{qs}"
|
|
632
|
+
try:
|
|
633
|
+
response = requests.get(url, headers=self.headers, timeout=60)
|
|
634
|
+
self._handle_response(response)
|
|
635
|
+
return response.json()
|
|
636
|
+
except requests.RequestException as e:
|
|
637
|
+
raise PredevAPIError(f"Request failed: {str(e)}") from e
|
|
638
|
+
|
|
639
|
+
def list_browser_agents(
|
|
640
|
+
self,
|
|
641
|
+
limit: Optional[int] = None,
|
|
642
|
+
skip: Optional[int] = None,
|
|
643
|
+
status: Optional[str] = None
|
|
644
|
+
) -> Dict[str, Any]:
|
|
645
|
+
"""
|
|
646
|
+
List browser-agent runs for the authenticated caller. Sorted by most recent.
|
|
647
|
+
|
|
648
|
+
Args:
|
|
649
|
+
limit: Page size (1-100, default 20)
|
|
650
|
+
skip: Offset for pagination
|
|
651
|
+
status: Optional filter ('processing' | 'completed')
|
|
652
|
+
|
|
653
|
+
Returns:
|
|
654
|
+
Dict with keys: batches, total, hasMore
|
|
655
|
+
"""
|
|
656
|
+
params: Dict[str, Any] = {}
|
|
657
|
+
if limit is not None:
|
|
658
|
+
params["limit"] = limit
|
|
659
|
+
if skip is not None:
|
|
660
|
+
params["skip"] = skip
|
|
661
|
+
if status is not None:
|
|
662
|
+
params["status"] = status
|
|
663
|
+
url = f"{self.base_url}/list-browser-agents"
|
|
664
|
+
try:
|
|
665
|
+
response = requests.get(url, headers=self.headers, params=params, timeout=60)
|
|
666
|
+
self._handle_response(response)
|
|
667
|
+
return response.json()
|
|
668
|
+
except requests.RequestException as e:
|
|
669
|
+
raise PredevAPIError(f"Request failed: {str(e)}") from e
|
|
670
|
+
|
|
671
|
+
def browser_agent_status(self) -> Dict[str, Any]:
|
|
672
|
+
"""
|
|
673
|
+
Caller's live queue snapshot. Useful for watching in-flight work
|
|
674
|
+
against the per-user queue cap (1000 live tasks).
|
|
675
|
+
|
|
676
|
+
Returns:
|
|
677
|
+
Dict with keys: userId, running, claimed, pending, total, cap
|
|
678
|
+
"""
|
|
679
|
+
url = f"{self.base_url}/browser-agent-status"
|
|
680
|
+
try:
|
|
681
|
+
response = requests.get(url, headers=self.headers, timeout=30)
|
|
682
|
+
self._handle_response(response)
|
|
683
|
+
return response.json()
|
|
684
|
+
except requests.RequestException as e:
|
|
685
|
+
raise PredevAPIError(f"Request failed: {str(e)}") from e
|
|
686
|
+
|
|
687
|
+
def browser_agent(
|
|
688
|
+
self,
|
|
689
|
+
tasks: List[Dict[str, Any]],
|
|
690
|
+
concurrency: Optional[int] = None,
|
|
691
|
+
stream: bool = False,
|
|
692
|
+
run_async: bool = False
|
|
693
|
+
) -> Union[Dict[str, Any], Iterator[Dict[str, Any]]]:
|
|
694
|
+
"""
|
|
695
|
+
Run browser-agent automation tasks - scrape data, fill forms, navigate pages.
|
|
696
|
+
|
|
697
|
+
Always pass an array of tasks (even for a single task). Each task has a
|
|
698
|
+
url and optional instruction, input, and output schema.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
tasks: Array of tasks. Each task has:
|
|
702
|
+
- url (str, required): URL to navigate to
|
|
703
|
+
- instruction (str, optional): What to do on the page
|
|
704
|
+
- input (dict, optional): Form values / input data
|
|
705
|
+
- output (dict, optional): JSON Schema for structured output
|
|
706
|
+
concurrency: Parallel browsers (default 5, max 20)
|
|
707
|
+
stream: If True, returns an iterator of SSE events instead of
|
|
708
|
+
waiting for all tasks to complete
|
|
709
|
+
run_async: If True, returns immediately with the batch id; poll
|
|
710
|
+
get_browser_agent(id) for progress.
|
|
711
|
+
|
|
712
|
+
Returns:
|
|
713
|
+
Without stream: Dict with id, total, completed, results, totalCost, totalCreditsUsed
|
|
714
|
+
With stream: Iterator yielding dicts with 'event' and 'data' keys:
|
|
715
|
+
- event='task_event': real-time events (navigation, screenshot, plan, action, etc.)
|
|
716
|
+
- event='task_result': a single task completed
|
|
717
|
+
- event='done': batch complete (data is the full result)
|
|
718
|
+
- event='error': fatal error
|
|
719
|
+
|
|
720
|
+
Example:
|
|
721
|
+
>>> # Standard mode
|
|
722
|
+
>>> result = client.browser_agent([
|
|
723
|
+
... {"url": "https://example.com", "output": {"type": "object", "properties": {"heading": {"type": "string"}}}}
|
|
724
|
+
... ])
|
|
725
|
+
>>> print(result["results"][0]["data"])
|
|
726
|
+
>>>
|
|
727
|
+
>>> # Stream mode
|
|
728
|
+
>>> for msg in client.browser_agent([...], stream=True):
|
|
729
|
+
... if msg["event"] == "done":
|
|
730
|
+
... print("Done:", msg["data"]["totalCreditsUsed"], "credits")
|
|
731
|
+
"""
|
|
732
|
+
if stream:
|
|
733
|
+
return self._browser_agent_stream(tasks, concurrency)
|
|
734
|
+
|
|
735
|
+
url = f"{self.base_url}/browser-agent"
|
|
736
|
+
payload: Dict[str, Any] = {"tasks": tasks}
|
|
737
|
+
if concurrency is not None:
|
|
738
|
+
payload["concurrency"] = concurrency
|
|
739
|
+
if run_async:
|
|
740
|
+
payload["async"] = True
|
|
741
|
+
|
|
742
|
+
try:
|
|
743
|
+
response = requests.post(
|
|
744
|
+
url,
|
|
745
|
+
headers=self.headers,
|
|
746
|
+
json=payload,
|
|
747
|
+
timeout=300
|
|
748
|
+
)
|
|
749
|
+
self._handle_response(response)
|
|
750
|
+
return response.json()
|
|
751
|
+
except requests.RequestException as e:
|
|
752
|
+
raise PredevAPIError(f"Request failed: {str(e)}") from e
|
|
753
|
+
|
|
754
|
+
def _browser_agent_stream(
|
|
755
|
+
self,
|
|
756
|
+
tasks: List[Dict[str, Any]],
|
|
757
|
+
concurrency: Optional[int] = None
|
|
758
|
+
) -> Iterator[Dict[str, Any]]:
|
|
759
|
+
"""SSE streaming implementation for browser_agent."""
|
|
760
|
+
url = f"{self.base_url}/browser-agent"
|
|
761
|
+
payload: Dict[str, Any] = {"tasks": tasks, "stream": True}
|
|
762
|
+
if concurrency is not None:
|
|
763
|
+
payload["concurrency"] = concurrency
|
|
764
|
+
|
|
765
|
+
try:
|
|
766
|
+
response = requests.post(
|
|
767
|
+
url,
|
|
768
|
+
headers=self.headers,
|
|
769
|
+
json=payload,
|
|
770
|
+
stream=True,
|
|
771
|
+
timeout=300
|
|
772
|
+
)
|
|
773
|
+
self._handle_response(response)
|
|
774
|
+
|
|
775
|
+
current_event = ""
|
|
776
|
+
for line in response.iter_lines(decode_unicode=True):
|
|
777
|
+
if not line:
|
|
778
|
+
continue
|
|
779
|
+
if line.startswith(":"):
|
|
780
|
+
continue
|
|
781
|
+
if line.startswith("event: "):
|
|
782
|
+
current_event = line[7:].strip()
|
|
783
|
+
elif line.startswith("data: ") and current_event:
|
|
784
|
+
data = json.loads(line[6:])
|
|
785
|
+
yield {"event": current_event, "data": data}
|
|
786
|
+
current_event = ""
|
|
787
|
+
except requests.RequestException as e:
|
|
788
|
+
raise PredevAPIError(f"Request failed: {str(e)}") from e
|
|
789
|
+
|
|
614
790
|
def _make_request(
|
|
615
791
|
self,
|
|
616
792
|
endpoint: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: predev-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Python client for the Pre.dev Architect API - Generate comprehensive software specifications
|
|
5
5
|
Home-page: https://github.com/predotdev/predev-api
|
|
6
6
|
Author: Pre.dev
|
|
@@ -38,18 +38,27 @@ Dynamic: requires-dist
|
|
|
38
38
|
Dynamic: requires-python
|
|
39
39
|
Dynamic: summary
|
|
40
40
|
|
|
41
|
-
# pre.dev
|
|
41
|
+
# pre.dev API — Python Client
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
Python client for the [Pre.dev API](https://docs.pre.dev) — AI-powered software specs + browser automation.
|
|
44
44
|
|
|
45
45
|
## Features
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
47
|
+
**Specs**
|
|
48
|
+
- 🚀 **Fast Spec**: Comprehensive specifications for MVPs and prototypes
|
|
49
|
+
- 🔍 **Deep Spec**: Ultra-detailed specifications for complex systems
|
|
50
|
+
- ⚡ **Async Spec**: Non-blocking async methods with status polling
|
|
51
|
+
- 📄 File upload support (PDFs, docs, images as reference context)
|
|
52
|
+
|
|
53
|
+
**Browser automation (NEW)**
|
|
54
|
+
- 🌐 **Browser Tasks**: Scrape, fill forms, navigate pages with structured JSON output
|
|
55
|
+
- 📡 **SSE streaming**: Watch execution live — screenshots, plans, actions
|
|
56
|
+
- ⏱ **Async mode**: Fire-and-forget, poll for progress
|
|
57
|
+
- 🔁 **Retrieval**: Get any past task with full timeline for audit/replay
|
|
58
|
+
|
|
59
|
+
**Quality of life**
|
|
60
|
+
- ✨ Full type hints
|
|
61
|
+
- 🛡 Custom exceptions for auth / rate limit / API errors
|
|
53
62
|
|
|
54
63
|
## Installation
|
|
55
64
|
|
|
@@ -357,10 +366,6 @@ class SpecResponse:
|
|
|
357
366
|
|
|
358
367
|
# Integration URLs (when completed)
|
|
359
368
|
predevUrl: Optional[str] = None # Link to pre.dev project
|
|
360
|
-
lovableUrl: Optional[str] = None # Link to generate with Lovable
|
|
361
|
-
cursorUrl: Optional[str] = None # Link to generate with Cursor
|
|
362
|
-
v0Url: Optional[str] = None # Link to generate with v0
|
|
363
|
-
boltUrl: Optional[str] = None # Link to generate with Bolt
|
|
364
369
|
|
|
365
370
|
# Documentation (when doc_urls provided)
|
|
366
371
|
zippedDocsUrls: Optional[List[ZippedDocsUrl]] = None
|
|
@@ -494,3 +499,157 @@ For more information about the Pre.dev Architect API, visit:
|
|
|
494
499
|
## Support
|
|
495
500
|
|
|
496
501
|
For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/predotdev/predev-api).
|
|
502
|
+
|
|
503
|
+
## Browser Tasks
|
|
504
|
+
|
|
505
|
+
Run browser automation — navigate, interact, and/or extract data from any web page. Each task navigates a URL, performs actions, and optionally returns typed JSON.
|
|
506
|
+
|
|
507
|
+
### Quick start
|
|
508
|
+
|
|
509
|
+
```python
|
|
510
|
+
from predev_api import PredevAPI
|
|
511
|
+
|
|
512
|
+
client = PredevAPI(api_key="your_api_key")
|
|
513
|
+
|
|
514
|
+
result = client.browser_agent([
|
|
515
|
+
{
|
|
516
|
+
"url": "https://news.ycombinator.com",
|
|
517
|
+
"instruction": "Extract the top 5 stories",
|
|
518
|
+
"output": {
|
|
519
|
+
"type": "object",
|
|
520
|
+
"properties": {
|
|
521
|
+
"stories": {
|
|
522
|
+
"type": "array",
|
|
523
|
+
"items": {
|
|
524
|
+
"type": "object",
|
|
525
|
+
"properties": {
|
|
526
|
+
"title": {"type": "string"},
|
|
527
|
+
"points": {"type": "number"},
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
}
|
|
534
|
+
])
|
|
535
|
+
|
|
536
|
+
for story in result["results"][0]["data"]["stories"]:
|
|
537
|
+
print(f"{story['title']} ({story['points']} pts)")
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Three execution modes
|
|
541
|
+
|
|
542
|
+
#### 1. Sync (default) — wait for completion
|
|
543
|
+
|
|
544
|
+
```python
|
|
545
|
+
result = client.browser_agent([
|
|
546
|
+
{"url": "https://example.com", "output": {"type": "object", "properties": {"heading": {"type": "string"}}}}
|
|
547
|
+
])
|
|
548
|
+
print(result["results"][0]["data"]) # {'heading': 'Example Domain'}
|
|
549
|
+
print(result["totalCreditsUsed"]) # 0.1
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
#### 2. Stream (`stream=True`) — live timeline via SSE
|
|
553
|
+
|
|
554
|
+
Yields events as the agent runs. Good for showing progress in a UI.
|
|
555
|
+
|
|
556
|
+
```python
|
|
557
|
+
for msg in client.browser_agent(tasks, stream=True):
|
|
558
|
+
e, d = msg["event"], msg["data"]
|
|
559
|
+
if e == "task_event":
|
|
560
|
+
# navigation | screenshot | plan | action | validation | done
|
|
561
|
+
print(f"[{d['type']}]", d.get("data"))
|
|
562
|
+
elif e == "task_result":
|
|
563
|
+
print(f"Task {d['taskIndex']} done:", d.get("data"))
|
|
564
|
+
elif e == "done":
|
|
565
|
+
print("Batch complete:", d["totalCreditsUsed"], "credits")
|
|
566
|
+
elif e == "error":
|
|
567
|
+
print("Batch error:", d)
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
#### 3. Async (`run_async=True`) — fire-and-forget, poll for progress
|
|
571
|
+
|
|
572
|
+
Returns the batch ID immediately. Use for long-running batches or background jobs.
|
|
573
|
+
|
|
574
|
+
```python
|
|
575
|
+
import time
|
|
576
|
+
|
|
577
|
+
r = client.browser_agent(tasks, run_async=True)
|
|
578
|
+
# {"id": "batch_abc", "status": "processing", "completed": 0, "total": 3}
|
|
579
|
+
|
|
580
|
+
while True:
|
|
581
|
+
state = client.get_browser_agent(r["id"])
|
|
582
|
+
print(f"{state['completed']}/{state['total']}")
|
|
583
|
+
for done in state["results"]:
|
|
584
|
+
print(f" ✓ {done['url']} -> {done.get('data')}")
|
|
585
|
+
if state["status"] == "completed":
|
|
586
|
+
break
|
|
587
|
+
time.sleep(1)
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Task shapes
|
|
591
|
+
|
|
592
|
+
Each task's behavior is determined by which fields are set:
|
|
593
|
+
|
|
594
|
+
| Fields | Shape | Example |
|
|
595
|
+
|---|---|---|
|
|
596
|
+
| `url` + `output` | **Scrape** | Extract structured data from a page |
|
|
597
|
+
| `url` + `instruction` | **Act** | Click, navigate, search |
|
|
598
|
+
| `url` + `instruction` + `input` | **Form fill** | Fill & submit a form |
|
|
599
|
+
| `url` + `instruction` + `output` | **Act + extract** | Navigate then extract data |
|
|
600
|
+
|
|
601
|
+
### Retrieving a batch with the full timeline
|
|
602
|
+
|
|
603
|
+
Every task records navigation, screenshots, LLM plans, actions, validations. Retrieve for audit, replay, or debugging. Screenshots are uploaded to a CDN during execution — retrieved events contain `data["url"]`, not base64.
|
|
604
|
+
|
|
605
|
+
```python
|
|
606
|
+
details = client.get_browser_agent(batch_id, include_events=True)
|
|
607
|
+
for result in details["results"]:
|
|
608
|
+
for ev in result.get("events", []):
|
|
609
|
+
if ev["type"] == "screenshot":
|
|
610
|
+
# ev["data"]["url"] is a permanent CDN URL. Use directly in an <img>.
|
|
611
|
+
print(f"Iter {ev.get('iteration')} screenshot:", ev["data"]["url"])
|
|
612
|
+
elif ev["type"] == "plan":
|
|
613
|
+
print(f"Iter {ev.get('iteration')} plan: {ev['data'].get('notes')}")
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
> **Note:** The live SSE stream (`stream=True`) still sends screenshots inline as base64 (`ev["data"]["base64"]`) so live UIs render instantly. The retrieval path (`get_browser_agent`) always returns CDN URLs.
|
|
617
|
+
|
|
618
|
+
### Parallel batch example
|
|
619
|
+
|
|
620
|
+
```python
|
|
621
|
+
# Scrape 100 URLs with 20 browsers in parallel
|
|
622
|
+
urls = [...100 urls...]
|
|
623
|
+
result = client.browser_agent(
|
|
624
|
+
[{"url": u, "output": {"type": "object", "properties": {"title": {"type": "string"}}}} for u in urls],
|
|
625
|
+
concurrency=20
|
|
626
|
+
)
|
|
627
|
+
print(f"{result['completed']}/{result['total']} done in {result['totalCreditsUsed']} credits")
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Browser task methods
|
|
631
|
+
|
|
632
|
+
| Method | Returns | Use when |
|
|
633
|
+
|---|---|---|
|
|
634
|
+
| `browser_agent(tasks, concurrency=N)` | dict | Default — wait for completion |
|
|
635
|
+
| `browser_agent(tasks, stream=True)` | iterator of SSE dicts | Live UI showing execution timeline |
|
|
636
|
+
| `browser_agent(tasks, run_async=True)` | dict (empty results, returned immediately) | Long batches, background jobs |
|
|
637
|
+
| `get_browser_agent(batch_id, include_events=False)` | dict | Poll progress or retrieve a completed batch |
|
|
638
|
+
|
|
639
|
+
### Task result statuses
|
|
640
|
+
|
|
641
|
+
| Status | Meaning |
|
|
642
|
+
|---|---|
|
|
643
|
+
| `SUCCESS` | Task completed, data extracted |
|
|
644
|
+
| `BLOCKED` | Page blocked automation (bot detection) |
|
|
645
|
+
| `TIMEOUT` | Task exceeded time limit |
|
|
646
|
+
| `LOOP` | Agent detected it was stuck in a loop |
|
|
647
|
+
| `ERROR` | Unexpected error |
|
|
648
|
+
| `NO_TARGET` | Could not find target elements |
|
|
649
|
+
| `CAPTCHA_FAILED` | CAPTCHA solve failed |
|
|
650
|
+
|
|
651
|
+
### Pricing
|
|
652
|
+
|
|
653
|
+
- Minimum: **0.1 credits per task** ($0.01)
|
|
654
|
+
- 10x margin on underlying LLM + sandbox compute
|
|
655
|
+
- 1 credit = $0.10
|
|
@@ -9,7 +9,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name="predev-api",
|
|
12
|
-
version="0.
|
|
12
|
+
version="1.0.0",
|
|
13
13
|
author="Pre.dev",
|
|
14
14
|
author_email="support@pre.dev",
|
|
15
15
|
description="Python client for the Pre.dev Architect API - Generate comprehensive software specifications",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|