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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: predev-api
3
- Version: 0.11.1
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 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 — 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 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 — 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
@@ -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__ = "1.0.0"
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
 
@@ -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.11.1
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 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 — 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.11.1",
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