predev-api 0.11.0__tar.gz → 0.12.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: predev-api
3
- Version: 0.11.0
3
+ Version: 0.12.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 Architect API - Python Client
41
+ # pre.dev API Python Client
42
42
 
43
- A Python client library for the [Pre.dev Architect API](https://docs.pre.dev). Generate comprehensive software specifications using AI-powered analysis.
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
- - 🚀 **Fast Spec**: Generate comprehensive specifications quickly - perfect for MVPs and prototypes
48
- - 🔍 **Deep Spec**: Generate ultra-detailed specifications for complex systems with enterprise-grade depth
49
- - **Async Spec**: Non-blocking async methods for long-running requests
50
- - 📊 **Status Tracking**: Check the status of async specification generation requests
51
- - **Type Hints**: Full type annotations for better IDE support
52
- - 🛡️ **Error Handling**: Custom exceptions for different error scenarios
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 — scrape data, fill forms, navigate pages, extract structured data. Each task navigates a URL, optionally performs actions, and 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_tasks([
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_tasks([
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_tasks(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_tasks(tasks, run_async=True)
578
+ # {"id": "batch_abc", "status": "processing", "completed": 0, "total": 3}
579
+
580
+ while True:
581
+ state = client.get_browser_tasks(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:
604
+
605
+ ```python
606
+ import base64
607
+
608
+ details = client.get_browser_tasks(batch_id, include_events=True)
609
+ for result in details["results"]:
610
+ for ev in result.get("events", []):
611
+ if ev["type"] == "screenshot":
612
+ with open(f"screenshot_iter{ev.get('iteration')}.jpg", "wb") as f:
613
+ f.write(base64.b64decode(ev["data"]["base64"]))
614
+ elif ev["type"] == "plan":
615
+ print(f"Iter {ev.get('iteration')} plan: {ev['data'].get('notes')}")
616
+ ```
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_tasks(
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_tasks(tasks, concurrency=N)` | dict | Default — wait for completion |
635
+ | `browser_tasks(tasks, stream=True)` | iterator of SSE dicts | Live UI showing execution timeline |
636
+ | `browser_tasks(tasks, run_async=True)` | dict (empty results, returned immediately) | Long batches, background jobs |
637
+ | `get_browser_tasks(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 Architect API - Python Client
1
+ # pre.dev API Python Client
2
2
 
3
- A Python client library for the [Pre.dev Architect API](https://docs.pre.dev). Generate comprehensive software specifications using AI-powered analysis.
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
- - 🚀 **Fast Spec**: Generate comprehensive specifications quickly - perfect for MVPs and prototypes
8
- - 🔍 **Deep Spec**: Generate ultra-detailed specifications for complex systems with enterprise-grade depth
9
- - **Async Spec**: Non-blocking async methods for long-running requests
10
- - 📊 **Status Tracking**: Check the status of async specification generation requests
11
- - **Type Hints**: Full type annotations for better IDE support
12
- - 🛡️ **Error Handling**: Custom exceptions for different error scenarios
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 — scrape data, fill forms, navigate pages, extract structured data. Each task navigates a URL, optionally performs actions, and 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_tasks([
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_tasks([
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_tasks(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_tasks(tasks, run_async=True)
538
+ # {"id": "batch_abc", "status": "processing", "completed": 0, "total": 3}
539
+
540
+ while True:
541
+ state = client.get_browser_tasks(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:
564
+
565
+ ```python
566
+ import base64
567
+
568
+ details = client.get_browser_tasks(batch_id, include_events=True)
569
+ for result in details["results"]:
570
+ for ev in result.get("events", []):
571
+ if ev["type"] == "screenshot":
572
+ with open(f"screenshot_iter{ev.get('iteration')}.jpg", "wb") as f:
573
+ f.write(base64.b64decode(ev["data"]["base64"]))
574
+ elif ev["type"] == "plan":
575
+ print(f"Iter {ev.get('iteration')} plan: {ev['data'].get('notes')}")
576
+ ```
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_tasks(
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_tasks(tasks, concurrency=N)` | dict | Default — wait for completion |
595
+ | `browser_tasks(tasks, stream=True)` | iterator of SSE dicts | Live UI showing execution timeline |
596
+ | `browser_tasks(tasks, run_async=True)` | dict (empty results, returned immediately) | Long batches, background jobs |
597
+ | `get_browser_tasks(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
@@ -29,7 +29,7 @@ from .client import (
29
29
  )
30
30
  from .exceptions import PredevAPIError, AuthenticationError, RateLimitError
31
31
 
32
- __version__ = "0.11.0"
32
+ __version__ = "0.11.1"
33
33
  __all__ = [
34
34
  "PredevAPI",
35
35
  "PredevAPIError",
@@ -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
 
@@ -131,7 +132,7 @@ class SpecGraphNode:
131
132
  label: str = ""
132
133
  type: Optional[str] = None
133
134
  description: Optional[str] = None
134
- level: Optional[str] = None
135
+ level: Optional[int] = None
135
136
  hours: Optional[float] = None
136
137
 
137
138
 
@@ -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,135 @@ 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_tasks(
612
+ self,
613
+ batch_id: str,
614
+ include_events: bool = False
615
+ ) -> Dict[str, Any]:
616
+ """
617
+ Get the status + results of a browser tasks batch by ID.
618
+
619
+ Works for both in-progress and completed batches - use with 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_tasks
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}/api/v1/browser-tasks/{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 browser_tasks(
640
+ self,
641
+ tasks: List[Dict[str, Any]],
642
+ concurrency: Optional[int] = None,
643
+ stream: bool = False,
644
+ run_async: bool = False
645
+ ) -> Union[Dict[str, Any], Iterator[Dict[str, Any]]]:
646
+ """
647
+ Run browser automation tasks - scrape data, fill forms, navigate pages.
648
+
649
+ Always pass an array of tasks (even for a single task). Each task has a
650
+ url and optional instruction, input, and output schema.
651
+
652
+ Args:
653
+ tasks: Array of tasks. Each task has:
654
+ - url (str, required): URL to navigate to
655
+ - instruction (str, optional): What to do on the page
656
+ - input (dict, optional): Form values / input data
657
+ - output (dict, optional): JSON Schema for structured output
658
+ concurrency: Parallel browsers (default 5, max 20)
659
+ stream: If True, returns an iterator of SSE events instead of
660
+ waiting for all tasks to complete
661
+
662
+ Returns:
663
+ Without stream: Dict with id, total, completed, results, totalCost, totalCreditsUsed
664
+ With stream: Iterator yielding dicts with 'event' and 'data' keys:
665
+ - event='task_event': real-time events (navigation, screenshot, plan, action, etc.)
666
+ - event='task_result': a single task completed
667
+ - event='done': batch complete (data is the full result)
668
+ - event='error': fatal error
669
+
670
+ Example:
671
+ >>> # Standard mode
672
+ >>> result = client.browser_tasks([
673
+ ... {"url": "https://example.com", "output": {"type": "object", "properties": {"heading": {"type": "string"}}}}
674
+ ... ])
675
+ >>> print(result["results"][0]["data"])
676
+ >>>
677
+ >>> # Stream mode
678
+ >>> for msg in client.browser_tasks([...], stream=True):
679
+ ... if msg["event"] == "done":
680
+ ... print("Done:", msg["data"]["totalCreditsUsed"], "credits")
681
+ """
682
+ if stream:
683
+ return self._browser_tasks_stream(tasks, concurrency)
684
+
685
+ url = f"{self.base_url}/api/v1/browser-tasks"
686
+ payload: Dict[str, Any] = {"tasks": tasks}
687
+ if concurrency is not None:
688
+ payload["concurrency"] = concurrency
689
+ if run_async:
690
+ payload["async"] = True
691
+
692
+ try:
693
+ response = requests.post(
694
+ url,
695
+ headers=self.headers,
696
+ json=payload,
697
+ timeout=300
698
+ )
699
+ self._handle_response(response)
700
+ return response.json()
701
+ except requests.RequestException as e:
702
+ raise PredevAPIError(f"Request failed: {str(e)}") from e
703
+
704
+ def _browser_tasks_stream(
705
+ self,
706
+ tasks: List[Dict[str, Any]],
707
+ concurrency: Optional[int] = None
708
+ ) -> Iterator[Dict[str, Any]]:
709
+ """SSE streaming implementation for browser_tasks."""
710
+ url = f"{self.base_url}/api/v1/browser-tasks"
711
+ payload: Dict[str, Any] = {"tasks": tasks, "stream": True}
712
+ if concurrency is not None:
713
+ payload["concurrency"] = concurrency
714
+
715
+ try:
716
+ response = requests.post(
717
+ url,
718
+ headers=self.headers,
719
+ json=payload,
720
+ stream=True,
721
+ timeout=300
722
+ )
723
+ self._handle_response(response)
724
+
725
+ current_event = ""
726
+ for line in response.iter_lines(decode_unicode=True):
727
+ if not line:
728
+ continue
729
+ if line.startswith(":"):
730
+ continue
731
+ if line.startswith("event: "):
732
+ current_event = line[7:].strip()
733
+ elif line.startswith("data: ") and current_event:
734
+ data = json.loads(line[6:])
735
+ yield {"event": current_event, "data": data}
736
+ current_event = ""
737
+ except requests.RequestException as e:
738
+ raise PredevAPIError(f"Request failed: {str(e)}") from e
739
+
614
740
  def _make_request(
615
741
  self,
616
742
  endpoint: str,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: predev-api
3
- Version: 0.11.0
3
+ Version: 0.12.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 Architect API - Python Client
41
+ # pre.dev API Python Client
42
42
 
43
- A Python client library for the [Pre.dev Architect API](https://docs.pre.dev). Generate comprehensive software specifications using AI-powered analysis.
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
- - 🚀 **Fast Spec**: Generate comprehensive specifications quickly - perfect for MVPs and prototypes
48
- - 🔍 **Deep Spec**: Generate ultra-detailed specifications for complex systems with enterprise-grade depth
49
- - **Async Spec**: Non-blocking async methods for long-running requests
50
- - 📊 **Status Tracking**: Check the status of async specification generation requests
51
- - **Type Hints**: Full type annotations for better IDE support
52
- - 🛡️ **Error Handling**: Custom exceptions for different error scenarios
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 — scrape data, fill forms, navigate pages, extract structured data. Each task navigates a URL, optionally performs actions, and 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_tasks([
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_tasks([
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_tasks(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_tasks(tasks, run_async=True)
578
+ # {"id": "batch_abc", "status": "processing", "completed": 0, "total": 3}
579
+
580
+ while True:
581
+ state = client.get_browser_tasks(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:
604
+
605
+ ```python
606
+ import base64
607
+
608
+ details = client.get_browser_tasks(batch_id, include_events=True)
609
+ for result in details["results"]:
610
+ for ev in result.get("events", []):
611
+ if ev["type"] == "screenshot":
612
+ with open(f"screenshot_iter{ev.get('iteration')}.jpg", "wb") as f:
613
+ f.write(base64.b64decode(ev["data"]["base64"]))
614
+ elif ev["type"] == "plan":
615
+ print(f"Iter {ev.get('iteration')} plan: {ev['data'].get('notes')}")
616
+ ```
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_tasks(
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_tasks(tasks, concurrency=N)` | dict | Default — wait for completion |
635
+ | `browser_tasks(tasks, stream=True)` | iterator of SSE dicts | Live UI showing execution timeline |
636
+ | `browser_tasks(tasks, run_async=True)` | dict (empty results, returned immediately) | Long batches, background jobs |
637
+ | `get_browser_tasks(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.11.0",
12
+ version="0.12.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