paperlypdf 0.1.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.
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: paperlypdf
3
+ Version: 0.1.0
4
+ Summary: Python client for Paperly — turn prompts or CSV/Excel files into polished, downloadable PDF reports.
5
+ License: MIT
6
+ Project-URL: Homepage, https://saas-pdf-kappa.vercel.app
7
+ Keywords: pdf,report,ai,generator,document,paperly
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Topic :: Office/Business
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: requests>=2.25
14
+
15
+ # Paperly Python SDK
16
+
17
+ Turn a prompt — or a CSV/Excel file — into a polished, downloadable **PDF report**
18
+ in a few lines of Python. The AI builds a designed, multi-page document
19
+ (table of contents, tables, summary section) and renders it to PDF — no manual
20
+ formatting.
21
+
22
+ ```bash
23
+ pip install paperlypdf
24
+ ```
25
+
26
+ ## Quickstart
27
+
28
+ ```python
29
+ from paperlypdf import PaperlyPdf
30
+
31
+ client = PaperlyPdf(api_key="pdf_YOUR_API_KEY")
32
+
33
+ pdf = client.generate_to_pdf(
34
+ "Write a quarterly sales analysis for Q2 2026.",
35
+ length="standard", # concise | standard | in-depth
36
+ output_path="report.pdf",
37
+ )
38
+ ```
39
+
40
+ That's it — submit, poll, download. `report.pdf` lands on disk.
41
+
42
+ ## Data file → PDF report (the killer use case)
43
+
44
+ ```python
45
+ import csv
46
+ import io
47
+ from paperlypdf import PaperlyPdf
48
+
49
+ client = PaperlyPdf(api_key="pdf_YOUR_API_KEY")
50
+
51
+ def csv_as_text(path):
52
+ with open(path, encoding="utf-8-sig") as f:
53
+ rows = list(csv.DictReader(f))
54
+ out = io.StringIO()
55
+ out.write(f"Source: {path} ({len(rows)} rows)\n\n")
56
+ for row in rows:
57
+ out.write(" | ".join(f"{k}: {v}" for k, v in row.items()))
58
+ out.write("\n")
59
+ return out.getvalue()
60
+
61
+ client.generate_to_pdf(
62
+ "Organize this data into a clean business report with tables, "
63
+ "then add an analysis section with concrete recommendations.",
64
+ file_text=csv_as_text("sales.csv"),
65
+ length="standard",
66
+ output_path="sales_report.pdf",
67
+ )
68
+ ```
69
+
70
+ For Excel (`.xlsx`) use pandas to read and pass a text representation the same way:
71
+
72
+ ```python
73
+ import pandas as pd
74
+ client.generate_to_pdf("Summarize this into a clean report.", file_text=pd.read_excel("orders.xlsx").to_string())
75
+ ```
76
+
77
+ ## Low-level API
78
+
79
+ | Method | Purpose |
80
+ |---|---|
81
+ | `generate(prompt, length, mode, file_text, clarify, answers)` | Submit a job — returns `jobId` |
82
+ | `get_job(job_id)` | Poll status: `pending` / `running` / `done` / `failed` |
83
+ | `wait(job_id)` | Block until `done` (raises on `failed` or timeout) |
84
+ | `download(job_id, output_path)` | Fetch the finished PDF (bytes) |
85
+ | `generate_to_pdf(...)` | All of the above in one call |
86
+ | `me()` | API-key info: balance, usage, caps |
87
+
88
+ ### Handling clarifying questions
89
+
90
+ Pass `clarify=True` and the API may ask questions first:
91
+
92
+ ```python
93
+ res = client.generate("I need a document about ice cream.", clarify=True)
94
+ if res.get("needsInput"):
95
+ print(res["questions"]) # -> ask the user, then retry with answers:
96
+ res = client.generate("I need a document about ice cream.",
97
+ clarify=True,
98
+ answers=["Tutorial", "Beginner home cooks", "3 sections"])
99
+ job = client.wait(res["jobId"])
100
+ client.download(job["jobId"], "out.pdf")
101
+ ```
102
+
103
+ ## Errors
104
+
105
+ Non-2xx responses raise `PaperlyError` with the server's error message. Failed
106
+ jobs raise `PaperlyError` too. Jobs expire after 30 minutes.
107
+
108
+ ## Pricing
109
+
110
+ Prepaid, pay-as-you-go: `concise` $0.30 · `standard` $0.30 · `in-depth` $1.00,
111
+ plus $0.10 per 100K characters of file data. No subscription.
@@ -0,0 +1,97 @@
1
+ # Paperly Python SDK
2
+
3
+ Turn a prompt — or a CSV/Excel file — into a polished, downloadable **PDF report**
4
+ in a few lines of Python. The AI builds a designed, multi-page document
5
+ (table of contents, tables, summary section) and renders it to PDF — no manual
6
+ formatting.
7
+
8
+ ```bash
9
+ pip install paperlypdf
10
+ ```
11
+
12
+ ## Quickstart
13
+
14
+ ```python
15
+ from paperlypdf import PaperlyPdf
16
+
17
+ client = PaperlyPdf(api_key="pdf_YOUR_API_KEY")
18
+
19
+ pdf = client.generate_to_pdf(
20
+ "Write a quarterly sales analysis for Q2 2026.",
21
+ length="standard", # concise | standard | in-depth
22
+ output_path="report.pdf",
23
+ )
24
+ ```
25
+
26
+ That's it — submit, poll, download. `report.pdf` lands on disk.
27
+
28
+ ## Data file → PDF report (the killer use case)
29
+
30
+ ```python
31
+ import csv
32
+ import io
33
+ from paperlypdf import PaperlyPdf
34
+
35
+ client = PaperlyPdf(api_key="pdf_YOUR_API_KEY")
36
+
37
+ def csv_as_text(path):
38
+ with open(path, encoding="utf-8-sig") as f:
39
+ rows = list(csv.DictReader(f))
40
+ out = io.StringIO()
41
+ out.write(f"Source: {path} ({len(rows)} rows)\n\n")
42
+ for row in rows:
43
+ out.write(" | ".join(f"{k}: {v}" for k, v in row.items()))
44
+ out.write("\n")
45
+ return out.getvalue()
46
+
47
+ client.generate_to_pdf(
48
+ "Organize this data into a clean business report with tables, "
49
+ "then add an analysis section with concrete recommendations.",
50
+ file_text=csv_as_text("sales.csv"),
51
+ length="standard",
52
+ output_path="sales_report.pdf",
53
+ )
54
+ ```
55
+
56
+ For Excel (`.xlsx`) use pandas to read and pass a text representation the same way:
57
+
58
+ ```python
59
+ import pandas as pd
60
+ client.generate_to_pdf("Summarize this into a clean report.", file_text=pd.read_excel("orders.xlsx").to_string())
61
+ ```
62
+
63
+ ## Low-level API
64
+
65
+ | Method | Purpose |
66
+ |---|---|
67
+ | `generate(prompt, length, mode, file_text, clarify, answers)` | Submit a job — returns `jobId` |
68
+ | `get_job(job_id)` | Poll status: `pending` / `running` / `done` / `failed` |
69
+ | `wait(job_id)` | Block until `done` (raises on `failed` or timeout) |
70
+ | `download(job_id, output_path)` | Fetch the finished PDF (bytes) |
71
+ | `generate_to_pdf(...)` | All of the above in one call |
72
+ | `me()` | API-key info: balance, usage, caps |
73
+
74
+ ### Handling clarifying questions
75
+
76
+ Pass `clarify=True` and the API may ask questions first:
77
+
78
+ ```python
79
+ res = client.generate("I need a document about ice cream.", clarify=True)
80
+ if res.get("needsInput"):
81
+ print(res["questions"]) # -> ask the user, then retry with answers:
82
+ res = client.generate("I need a document about ice cream.",
83
+ clarify=True,
84
+ answers=["Tutorial", "Beginner home cooks", "3 sections"])
85
+ job = client.wait(res["jobId"])
86
+ client.download(job["jobId"], "out.pdf")
87
+ ```
88
+
89
+ ## Errors
90
+
91
+ Non-2xx responses raise `PaperlyError` with the server's error message. Failed
92
+ jobs raise `PaperlyError` too. Jobs expire after 30 minutes.
93
+
94
+ ## Pricing
95
+
96
+ Prepaid, pay-as-you-go: `concise` $0.30 · `standard` $0.30 · `in-depth` $1.00,
97
+ plus $0.10 per 100K characters of file data. No subscription.
@@ -0,0 +1,6 @@
1
+ """Paperly PDF SDK — turn prompts or data files into polished PDF documents."""
2
+
3
+ from .client import PaperlyPdf, PaperlyError
4
+
5
+ __version__ = "0.1.0"
6
+ __all__ = ["PaperlyPdf", "PaperlyError"]
@@ -0,0 +1,157 @@
1
+ """Minimal Python client for the Paperly PDF API.
2
+
3
+ Turn a prompt — or a CSV/Excel file — into a polished, downloadable PDF report.
4
+
5
+ from paperlypdf import PaperlyPdf
6
+
7
+ client = PaperlyPdf(api_key="pdf_...")
8
+ client.generate_to_pdf(
9
+ "Write a quarterly sales analysis for Q2 2026.",
10
+ length="standard",
11
+ output_path="report.pdf",
12
+ )
13
+
14
+ The API is a simple submit → poll → download flow, so nothing fancy is needed
15
+ here: this module just wraps the four endpoints with retries-free polling and
16
+ clean error messages.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import time
21
+ from typing import List, Optional, Union
22
+
23
+ import requests
24
+
25
+ __all__ = ["PaperlyPdf", "PaperlyError"]
26
+
27
+
28
+ class PaperlyError(Exception):
29
+ """Raised when the Paperly PDF API returns an error or a job fails."""
30
+
31
+
32
+ class PaperlyPdf:
33
+ """Async job-based client. Submitting returns a jobId immediately; you
34
+ poll with ``wait``/``get_job`` and fetch the finished PDF with ``download``."""
35
+
36
+ def __init__(
37
+ self,
38
+ api_key: str,
39
+ base_url: str = "https://saas-pdf-kappa.vercel.app",
40
+ timeout: int = 60,
41
+ ) -> None:
42
+ self.base_url = base_url.rstrip("/")
43
+ self.timeout = timeout
44
+ self._session = requests.Session()
45
+ self._session.headers["Authorization"] = f"Bearer {api_key}"
46
+ self._session.headers["Accept"] = "application/json"
47
+
48
+ # ── low-level ────────────────────────────────────────────────────────────
49
+ def _request(self, method: str, path: str, **kwargs) -> requests.Response:
50
+ url = f"{self.base_url}{path}"
51
+ resp = self._session.request(method, url, timeout=self.timeout, **kwargs)
52
+ if resp.status_code >= 400:
53
+ try:
54
+ msg = resp.json().get("error", resp.text)
55
+ except Exception: # noqa: BLE001 — non-JSON error body
56
+ msg = resp.text
57
+ raise PaperlyError(f"HTTP {resp.status_code}: {msg}")
58
+ return resp
59
+
60
+ # ── endpoints ────────────────────────────────────────────────────────────
61
+ def me(self) -> dict:
62
+ """Account / API-key info: balance, usage, caps."""
63
+ return self._request("GET", "/api/v1/me").json()
64
+
65
+ def generate(
66
+ self,
67
+ prompt: str,
68
+ length: str = "standard",
69
+ mode: str = "business",
70
+ file_text: Optional[str] = None,
71
+ clarify: bool = False,
72
+ answers: Optional[List[str]] = None,
73
+ ) -> dict:
74
+ """Submit a generation job.
75
+
76
+ Returns ``{"jobId": ...}`` when the job starts. If ``clarify=True`` and
77
+ the prompt is judged vague, returns ``{"needsInput": True, "questions": [...]}``
78
+ instead — pass the answered values back via ``answers`` on a retry.
79
+ """
80
+ body: dict = {"prompt": prompt, "length": length, "mode": mode, "clarify": bool(clarify)}
81
+ if file_text:
82
+ body["fileText"] = file_text
83
+ if answers:
84
+ body["answers"] = list(answers)
85
+ return self._request("POST", "/api/v1/generate", json=body).json()
86
+
87
+ def get_job(self, job_id: str) -> dict:
88
+ """Current job state: status (pending/running/done/failed) + step."""
89
+ return self._request("GET", f"/api/v1/jobs/{job_id}").json()
90
+
91
+ def wait(self, job_id: str, poll_interval: float = 3, timeout: float = 900) -> dict:
92
+ """Poll until the job reaches a terminal state.
93
+
94
+ Jobs expire after 30 minutes; ``timeout`` defaults to 15 min and can be
95
+ raised for very long in-depth generations.
96
+ """
97
+ deadline = time.time() + timeout
98
+ while True:
99
+ job = self.get_job(job_id)
100
+ status = job.get("status")
101
+ if status == "done":
102
+ return job
103
+ if status == "failed":
104
+ raise PaperlyError(job.get("error") or f"Job {job_id} failed.")
105
+ if time.time() >= deadline:
106
+ raise PaperlyError(f"Timed out after {timeout}s waiting for job {job_id}.")
107
+ time.sleep(poll_interval)
108
+
109
+ def download(self, job_id: str, output_path: Optional[str] = None) -> bytes:
110
+ """Fetch the finished PDF. Returns raw bytes; writes to ``output_path`` when given."""
111
+ resp = self._request("GET", f"/api/v1/jobs/{job_id}/download")
112
+ content = resp.content
113
+ if output_path:
114
+ with open(output_path, "wb") as f:
115
+ f.write(content)
116
+ return content
117
+
118
+ # ── convenience ──────────────────────────────────────────────────────────
119
+ def generate_to_pdf(
120
+ self,
121
+ prompt: str,
122
+ output_path: Optional[str] = None,
123
+ length: str = "standard",
124
+ mode: str = "business",
125
+ file_text: Optional[str] = None,
126
+ clarify: bool = False,
127
+ answers: Optional[List[str]] = None,
128
+ poll_interval: float = 3,
129
+ timeout: float = 900,
130
+ ) -> bytes:
131
+ """Submit, poll to completion, and download — one call end to end."""
132
+ data = self.generate(
133
+ prompt=prompt,
134
+ length=length,
135
+ mode=mode,
136
+ file_text=file_text,
137
+ clarify=clarify,
138
+ answers=answers,
139
+ )
140
+ if data.get("needsInput"):
141
+ raise PaperlyError(
142
+ f"Clarification needed — call generate(clarify=True) with answers for: "
143
+ f"{data.get('questions')}"
144
+ )
145
+ job = self.wait(data["jobId"], poll_interval=poll_interval, timeout=timeout)
146
+ return self.download(job["jobId"], output_path)
147
+
148
+ def generate_from_text_file(
149
+ self,
150
+ file_path: str,
151
+ prompt: str = "Organize this file's content into a clean, structured document.",
152
+ **kwargs,
153
+ ) -> bytes:
154
+ """Read a plain-text (or CSV) file locally and generate a PDF from it."""
155
+ with open(file_path, encoding="utf-8", errors="replace") as f:
156
+ text = f.read()
157
+ return self.generate_to_pdf(prompt=prompt, file_text=text, **kwargs)
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: paperlypdf
3
+ Version: 0.1.0
4
+ Summary: Python client for Paperly — turn prompts or CSV/Excel files into polished, downloadable PDF reports.
5
+ License: MIT
6
+ Project-URL: Homepage, https://saas-pdf-kappa.vercel.app
7
+ Keywords: pdf,report,ai,generator,document,paperly
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Topic :: Office/Business
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: requests>=2.25
14
+
15
+ # Paperly Python SDK
16
+
17
+ Turn a prompt — or a CSV/Excel file — into a polished, downloadable **PDF report**
18
+ in a few lines of Python. The AI builds a designed, multi-page document
19
+ (table of contents, tables, summary section) and renders it to PDF — no manual
20
+ formatting.
21
+
22
+ ```bash
23
+ pip install paperlypdf
24
+ ```
25
+
26
+ ## Quickstart
27
+
28
+ ```python
29
+ from paperlypdf import PaperlyPdf
30
+
31
+ client = PaperlyPdf(api_key="pdf_YOUR_API_KEY")
32
+
33
+ pdf = client.generate_to_pdf(
34
+ "Write a quarterly sales analysis for Q2 2026.",
35
+ length="standard", # concise | standard | in-depth
36
+ output_path="report.pdf",
37
+ )
38
+ ```
39
+
40
+ That's it — submit, poll, download. `report.pdf` lands on disk.
41
+
42
+ ## Data file → PDF report (the killer use case)
43
+
44
+ ```python
45
+ import csv
46
+ import io
47
+ from paperlypdf import PaperlyPdf
48
+
49
+ client = PaperlyPdf(api_key="pdf_YOUR_API_KEY")
50
+
51
+ def csv_as_text(path):
52
+ with open(path, encoding="utf-8-sig") as f:
53
+ rows = list(csv.DictReader(f))
54
+ out = io.StringIO()
55
+ out.write(f"Source: {path} ({len(rows)} rows)\n\n")
56
+ for row in rows:
57
+ out.write(" | ".join(f"{k}: {v}" for k, v in row.items()))
58
+ out.write("\n")
59
+ return out.getvalue()
60
+
61
+ client.generate_to_pdf(
62
+ "Organize this data into a clean business report with tables, "
63
+ "then add an analysis section with concrete recommendations.",
64
+ file_text=csv_as_text("sales.csv"),
65
+ length="standard",
66
+ output_path="sales_report.pdf",
67
+ )
68
+ ```
69
+
70
+ For Excel (`.xlsx`) use pandas to read and pass a text representation the same way:
71
+
72
+ ```python
73
+ import pandas as pd
74
+ client.generate_to_pdf("Summarize this into a clean report.", file_text=pd.read_excel("orders.xlsx").to_string())
75
+ ```
76
+
77
+ ## Low-level API
78
+
79
+ | Method | Purpose |
80
+ |---|---|
81
+ | `generate(prompt, length, mode, file_text, clarify, answers)` | Submit a job — returns `jobId` |
82
+ | `get_job(job_id)` | Poll status: `pending` / `running` / `done` / `failed` |
83
+ | `wait(job_id)` | Block until `done` (raises on `failed` or timeout) |
84
+ | `download(job_id, output_path)` | Fetch the finished PDF (bytes) |
85
+ | `generate_to_pdf(...)` | All of the above in one call |
86
+ | `me()` | API-key info: balance, usage, caps |
87
+
88
+ ### Handling clarifying questions
89
+
90
+ Pass `clarify=True` and the API may ask questions first:
91
+
92
+ ```python
93
+ res = client.generate("I need a document about ice cream.", clarify=True)
94
+ if res.get("needsInput"):
95
+ print(res["questions"]) # -> ask the user, then retry with answers:
96
+ res = client.generate("I need a document about ice cream.",
97
+ clarify=True,
98
+ answers=["Tutorial", "Beginner home cooks", "3 sections"])
99
+ job = client.wait(res["jobId"])
100
+ client.download(job["jobId"], "out.pdf")
101
+ ```
102
+
103
+ ## Errors
104
+
105
+ Non-2xx responses raise `PaperlyError` with the server's error message. Failed
106
+ jobs raise `PaperlyError` too. Jobs expire after 30 minutes.
107
+
108
+ ## Pricing
109
+
110
+ Prepaid, pay-as-you-go: `concise` $0.30 · `standard` $0.30 · `in-depth` $1.00,
111
+ plus $0.10 per 100K characters of file data. No subscription.
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ paperlypdf/__init__.py
4
+ paperlypdf/client.py
5
+ paperlypdf.egg-info/PKG-INFO
6
+ paperlypdf.egg-info/SOURCES.txt
7
+ paperlypdf.egg-info/dependency_links.txt
8
+ paperlypdf.egg-info/requires.txt
9
+ paperlypdf.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.25
@@ -0,0 +1 @@
1
+ paperlypdf
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "paperlypdf"
7
+ version = "0.1.0"
8
+ description = "Python client for Paperly — turn prompts or CSV/Excel files into polished, downloadable PDF reports."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ keywords = ["pdf", "report", "ai", "generator", "document", "paperly"]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Topic :: Office/Business",
17
+ ]
18
+ dependencies = ["requests>=2.25"]
19
+
20
+ [project.urls]
21
+ Homepage = "https://saas-pdf-kappa.vercel.app"
22
+
23
+ [tool.setuptools.packages.find]
24
+ include = ["paperlypdf*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+