lawsaathi 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 LawSaathi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include LICENSE
2
+ include README.md
3
+ include pyproject.toml
4
+ recursive-include lawsaathi *.py
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.4
2
+ Name: lawsaathi
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for LawSaathi Legal AI API — DELTA
5
+ Home-page: https://github.com/lawsaathi/lawsaathi-python-sdk
6
+ Author: LawSaathi
7
+ Author-email: LawSaathi <support@lawsaathi.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://lawsaathi.com
10
+ Project-URL: Documentation, https://lawsaathi.com/docs
11
+ Project-URL: Repository, https://github.com/lawsaathi/lawsaathi-python-sdk
12
+ Project-URL: Dashboard, https://lawsaathi.com/developer
13
+ Project-URL: Bug Tracker, https://github.com/lawsaathi/lawsaathi-python-sdk/issues
14
+ Keywords: lawsaathi,legal-ai,indian-law,ipc,crpc,delta,ai-sdk,legal-tech,chatbot,api-client
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.8
22
+ Classifier: Programming Language :: Python :: 3.9
23
+ Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Operating System :: OS Independent
28
+ Requires-Python: >=3.8
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: requests>=2.25.0
32
+ Provides-Extra: websocket
33
+ Requires-Dist: websocket-client>=1.0.0; extra == "websocket"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=6.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=2.0; extra == "dev"
37
+ Requires-Dist: black>=21.0; extra == "dev"
38
+ Requires-Dist: flake8>=3.9; extra == "dev"
39
+ Requires-Dist: build; extra == "dev"
40
+ Requires-Dist: twine; extra == "dev"
41
+ Dynamic: author
42
+ Dynamic: home-page
43
+ Dynamic: license-file
44
+ Dynamic: requires-python
45
+
46
+ # LawSaathi Python SDK
47
+
48
+ [![PyPI version](https://img.shields.io/pypi/v/lawsaathi.svg)](https://pypi.org/project/lawsaathi/)
49
+ [![Python versions](https://img.shields.io/pypi/pyversions/lawsaathi.svg)](https://pypi.org/project/lawsaathi/)
50
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
51
+
52
+ Official Python client for the **LawSaathi Legal AI API** — powered by **DELTA**, India's most advanced Legal AI.
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install lawsaathi
58
+
59
+ # With WebSocket streaming support:
60
+ pip install lawsaathi[websocket]
61
+ ```
62
+
63
+ ## Quick Start
64
+
65
+ ```python
66
+ from lawsaathi import LawSaathi
67
+
68
+ client = LawSaathi(api_key="ls_live_your_api_key_here")
69
+
70
+ # Ask any legal question
71
+ response = client.chat("What is IPC Section 302?")
72
+ print(response['response'])
73
+ print(f"Tokens used: {response['usage']['total_tokens']}")
74
+ print(f"Cost: ₹{response['usage']['cost_rupees']}")
75
+ ```
76
+
77
+ ## File Attachments (OpenAI-style)
78
+
79
+ Attach files to your queries — just pass file paths. The SDK handles uploading automatically.
80
+
81
+ ```python
82
+ # Analyze a contract — just pass the file path!
83
+ response = client.chat(
84
+ "Summarize the key clauses in this contract",
85
+ files=["contract.pdf"]
86
+ )
87
+ print(response['response'])
88
+
89
+ # Multiple files work too
90
+ response = client.chat(
91
+ "Compare these two agreements",
92
+ files=["agreement_v1.pdf", "agreement_v2.pdf"]
93
+ )
94
+ ```
95
+
96
+ ## Streaming (WebSocket)
97
+
98
+ ```python
99
+ # Requires: pip install lawsaathi[websocket]
100
+ for chunk in client.stream("Explain bail provisions under CrPC"):
101
+ print(chunk, end="", flush=True)
102
+
103
+ # Streaming with files
104
+ for chunk in client.stream("Analyze this document", files=["contract.pdf"]):
105
+ print(chunk, end="", flush=True)
106
+ ```
107
+
108
+ ## Check Balance
109
+
110
+ ```python
111
+ balance = client.get_balance()
112
+ print(f"Balance: ₹{balance['balance_rupees']}")
113
+ print(f"Tokens available: {balance['tokens_available']:,}")
114
+ ```
115
+
116
+ ## Error Handling
117
+
118
+ ```python
119
+ from lawsaathi import (
120
+ LawSaathi,
121
+ AuthenticationError,
122
+ RateLimitError,
123
+ InsufficientCreditsError,
124
+ APIError,
125
+ )
126
+
127
+ client = LawSaathi(api_key="ls_live_your_key")
128
+
129
+ try:
130
+ response = client.chat("Hello")
131
+ except AuthenticationError:
132
+ print("Invalid API key — check or regenerate it")
133
+ except InsufficientCreditsError as e:
134
+ print(f"Not enough credits. Balance: ₹{e.balance_rupees}")
135
+ except RateLimitError as e:
136
+ print(f"Rate limited. Retry after: {e.retry_after}s")
137
+ except APIError as e:
138
+ print(f"API error ({e.status_code}): {e}")
139
+ ```
140
+
141
+ ## Pricing
142
+
143
+ | | |
144
+ |---|---|
145
+ | **Cost per 1M tokens** | ₹998 |
146
+ | **Free credits on signup** | ₹100 |
147
+ | **Minimum topup** | ₹999 |
148
+ | **Credit expiry** | 150 days |
149
+
150
+ ## Rate Limits
151
+
152
+ | Limit | Default |
153
+ |-------|---------|
154
+ | Requests/minute | 5 |
155
+ | Requests/day | 2,500 |
156
+ | Max file size | 100 MB |
157
+
158
+ Need higher limits? Contact support@lawsaathi.com.
159
+
160
+ ## Requirements
161
+
162
+ - Python 3.8+
163
+ - `requests` library (installed automatically)
164
+ - `websocket-client` (optional, for streaming)
165
+
166
+ ## Links
167
+
168
+ - **Dashboard**: [lawsaathi.com/developer](https://lawsaathi.com/developer)
169
+ - **API Docs**: [lawsaathi.com/docs](https://lawsaathi.com/docs)
170
+ - **Support**: support@lawsaathi.com
171
+
172
+ ## License
173
+
174
+ MIT License — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,129 @@
1
+ # LawSaathi Python SDK
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/lawsaathi.svg)](https://pypi.org/project/lawsaathi/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/lawsaathi.svg)](https://pypi.org/project/lawsaathi/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ Official Python client for the **LawSaathi Legal AI API** — powered by **DELTA**, India's most advanced Legal AI.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install lawsaathi
13
+
14
+ # With WebSocket streaming support:
15
+ pip install lawsaathi[websocket]
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```python
21
+ from lawsaathi import LawSaathi
22
+
23
+ client = LawSaathi(api_key="ls_live_your_api_key_here")
24
+
25
+ # Ask any legal question
26
+ response = client.chat("What is IPC Section 302?")
27
+ print(response['response'])
28
+ print(f"Tokens used: {response['usage']['total_tokens']}")
29
+ print(f"Cost: ₹{response['usage']['cost_rupees']}")
30
+ ```
31
+
32
+ ## File Attachments (OpenAI-style)
33
+
34
+ Attach files to your queries — just pass file paths. The SDK handles uploading automatically.
35
+
36
+ ```python
37
+ # Analyze a contract — just pass the file path!
38
+ response = client.chat(
39
+ "Summarize the key clauses in this contract",
40
+ files=["contract.pdf"]
41
+ )
42
+ print(response['response'])
43
+
44
+ # Multiple files work too
45
+ response = client.chat(
46
+ "Compare these two agreements",
47
+ files=["agreement_v1.pdf", "agreement_v2.pdf"]
48
+ )
49
+ ```
50
+
51
+ ## Streaming (WebSocket)
52
+
53
+ ```python
54
+ # Requires: pip install lawsaathi[websocket]
55
+ for chunk in client.stream("Explain bail provisions under CrPC"):
56
+ print(chunk, end="", flush=True)
57
+
58
+ # Streaming with files
59
+ for chunk in client.stream("Analyze this document", files=["contract.pdf"]):
60
+ print(chunk, end="", flush=True)
61
+ ```
62
+
63
+ ## Check Balance
64
+
65
+ ```python
66
+ balance = client.get_balance()
67
+ print(f"Balance: ₹{balance['balance_rupees']}")
68
+ print(f"Tokens available: {balance['tokens_available']:,}")
69
+ ```
70
+
71
+ ## Error Handling
72
+
73
+ ```python
74
+ from lawsaathi import (
75
+ LawSaathi,
76
+ AuthenticationError,
77
+ RateLimitError,
78
+ InsufficientCreditsError,
79
+ APIError,
80
+ )
81
+
82
+ client = LawSaathi(api_key="ls_live_your_key")
83
+
84
+ try:
85
+ response = client.chat("Hello")
86
+ except AuthenticationError:
87
+ print("Invalid API key — check or regenerate it")
88
+ except InsufficientCreditsError as e:
89
+ print(f"Not enough credits. Balance: ₹{e.balance_rupees}")
90
+ except RateLimitError as e:
91
+ print(f"Rate limited. Retry after: {e.retry_after}s")
92
+ except APIError as e:
93
+ print(f"API error ({e.status_code}): {e}")
94
+ ```
95
+
96
+ ## Pricing
97
+
98
+ | | |
99
+ |---|---|
100
+ | **Cost per 1M tokens** | ₹998 |
101
+ | **Free credits on signup** | ₹100 |
102
+ | **Minimum topup** | ₹999 |
103
+ | **Credit expiry** | 150 days |
104
+
105
+ ## Rate Limits
106
+
107
+ | Limit | Default |
108
+ |-------|---------|
109
+ | Requests/minute | 5 |
110
+ | Requests/day | 2,500 |
111
+ | Max file size | 100 MB |
112
+
113
+ Need higher limits? Contact support@lawsaathi.com.
114
+
115
+ ## Requirements
116
+
117
+ - Python 3.8+
118
+ - `requests` library (installed automatically)
119
+ - `websocket-client` (optional, for streaming)
120
+
121
+ ## Links
122
+
123
+ - **Dashboard**: [lawsaathi.com/developer](https://lawsaathi.com/developer)
124
+ - **API Docs**: [lawsaathi.com/docs](https://lawsaathi.com/docs)
125
+ - **Support**: support@lawsaathi.com
126
+
127
+ ## License
128
+
129
+ MIT License — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,40 @@
1
+ # LawSaathi Python SDK
2
+ """
3
+ Official Python SDK for LawSaathi Legal AI API.
4
+
5
+ Quick Start:
6
+ from lawsaathi import LawSaathi
7
+
8
+ client = LawSaathi(api_key="ls_live_xxxxx")
9
+
10
+ # Non-streaming (HTTP)
11
+ response = client.chat("What is IPC Section 302?")
12
+ print(response['response'])
13
+
14
+ # Streaming (WebSocket)
15
+ for chunk in client.stream("Explain bail under CrPC"):
16
+ print(chunk, end="", flush=True)
17
+
18
+ # Check balance
19
+ balance = client.get_balance()
20
+ print(f"₹{balance['balance_rupees']}")
21
+ """
22
+
23
+ from .client import LawSaathi
24
+ from .exceptions import (
25
+ LawSaathiError,
26
+ AuthenticationError,
27
+ RateLimitError,
28
+ InsufficientCreditsError,
29
+ APIError,
30
+ )
31
+
32
+ __version__ = "1.0.0"
33
+ __all__ = [
34
+ "LawSaathi",
35
+ "LawSaathiError",
36
+ "AuthenticationError",
37
+ "RateLimitError",
38
+ "InsufficientCreditsError",
39
+ "APIError",
40
+ ]
@@ -0,0 +1,390 @@
1
+ """
2
+ LawSaathi Python SDK — Main Client
3
+ ====================================
4
+ Supports both non-streaming (HTTP) and streaming (WebSocket) modes.
5
+
6
+ Usage:
7
+ from lawsaathi import LawSaathi
8
+
9
+ client = LawSaathi(api_key="ls_live_xxxxx")
10
+
11
+ # Non-streaming (HTTP POST)
12
+ response = client.chat("What is IPC Section 302?")
13
+ print(response['response'])
14
+
15
+ # Chat with file attachments (just pass file paths — like OpenAI)
16
+ response = client.chat(
17
+ "Summarize this contract",
18
+ files=["contract.pdf", "annexure.pdf"]
19
+ )
20
+ print(response['response'])
21
+
22
+ # Streaming (WebSocket)
23
+ for chunk in client.stream("Explain bail under CrPC"):
24
+ print(chunk, end="", flush=True)
25
+
26
+ # Check balance
27
+ print(client.get_balance())
28
+ """
29
+
30
+ import json
31
+ import time
32
+ import threading
33
+ import logging
34
+ from typing import Optional, List, Dict, Any, Generator
35
+ from urllib.parse import urljoin
36
+
37
+ import requests
38
+
39
+ try:
40
+ import websocket as ws_lib # websocket-client
41
+ HAS_WEBSOCKET = True
42
+ except ImportError:
43
+ HAS_WEBSOCKET = False
44
+
45
+ from .exceptions import (
46
+ LawSaathiError,
47
+ AuthenticationError,
48
+ RateLimitError,
49
+ InsufficientCreditsError,
50
+ APIError,
51
+ )
52
+
53
+ __version__ = "1.0.0"
54
+ logger = logging.getLogger("lawsaathi")
55
+
56
+
57
+ class LawSaathi:
58
+ """
59
+ Official LawSaathi Python SDK client.
60
+
61
+ Args:
62
+ api_key: Your API key (starts with 'ls_live_').
63
+ base_url: Base HTTP URL (default: https://api.lawsaathi.com).
64
+ ws_url: Base WebSocket URL (default: wss://api.lawsaathi.com).
65
+ timeout: HTTP request timeout in seconds (default: 120).
66
+ max_retries: Max retries on transient failures (default: 2).
67
+ """
68
+
69
+ def __init__(
70
+ self,
71
+ api_key: str,
72
+ base_url: str = "https://api.lawsaathi.com",
73
+ ws_url: str = "wss://api.lawsaathi.com",
74
+ timeout: int = 120,
75
+ max_retries: int = 2,
76
+ ):
77
+ if not api_key or not api_key.startswith("ls_live_"):
78
+ raise ValueError("Invalid API key. Must start with 'ls_live_'.")
79
+
80
+ self.api_key = api_key
81
+ self.base_url = base_url.rstrip("/")
82
+ self.ws_url = ws_url.rstrip("/")
83
+ self.timeout = timeout
84
+ self.max_retries = max_retries
85
+
86
+ self._session = requests.Session()
87
+ self._session.headers.update({
88
+ "X-API-Key": api_key,
89
+ "Content-Type": "application/json",
90
+ "User-Agent": f"LawSaathi-Python-SDK/{__version__}",
91
+ })
92
+
93
+ # =========================================================================
94
+ # Non-Streaming Chat (HTTP POST)
95
+ # =========================================================================
96
+
97
+ def chat(
98
+ self,
99
+ message: str,
100
+ files: Optional[List[str]] = None,
101
+ file_uris: Optional[List[str]] = None,
102
+ session_id: Optional[str] = None,
103
+ ) -> Dict[str, Any]:
104
+ """
105
+ Send a message to DELTA and get a complete response (non-streaming).
106
+
107
+ Args:
108
+ message: Your question or prompt.
109
+ files: Optional list of file paths to attach (PDF, DOCX, TXT, JPG, PNG).
110
+ Files are uploaded automatically — you never need to manage URIs.
111
+ file_uris: Optional list of pre-uploaded GCS URIs (advanced use only).
112
+ session_id: Optional session ID for conversation continuity.
113
+
114
+ Returns:
115
+ Dict with 'response', 'usage', 'response_time_ms', 'balance_remaining_rupees'.
116
+
117
+ Example:
118
+ # Simple query
119
+ response = client.chat("What is IPC Section 302?")
120
+
121
+ # With file attachments (just pass paths!)
122
+ response = client.chat(
123
+ "Summarize this contract",
124
+ files=["contract.pdf"]
125
+ )
126
+ """
127
+ # Auto-upload files if provided
128
+ all_uris = list(file_uris or [])
129
+ if files:
130
+ for file_path in files:
131
+ upload_result = self.upload_file(file_path)
132
+ all_uris.append(upload_result['file_ref'])
133
+
134
+ payload = {"message": message}
135
+ if all_uris:
136
+ payload["file_uris"] = all_uris
137
+ if session_id:
138
+ payload["session_id"] = session_id
139
+
140
+ return self._request("POST", "/api/sdk/chat/", json=payload)
141
+
142
+ # =========================================================================
143
+ # Streaming Chat (WebSocket)
144
+ # =========================================================================
145
+
146
+ def stream(
147
+ self,
148
+ message: str,
149
+ files: Optional[List[str]] = None,
150
+ file_uris: Optional[List[str]] = None,
151
+ session_id: Optional[str] = None,
152
+ ) -> Generator[str, None, Dict[str, Any]]:
153
+ """
154
+ Stream a DELTA response via WebSocket. Yields text chunks.
155
+
156
+ Args:
157
+ message: Your question or prompt.
158
+ files: Optional list of file paths to attach (auto-uploaded).
159
+ file_uris: Optional list of pre-uploaded GCS URIs (advanced).
160
+ session_id: Optional session ID.
161
+
162
+ Yields:
163
+ Text chunks as they arrive.
164
+
165
+ Example:
166
+ for chunk in client.stream("Explain Article 21"):
167
+ print(chunk, end="", flush=True)
168
+
169
+ # With files
170
+ for chunk in client.stream("Summarize this", files=["doc.pdf"]):
171
+ print(chunk, end="", flush=True)
172
+ """
173
+ if not HAS_WEBSOCKET:
174
+ raise LawSaathiError(
175
+ "WebSocket streaming requires 'websocket-client'. "
176
+ "Install it: pip install websocket-client"
177
+ )
178
+
179
+ # Auto-upload files if provided
180
+ all_uris = list(file_uris or [])
181
+ if files:
182
+ for file_path in files:
183
+ upload_result = self.upload_file(file_path)
184
+ all_uris.append(upload_result['file_ref'])
185
+ file_uris = all_uris or None
186
+
187
+ ws_endpoint = f"{self.ws_url}/ws/sdk/delta/?api_key={self.api_key}"
188
+ chunks = []
189
+ usage_info = {}
190
+ error_msg = None
191
+ done_event = threading.Event()
192
+
193
+ def on_message(ws_conn, raw):
194
+ nonlocal usage_info, error_msg
195
+ try:
196
+ data = json.loads(raw)
197
+ msg_type = data.get("type", "")
198
+ if msg_type == "chunk":
199
+ chunks.append(data.get("text", ""))
200
+ elif msg_type == "done":
201
+ usage_info = data
202
+ done_event.set()
203
+ ws_conn.close()
204
+ elif msg_type == "error":
205
+ error_msg = data.get("error", "Unknown error")
206
+ done_event.set()
207
+ ws_conn.close()
208
+ elif msg_type == "status":
209
+ pass # Processing status, ignore
210
+ except json.JSONDecodeError:
211
+ pass
212
+
213
+ def on_error(ws_conn, err):
214
+ nonlocal error_msg
215
+ error_msg = str(err)
216
+ done_event.set()
217
+
218
+ def on_open(ws_conn):
219
+ payload = {"message": message}
220
+ if file_uris:
221
+ payload["file_uris"] = file_uris
222
+ if session_id:
223
+ payload["session_id"] = session_id
224
+ ws_conn.send(json.dumps(payload))
225
+
226
+ def on_close(ws_conn, close_status_code, close_msg):
227
+ done_event.set()
228
+
229
+ ws_app = ws_lib.WebSocketApp(
230
+ ws_endpoint,
231
+ on_open=on_open,
232
+ on_message=on_message,
233
+ on_error=on_error,
234
+ on_close=on_close,
235
+ )
236
+
237
+ # Run WebSocket in a background thread
238
+ ws_thread = threading.Thread(target=ws_app.run_forever, daemon=True)
239
+ ws_thread.start()
240
+
241
+ # Yield chunks as they arrive
242
+ yielded = 0
243
+ while not done_event.is_set() or yielded < len(chunks):
244
+ if yielded < len(chunks):
245
+ yield chunks[yielded]
246
+ yielded += 1
247
+ else:
248
+ done_event.wait(timeout=0.05)
249
+
250
+ # Yield any remaining chunks
251
+ while yielded < len(chunks):
252
+ yield chunks[yielded]
253
+ yielded += 1
254
+
255
+ ws_thread.join(timeout=5)
256
+
257
+ if error_msg:
258
+ if "Insufficient credits" in error_msg:
259
+ raise InsufficientCreditsError(error_msg)
260
+ elif "Rate limit" in error_msg:
261
+ raise RateLimitError(error_msg)
262
+ else:
263
+ raise APIError(error_msg)
264
+
265
+ return usage_info
266
+
267
+ # =========================================================================
268
+ # File Upload (handled internally — users don't need to call this directly)
269
+ # =========================================================================
270
+
271
+ def upload_file(self, file_path: str) -> Dict[str, Any]:
272
+ """
273
+ Upload a file via HTTP. Returns a file reference for use in queries.
274
+
275
+ You usually don't need to call this directly — just pass `files=["path"]`
276
+ to `chat()` or `stream()` and it's handled automatically.
277
+
278
+ Args:
279
+ file_path: Path to file (PDF, DOCX, TXT, JPG, PNG).
280
+
281
+ Returns:
282
+ Dict with 'file_ref', 'file_id', 'filename', 'size'.
283
+ """
284
+ import os
285
+ if not os.path.exists(file_path):
286
+ raise FileNotFoundError(f"File not found: {file_path}")
287
+
288
+ filename = os.path.basename(file_path)
289
+ url = f"{self.base_url}/api/sdk/upload/"
290
+
291
+ # Use a fresh request (NOT the session, which has Content-Type: application/json)
292
+ with open(file_path, "rb") as f:
293
+ response = requests.post(
294
+ url,
295
+ files={"file": (filename, f)},
296
+ headers={
297
+ "X-API-Key": self.api_key,
298
+ "User-Agent": f"LawSaathi-Python-SDK/{__version__}",
299
+ },
300
+ timeout=self.timeout,
301
+ )
302
+
303
+ return self._handle_response(response)
304
+
305
+ # =========================================================================
306
+ # Balance & Health
307
+ # =========================================================================
308
+
309
+ def get_balance(self) -> Dict[str, Any]:
310
+ """
311
+ Check remaining wallet balance and token availability.
312
+
313
+ Returns:
314
+ Dict with 'balance_rupees', 'tokens_available', etc.
315
+ """
316
+ return self._request("GET", "/api/sdk/balance/")
317
+
318
+ def health(self) -> Dict[str, Any]:
319
+ """
320
+ Check API health and your auth status.
321
+
322
+ Returns:
323
+ Dict with 'status', 'version', 'authenticated', etc.
324
+ """
325
+ return self._request("GET", "/api/sdk/health/")
326
+
327
+ # =========================================================================
328
+ # Internal HTTP helpers
329
+ # =========================================================================
330
+
331
+ def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
332
+ """Make an HTTP request with retry logic."""
333
+ url = f"{self.base_url}{endpoint}"
334
+ last_error = None
335
+
336
+ for attempt in range(self.max_retries + 1):
337
+ try:
338
+ response = self._session.request(
339
+ method=method,
340
+ url=url,
341
+ timeout=self.timeout,
342
+ **kwargs,
343
+ )
344
+ return self._handle_response(response)
345
+
346
+ except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
347
+ last_error = e
348
+ if attempt < self.max_retries:
349
+ wait = (attempt + 1) * 2
350
+ logger.warning(f"Request failed (attempt {attempt + 1}), retrying in {wait}s: {e}")
351
+ time.sleep(wait)
352
+ continue
353
+ raise APIError(f"Request failed after {self.max_retries + 1} attempts: {e}")
354
+
355
+ except (AuthenticationError, RateLimitError, InsufficientCreditsError):
356
+ raise # Don't retry auth/rate/credit errors
357
+
358
+ except requests.exceptions.RequestException as e:
359
+ raise APIError(f"Request failed: {e}")
360
+
361
+ raise APIError(f"Request failed: {last_error}")
362
+
363
+ def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
364
+ """Handle HTTP response, raising appropriate exceptions."""
365
+ if response.status_code == 401:
366
+ raise AuthenticationError("Invalid or expired API key.")
367
+ if response.status_code == 402:
368
+ data = response.json() if response.content else {}
369
+ raise InsufficientCreditsError(
370
+ data.get("message", "Insufficient credits."),
371
+ balance_rupees=data.get("balance_rupees"),
372
+ )
373
+ if response.status_code == 429:
374
+ data = response.json() if response.content else {}
375
+ raise RateLimitError(
376
+ data.get("message", "Rate limit exceeded."),
377
+ retry_after=response.headers.get("Retry-After"),
378
+ )
379
+ if response.status_code >= 400:
380
+ try:
381
+ data = response.json()
382
+ msg = data.get("error", data.get("message", response.text))
383
+ except Exception:
384
+ msg = response.text
385
+ raise APIError(msg, status_code=response.status_code, response=response)
386
+
387
+ return response.json()
388
+
389
+ def __repr__(self):
390
+ return f"LawSaathi(api_key='{self.api_key[:12]}...', base_url='{self.base_url}')"
@@ -0,0 +1,33 @@
1
+ """Custom exceptions for LawSaathi SDK."""
2
+
3
+
4
+ class LawSaathiError(Exception):
5
+ """Base exception for all LawSaathi SDK errors."""
6
+ pass
7
+
8
+
9
+ class AuthenticationError(LawSaathiError):
10
+ """Raised when API key authentication fails (401)."""
11
+ pass
12
+
13
+
14
+ class RateLimitError(LawSaathiError):
15
+ """Raised when rate limit is exceeded (429)."""
16
+ def __init__(self, message, retry_after=None):
17
+ super().__init__(message)
18
+ self.retry_after = retry_after
19
+
20
+
21
+ class InsufficientCreditsError(LawSaathiError):
22
+ """Raised when wallet balance is insufficient (402)."""
23
+ def __init__(self, message, balance_rupees=None):
24
+ super().__init__(message)
25
+ self.balance_rupees = balance_rupees
26
+
27
+
28
+ class APIError(LawSaathiError):
29
+ """Raised for general API errors (4xx/5xx)."""
30
+ def __init__(self, message, status_code=None, response=None):
31
+ super().__init__(message)
32
+ self.status_code = status_code
33
+ self.response = response
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.4
2
+ Name: lawsaathi
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for LawSaathi Legal AI API — DELTA
5
+ Home-page: https://github.com/lawsaathi/lawsaathi-python-sdk
6
+ Author: LawSaathi
7
+ Author-email: LawSaathi <support@lawsaathi.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://lawsaathi.com
10
+ Project-URL: Documentation, https://lawsaathi.com/docs
11
+ Project-URL: Repository, https://github.com/lawsaathi/lawsaathi-python-sdk
12
+ Project-URL: Dashboard, https://lawsaathi.com/developer
13
+ Project-URL: Bug Tracker, https://github.com/lawsaathi/lawsaathi-python-sdk/issues
14
+ Keywords: lawsaathi,legal-ai,indian-law,ipc,crpc,delta,ai-sdk,legal-tech,chatbot,api-client
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.8
22
+ Classifier: Programming Language :: Python :: 3.9
23
+ Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Operating System :: OS Independent
28
+ Requires-Python: >=3.8
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: requests>=2.25.0
32
+ Provides-Extra: websocket
33
+ Requires-Dist: websocket-client>=1.0.0; extra == "websocket"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=6.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=2.0; extra == "dev"
37
+ Requires-Dist: black>=21.0; extra == "dev"
38
+ Requires-Dist: flake8>=3.9; extra == "dev"
39
+ Requires-Dist: build; extra == "dev"
40
+ Requires-Dist: twine; extra == "dev"
41
+ Dynamic: author
42
+ Dynamic: home-page
43
+ Dynamic: license-file
44
+ Dynamic: requires-python
45
+
46
+ # LawSaathi Python SDK
47
+
48
+ [![PyPI version](https://img.shields.io/pypi/v/lawsaathi.svg)](https://pypi.org/project/lawsaathi/)
49
+ [![Python versions](https://img.shields.io/pypi/pyversions/lawsaathi.svg)](https://pypi.org/project/lawsaathi/)
50
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
51
+
52
+ Official Python client for the **LawSaathi Legal AI API** — powered by **DELTA**, India's most advanced Legal AI.
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install lawsaathi
58
+
59
+ # With WebSocket streaming support:
60
+ pip install lawsaathi[websocket]
61
+ ```
62
+
63
+ ## Quick Start
64
+
65
+ ```python
66
+ from lawsaathi import LawSaathi
67
+
68
+ client = LawSaathi(api_key="ls_live_your_api_key_here")
69
+
70
+ # Ask any legal question
71
+ response = client.chat("What is IPC Section 302?")
72
+ print(response['response'])
73
+ print(f"Tokens used: {response['usage']['total_tokens']}")
74
+ print(f"Cost: ₹{response['usage']['cost_rupees']}")
75
+ ```
76
+
77
+ ## File Attachments (OpenAI-style)
78
+
79
+ Attach files to your queries — just pass file paths. The SDK handles uploading automatically.
80
+
81
+ ```python
82
+ # Analyze a contract — just pass the file path!
83
+ response = client.chat(
84
+ "Summarize the key clauses in this contract",
85
+ files=["contract.pdf"]
86
+ )
87
+ print(response['response'])
88
+
89
+ # Multiple files work too
90
+ response = client.chat(
91
+ "Compare these two agreements",
92
+ files=["agreement_v1.pdf", "agreement_v2.pdf"]
93
+ )
94
+ ```
95
+
96
+ ## Streaming (WebSocket)
97
+
98
+ ```python
99
+ # Requires: pip install lawsaathi[websocket]
100
+ for chunk in client.stream("Explain bail provisions under CrPC"):
101
+ print(chunk, end="", flush=True)
102
+
103
+ # Streaming with files
104
+ for chunk in client.stream("Analyze this document", files=["contract.pdf"]):
105
+ print(chunk, end="", flush=True)
106
+ ```
107
+
108
+ ## Check Balance
109
+
110
+ ```python
111
+ balance = client.get_balance()
112
+ print(f"Balance: ₹{balance['balance_rupees']}")
113
+ print(f"Tokens available: {balance['tokens_available']:,}")
114
+ ```
115
+
116
+ ## Error Handling
117
+
118
+ ```python
119
+ from lawsaathi import (
120
+ LawSaathi,
121
+ AuthenticationError,
122
+ RateLimitError,
123
+ InsufficientCreditsError,
124
+ APIError,
125
+ )
126
+
127
+ client = LawSaathi(api_key="ls_live_your_key")
128
+
129
+ try:
130
+ response = client.chat("Hello")
131
+ except AuthenticationError:
132
+ print("Invalid API key — check or regenerate it")
133
+ except InsufficientCreditsError as e:
134
+ print(f"Not enough credits. Balance: ₹{e.balance_rupees}")
135
+ except RateLimitError as e:
136
+ print(f"Rate limited. Retry after: {e.retry_after}s")
137
+ except APIError as e:
138
+ print(f"API error ({e.status_code}): {e}")
139
+ ```
140
+
141
+ ## Pricing
142
+
143
+ | | |
144
+ |---|---|
145
+ | **Cost per 1M tokens** | ₹998 |
146
+ | **Free credits on signup** | ₹100 |
147
+ | **Minimum topup** | ₹999 |
148
+ | **Credit expiry** | 150 days |
149
+
150
+ ## Rate Limits
151
+
152
+ | Limit | Default |
153
+ |-------|---------|
154
+ | Requests/minute | 5 |
155
+ | Requests/day | 2,500 |
156
+ | Max file size | 100 MB |
157
+
158
+ Need higher limits? Contact support@lawsaathi.com.
159
+
160
+ ## Requirements
161
+
162
+ - Python 3.8+
163
+ - `requests` library (installed automatically)
164
+ - `websocket-client` (optional, for streaming)
165
+
166
+ ## Links
167
+
168
+ - **Dashboard**: [lawsaathi.com/developer](https://lawsaathi.com/developer)
169
+ - **API Docs**: [lawsaathi.com/docs](https://lawsaathi.com/docs)
170
+ - **Support**: support@lawsaathi.com
171
+
172
+ ## License
173
+
174
+ MIT License — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ setup.py
6
+ lawsaathi/__init__.py
7
+ lawsaathi/client.py
8
+ lawsaathi/exceptions.py
9
+ lawsaathi.egg-info/PKG-INFO
10
+ lawsaathi.egg-info/SOURCES.txt
11
+ lawsaathi.egg-info/dependency_links.txt
12
+ lawsaathi.egg-info/requires.txt
13
+ lawsaathi.egg-info/top_level.txt
@@ -0,0 +1,12 @@
1
+ requests>=2.25.0
2
+
3
+ [dev]
4
+ pytest>=6.0
5
+ pytest-cov>=2.0
6
+ black>=21.0
7
+ flake8>=3.9
8
+ build
9
+ twine
10
+
11
+ [websocket]
12
+ websocket-client>=1.0.0
@@ -0,0 +1 @@
1
+ lawsaathi
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lawsaathi"
7
+ version = "1.0.0"
8
+ description = "Official Python SDK for LawSaathi Legal AI API — DELTA"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.8"
12
+ authors = [
13
+ {name = "LawSaathi", email = "support@lawsaathi.com"},
14
+ ]
15
+ keywords = [
16
+ "lawsaathi", "legal-ai", "indian-law", "ipc", "crpc",
17
+ "delta", "ai-sdk", "legal-tech", "chatbot", "api-client",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Developers",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.8",
27
+ "Programming Language :: Python :: 3.9",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Programming Language :: Python :: 3.13",
32
+ "Operating System :: OS Independent",
33
+ ]
34
+ dependencies = [
35
+ "requests>=2.25.0",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ websocket = ["websocket-client>=1.0.0"]
40
+ dev = [
41
+ "pytest>=6.0",
42
+ "pytest-cov>=2.0",
43
+ "black>=21.0",
44
+ "flake8>=3.9",
45
+ "build",
46
+ "twine",
47
+ ]
48
+
49
+ [project.urls]
50
+ Homepage = "https://lawsaathi.com"
51
+ Documentation = "https://lawsaathi.com/docs"
52
+ Repository = "https://github.com/lawsaathi/lawsaathi-python-sdk"
53
+ Dashboard = "https://lawsaathi.com/developer"
54
+ "Bug Tracker" = "https://github.com/lawsaathi/lawsaathi-python-sdk/issues"
55
+
56
+ [tool.setuptools.packages.find]
57
+ include = ["lawsaathi*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,41 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="lawsaathi",
8
+ version="1.0.0",
9
+ author="LawSaathi",
10
+ author_email="support@lawsaathi.com",
11
+ description="Official Python SDK for LawSaathi Legal AI API — DELTA",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/lawsaathi/lawsaathi-python-sdk",
15
+ packages=find_packages(),
16
+ classifiers=[
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Topic :: Software Development :: Libraries :: Python Modules",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.8",
23
+ "Programming Language :: Python :: 3.9",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ ],
28
+ python_requires=">=3.8",
29
+ install_requires=[
30
+ "requests>=2.25.0",
31
+ ],
32
+ extras_require={
33
+ "websocket": ["websocket-client>=1.0.0"],
34
+ "dev": [
35
+ "pytest>=6.0",
36
+ "pytest-cov>=2.0",
37
+ "black>=21.0",
38
+ "flake8>=3.9",
39
+ ],
40
+ },
41
+ )