kynth 0.1.0__py3-none-any.whl

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.
kynth/__init__.py ADDED
@@ -0,0 +1,199 @@
1
+ """Official Python SDK for Kynth Core — the AI back-end for your product.
2
+
3
+ from kynth import Kynth, KynthError
4
+
5
+ client = Kynth(api_key="ksk_live_...")
6
+ doc = client.parse(file_url="https://.../invoice.pdf")
7
+ print(doc["totalAmount"], doc["usage"]["balanceRemaining"])
8
+
9
+ Zero dependencies (stdlib only). Every method returns the endpoint result as a
10
+ dict including a ``usage`` envelope; failures raise a typed ``KynthError`` and
11
+ never burn credits. Get a key + 500 free credits at https://api.kynth.studio.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import urllib.error
18
+ import urllib.request
19
+ from typing import Any, Dict, List, Optional
20
+
21
+ __all__ = ["Kynth", "KynthError", "__version__"]
22
+ __version__ = "0.1.0"
23
+
24
+ _DEFAULT_BASE_URL = "https://api.kynth.studio"
25
+
26
+
27
+ class KynthError(Exception):
28
+ """Raised for any non-2xx API response. Mirrors ``{"error": {"code", "message"}}``."""
29
+
30
+ def __init__(self, code: str, message: str, status: int) -> None:
31
+ super().__init__(message)
32
+ self.code = code
33
+ self.message = message
34
+ self.status = status
35
+
36
+ def __repr__(self) -> str: # pragma: no cover - trivial
37
+ return f"KynthError(code={self.code!r}, status={self.status}, message={self.message!r})"
38
+
39
+
40
+ def _document(file_url: Optional[str], text: Optional[str], file: Optional[Dict[str, str]]) -> Dict[str, Any]:
41
+ if file_url:
42
+ return {"fileUrl": file_url}
43
+ if file:
44
+ return {"file": file}
45
+ if text:
46
+ return {"text": text}
47
+ raise ValueError("Provide one of: file_url, text, or file={'data','mimeType'}.")
48
+
49
+
50
+ class Kynth:
51
+ """Synchronous client for the Kynth Core API."""
52
+
53
+ def __init__(
54
+ self,
55
+ api_key: str,
56
+ base_url: str = _DEFAULT_BASE_URL,
57
+ timeout: float = 60.0,
58
+ ) -> None:
59
+ if not api_key:
60
+ raise ValueError(
61
+ "Kynth: `api_key` is required. Mint one at https://api.kynth.studio/dashboard."
62
+ )
63
+ self.api_key = api_key
64
+ self.base_url = base_url.rstrip("/")
65
+ self.timeout = timeout
66
+
67
+ # -- transport ---------------------------------------------------------
68
+ def _request(self, method: str, path: str, body: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
69
+ url = f"{self.base_url}{path}"
70
+ data = json.dumps(body).encode("utf-8") if body is not None else None
71
+ headers = {"Authorization": f"Bearer {self.api_key}"}
72
+ if data is not None:
73
+ headers["Content-Type"] = "application/json"
74
+ req = urllib.request.Request(url, data=data, headers=headers, method=method)
75
+ try:
76
+ with urllib.request.urlopen(req, timeout=self.timeout) as resp:
77
+ payload = resp.read().decode("utf-8")
78
+ return json.loads(payload) if payload else {}
79
+ except urllib.error.HTTPError as exc:
80
+ raw = exc.read().decode("utf-8", errors="replace")
81
+ code, message = "unknown", f"Request failed with status {exc.code}."
82
+ try:
83
+ err = json.loads(raw).get("error") or {}
84
+ code = err.get("code", code)
85
+ message = err.get("message", message)
86
+ except (ValueError, AttributeError):
87
+ pass
88
+ raise KynthError(code, message, exc.code) from None
89
+ except urllib.error.URLError as exc:
90
+ raise KynthError("unknown", str(exc.reason), 0) from None
91
+
92
+ def _post(self, path: str, body: Dict[str, Any]) -> Dict[str, Any]:
93
+ # Drop None values so optional params are omitted.
94
+ clean = {k: v for k, v in body.items() if v is not None}
95
+ return self._request("POST", path, clean)
96
+
97
+ # -- capabilities ------------------------------------------------------
98
+ def parse(
99
+ self,
100
+ *,
101
+ file_url: Optional[str] = None,
102
+ text: Optional[str] = None,
103
+ file: Optional[Dict[str, str]] = None,
104
+ doc_type: Optional[str] = None,
105
+ ) -> Dict[str, Any]:
106
+ """Parse a document (PDF/image/text) into structured, validated JSON."""
107
+ body = _document(file_url, text, file)
108
+ if doc_type:
109
+ body["docType"] = doc_type
110
+ return self._post("/v1/parse", body)
111
+
112
+ def extract(
113
+ self, *, text: str, fields: List[str], instructions: Optional[str] = None
114
+ ) -> Dict[str, Any]:
115
+ """Pull a caller-defined set of fields out of any text."""
116
+ return self._post("/v1/extract", {"text": text, "fields": fields, "instructions": instructions})
117
+
118
+ def classify(
119
+ self,
120
+ *,
121
+ text: str,
122
+ labels: List[str],
123
+ multi: Optional[bool] = None,
124
+ instructions: Optional[str] = None,
125
+ ) -> Dict[str, Any]:
126
+ """Classify text against your own taxonomy."""
127
+ return self._post(
128
+ "/v1/classify",
129
+ {"text": text, "labels": labels, "multi": multi, "instructions": instructions},
130
+ )
131
+
132
+ def summarize(
133
+ self,
134
+ *,
135
+ text: str,
136
+ length: Optional[str] = None,
137
+ action_items: Optional[bool] = None,
138
+ ) -> Dict[str, Any]:
139
+ """Summarize long text into a summary, key points, and action items."""
140
+ return self._post(
141
+ "/v1/summarize", {"text": text, "length": length, "actionItems": action_items}
142
+ )
143
+
144
+ def redact(
145
+ self,
146
+ *,
147
+ text: str,
148
+ types: Optional[List[str]] = None,
149
+ placeholder: Optional[str] = None,
150
+ ) -> Dict[str, Any]:
151
+ """Detect and strip PII/PHI from text."""
152
+ return self._post("/v1/redact", {"text": text, "types": types, "placeholder": placeholder})
153
+
154
+ def sentiment(self, *, text: str, aspects: Optional[List[str]] = None) -> Dict[str, Any]:
155
+ """Sentiment, aspects, and themes from feedback text."""
156
+ return self._post("/v1/sentiment", {"text": text, "aspects": aspects})
157
+
158
+ def contract(
159
+ self,
160
+ *,
161
+ file_url: Optional[str] = None,
162
+ text: Optional[str] = None,
163
+ file: Optional[Dict[str, str]] = None,
164
+ ) -> Dict[str, Any]:
165
+ """Analyze a contract into parties, terms, obligations, and risk flags."""
166
+ return self._post("/v1/contract", _document(file_url, text, file))
167
+
168
+ def chargeback(
169
+ self,
170
+ *,
171
+ reason: str,
172
+ transaction: Dict[str, Any],
173
+ evidence: Optional[List[str]] = None,
174
+ context: Optional[str] = None,
175
+ network: Optional[str] = None,
176
+ ) -> Dict[str, Any]:
177
+ """Turn dispute details into a chargeback representment packet."""
178
+ return self._post(
179
+ "/v1/chargeback",
180
+ {
181
+ "reason": reason,
182
+ "transaction": transaction,
183
+ "evidence": evidence,
184
+ "context": context,
185
+ "network": network,
186
+ },
187
+ )
188
+
189
+ def enrich(
190
+ self, *, domain: Optional[str] = None, email: Optional[str] = None
191
+ ) -> Dict[str, Any]:
192
+ """Enrich a domain or work email into a structured company profile."""
193
+ if not domain and not email:
194
+ raise ValueError("Provide one of: domain or email.")
195
+ return self._post("/v1/enrich", {"domain": domain, "email": email})
196
+
197
+ def account(self) -> Dict[str, Any]:
198
+ """Fetch the authenticated account's credit balance."""
199
+ return self._request("GET", "/v1/account")
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: kynth
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for Kynth Core — the AI back-end for your product.
5
+ Project-URL: Homepage, https://api.kynth.studio
6
+ Project-URL: Documentation, https://api.kynth.studio/docs
7
+ Project-URL: Source, https://github.com/kyisaiah47/kynth-os
8
+ Author: Kynth Studios
9
+ License: MIT
10
+ Keywords: ai,api,chargeback,contract,document-parsing,kynth,llm,ocr,pii
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+
19
+ # kynth
20
+
21
+ Official Python SDK for **[Kynth Core](https://api.kynth.studio)** — the AI back-end for your product. Parse documents, extract fields, redact PII, analyze contracts, fight chargebacks, and enrich companies.
22
+
23
+ ```bash
24
+ pip install kynth
25
+ ```
26
+
27
+ ## Quickstart
28
+
29
+ ```python
30
+ from kynth import Kynth
31
+
32
+ client = Kynth(api_key="ksk_live_...")
33
+
34
+ doc = client.parse(file_url="https://.../invoice.pdf")
35
+ print(doc["totalAmount"]) # 4820.5
36
+ print(doc["usage"]["balanceRemaining"]) # 490
37
+ ```
38
+
39
+ Get a key (and 500 free credits) at **[api.kynth.studio](https://api.kynth.studio)**. Zero dependencies — stdlib only, Python 3.8+.
40
+
41
+ ## Methods
42
+
43
+ Every method returns the endpoint result as a dict, including a `usage` envelope. A non-2xx response raises a typed `KynthError` (and never burns credits).
44
+
45
+ ```python
46
+ client.parse(file_url=...) # documents → JSON
47
+ client.extract(text=..., fields=["order", "total"]) # pull named fields
48
+ client.classify(text=..., labels=["billing", "tech"]) # label text
49
+ client.summarize(text=..., length="standard") # summary + actions
50
+ client.redact(text=...) # strip PII/PHI
51
+ client.sentiment(text=..., aspects=["product"]) # sentiment + aspects
52
+ client.contract(file_url=...) # contract → terms + risks
53
+ client.chargeback(reason=..., transaction={"amount": 129}) # representment packet
54
+ client.enrich(email="sam@stripe.com") # company profile
55
+ client.account() # balance
56
+ ```
57
+
58
+ ## Error handling
59
+
60
+ ```python
61
+ from kynth import Kynth, KynthError
62
+
63
+ client = Kynth(api_key="ksk_live_...")
64
+ try:
65
+ client.parse(file_url="https://.../invoice.pdf")
66
+ except KynthError as err:
67
+ # err.code: "insufficient_credits" | "rate_limited" | "unauthorized" | ...
68
+ print(err.code, err.status, err.message)
69
+ ```
70
+
71
+ MIT © Kynth Studios
@@ -0,0 +1,4 @@
1
+ kynth/__init__.py,sha256=fDTzqVrAgnyuopKB1yFdwOkxrsHC6ZuClMQX3GoGm3E,7349
2
+ kynth-0.1.0.dist-info/METADATA,sha256=38tfVajz_Uyp6ytdCLf9V-IgzFq0Y-zrpv1r5Y4KEqE,2674
3
+ kynth-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
4
+ kynth-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any