sapling-py 4.0.1__tar.gz → 4.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,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: sapling-py
3
+ Version: 4.1.0
4
+ Summary: Sapling Python Client
5
+ Home-page: https://sapling.ai
6
+ Author: Sapling Intelligence
7
+ Author-email: info@sapling.ai
8
+ License: Apache License 2.0
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Requires-Python: >=3.7
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: requests
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest; extra == "test"
19
+ Requires-Dist: responses; extra == "test"
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license
27
+ Dynamic: license-file
28
+ Dynamic: provides-extra
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # Sapling Python Client
34
+
35
+ Python SDK wrapper for the [Sapling.ai API](https://sapling.ai/docs).
36
+
37
+ Try our [grammar check demo](https://sapling.ai/grammar-check).
38
+ Compare against leading grammar checking tools and APIs like Grammarly (Grammerly), LanguageTool, ProWritingAid and Ginger.
39
+
40
+ ## Overview
41
+
42
+ Sapling is a toolkit for helping developers build language model-powered applications.
43
+ The API provides spelling and grammar checking, autocomplete, tone detection, rephrasing, AI detection, and more.
44
+
45
+ Key Features:
46
+ - **60% More Corrections**: Outperforms [competing solutions](https://sapling.ai/comparison/api#sapling) at similar accuracy levels using state-of-the-art generative AI models.
47
+ - **Real-Time Performance**: Experience the same low-latency corrections as Sapling's native integrations.
48
+ - **Comprehensive Error Detection**: Identifies over 20 error types including prepositions, noun forms, verb tense, with both high-level and fine-grained error information.
49
+ - **Domain-Specific Models**: Get corrections optimized for your content type, from academic writing, to doctors notes, to user-generated reviews.
50
+ - **Enterprise-Grade Security**: Features include no data retention policies, [self-hosted/on-premises](https://sapling.ai/onprem) deployment options, and enterprise-grade [security policies and procedures](https://sapling.ai/security).
51
+ - **Rich Text Editor Integration**: Supports TinyMCE, CKEditor, QuillJS, Trix, ProseMirror, WordPress, Draft.js, Froala, Lexical and more. For web-based editors, consider using our [JavaScript SDK](https://sapling.ai/docs/sdk/JavaScript/quickstart) for display UI components.
52
+
53
+ ## Installation
54
+
55
+ Install the `sapling-py` package with [pip](https://pip.pypa.io/en/stable/installation/)
56
+
57
+ ```
58
+ python -m pip install sapling-py
59
+ ```
60
+
61
+ ## Documentation
62
+ Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
63
+ documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
64
+
65
+ ## Quickstart
66
+
67
+ - Register for an account at [Sapling.ai](https://sapling.ai).
68
+ - After registering and signing in, generate a development API key in your [dashboard](https://sapling.ai/user_settings).
69
+ - Install the Python client by following the installation steps above.
70
+
71
+ ```python
72
+ from sapling import SaplingClient
73
+
74
+ API_KEY = '<YOUR_API_KEY>'
75
+ client = SaplingClient(api_key=API_KEY)
76
+ edits = client.edits('Lets get started!', session_id='test_session')
77
+ ```
78
+
79
+ The result should be an array of edits of this form:
80
+
81
+ ```json
82
+ [{
83
+ "id": "aa5ee291-a073-5146-8ebc-c9c899d01278",
84
+ "sentence": "Lets get started!",
85
+ "sentence_start": 0,
86
+ "start": 0,
87
+ "end": 4,
88
+ "replacement": "Let's",
89
+ "error_type": "R:OTHER",
90
+ "general_error_type": "Other",
91
+ }]
92
+ ```
93
+
94
+ Get a production key by following [this documentation](https://sapling.ai/docs/api/api-access).
95
+
96
+ Here's some more information on [request options and response structure](https://sapling.ai/docs/api/edits-overview).
97
+
98
+ ## License
99
+
100
+ Copyright 2024--present Sapling Intelligence, Inc.
101
+
102
+ Licensed under the Apache License, Version 2.0.
@@ -0,0 +1,70 @@
1
+ # Sapling Python Client
2
+
3
+ Python SDK wrapper for the [Sapling.ai API](https://sapling.ai/docs).
4
+
5
+ Try our [grammar check demo](https://sapling.ai/grammar-check).
6
+ Compare against leading grammar checking tools and APIs like Grammarly (Grammerly), LanguageTool, ProWritingAid and Ginger.
7
+
8
+ ## Overview
9
+
10
+ Sapling is a toolkit for helping developers build language model-powered applications.
11
+ The API provides spelling and grammar checking, autocomplete, tone detection, rephrasing, AI detection, and more.
12
+
13
+ Key Features:
14
+ - **60% More Corrections**: Outperforms [competing solutions](https://sapling.ai/comparison/api#sapling) at similar accuracy levels using state-of-the-art generative AI models.
15
+ - **Real-Time Performance**: Experience the same low-latency corrections as Sapling's native integrations.
16
+ - **Comprehensive Error Detection**: Identifies over 20 error types including prepositions, noun forms, verb tense, with both high-level and fine-grained error information.
17
+ - **Domain-Specific Models**: Get corrections optimized for your content type, from academic writing, to doctors notes, to user-generated reviews.
18
+ - **Enterprise-Grade Security**: Features include no data retention policies, [self-hosted/on-premises](https://sapling.ai/onprem) deployment options, and enterprise-grade [security policies and procedures](https://sapling.ai/security).
19
+ - **Rich Text Editor Integration**: Supports TinyMCE, CKEditor, QuillJS, Trix, ProseMirror, WordPress, Draft.js, Froala, Lexical and more. For web-based editors, consider using our [JavaScript SDK](https://sapling.ai/docs/sdk/JavaScript/quickstart) for display UI components.
20
+
21
+ ## Installation
22
+
23
+ Install the `sapling-py` package with [pip](https://pip.pypa.io/en/stable/installation/)
24
+
25
+ ```
26
+ python -m pip install sapling-py
27
+ ```
28
+
29
+ ## Documentation
30
+ Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
31
+ documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
32
+
33
+ ## Quickstart
34
+
35
+ - Register for an account at [Sapling.ai](https://sapling.ai).
36
+ - After registering and signing in, generate a development API key in your [dashboard](https://sapling.ai/user_settings).
37
+ - Install the Python client by following the installation steps above.
38
+
39
+ ```python
40
+ from sapling import SaplingClient
41
+
42
+ API_KEY = '<YOUR_API_KEY>'
43
+ client = SaplingClient(api_key=API_KEY)
44
+ edits = client.edits('Lets get started!', session_id='test_session')
45
+ ```
46
+
47
+ The result should be an array of edits of this form:
48
+
49
+ ```json
50
+ [{
51
+ "id": "aa5ee291-a073-5146-8ebc-c9c899d01278",
52
+ "sentence": "Lets get started!",
53
+ "sentence_start": 0,
54
+ "start": 0,
55
+ "end": 4,
56
+ "replacement": "Let's",
57
+ "error_type": "R:OTHER",
58
+ "general_error_type": "Other",
59
+ }]
60
+ ```
61
+
62
+ Get a production key by following [this documentation](https://sapling.ai/docs/api/api-access).
63
+
64
+ Here's some more information on [request options and response structure](https://sapling.ai/docs/api/edits-overview).
65
+
66
+ ## License
67
+
68
+ Copyright 2024--present Sapling Intelligence, Inc.
69
+
70
+ Licensed under the Apache License, Version 2.0.
@@ -1,8 +1,9 @@
1
1
  """Sapling Python Client"""
2
2
 
3
- from .client import SaplingClient
3
+ from .client import SaplingClient, SaplingError
4
4
  from .version import __version__
5
5
 
6
6
  __all__ = [
7
7
  "SaplingClient",
8
+ "SaplingError",
8
9
  ]
@@ -1,6 +1,21 @@
1
1
  import requests
2
2
  import uuid
3
3
 
4
+
5
+ class SaplingError(Exception):
6
+ '''
7
+ Raised when the Sapling API returns a non-2xx response.
8
+
9
+ :ivar status_code: HTTP status code returned by the API.
10
+ :ivar body: Raw response body returned by the API (may be empty).
11
+ '''
12
+
13
+ def __init__(self, status_code, body=None):
14
+ self.status_code = status_code
15
+ self.body = body
16
+ super().__init__(f'HTTP {status_code}: {body}')
17
+
18
+
4
19
  class SaplingClient:
5
20
  '''
6
21
  Sapling client class. Provides a mapping of Python functions to Sapling HTTP REST APIs.
@@ -28,6 +43,32 @@ class SaplingClient:
28
43
  self.pathname = pathname or '/api/v1/'
29
44
  self.url_endpoint = self.hostname + self.pathname
30
45
  self.default_session_id = str(uuid.uuid4())
46
+ self.session = requests.Session()
47
+
48
+ def _request(self, url, data):
49
+ '''
50
+ Issues a POST request to the Sapling API and returns the parsed JSON body.
51
+
52
+ :param url: Fully-qualified endpoint URL.
53
+ :type url: str
54
+ :param data: JSON-serializable request body.
55
+ :type data: dict
56
+ :rtype: dict, list, or None
57
+ :raises SaplingError: If the API responds with a non-2xx status code.
58
+ '''
59
+ resp = self.session.post(
60
+ url,
61
+ json=data,
62
+ timeout=self.timeout,
63
+ )
64
+ if not 200 <= resp.status_code < 300:
65
+ raise SaplingError(resp.status_code, resp.text)
66
+ if not resp.content:
67
+ return None
68
+ try:
69
+ return resp.json()
70
+ except ValueError:
71
+ return None
31
72
 
32
73
  def edits(
33
74
  self,
@@ -140,14 +181,7 @@ class SaplingClient:
140
181
  if is_anon_user is not None:
141
182
  data['is_anon_user'] = is_anon_user
142
183
 
143
- resp = requests.post(
144
- url,
145
- json=data,
146
- timeout=self.timeout,
147
- )
148
- if 200 <= resp.status_code < 300:
149
- return resp.json()
150
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
184
+ return self._request(url, data)
151
185
 
152
186
  def accept_edit(
153
187
  self,
@@ -179,14 +213,7 @@ class SaplingClient:
179
213
  if user_id is not None:
180
214
  data['user_id'] = user_id
181
215
 
182
- resp = requests.post(
183
- url,
184
- json=data,
185
- timeout=self.timeout,
186
- )
187
- if 200 <= resp.status_code < 300:
188
- return
189
- raise Exception(f'HTTP {resp.status_code}: resp.text')
216
+ self._request(url, data)
190
217
 
191
218
  def reject_edit(
192
219
  self,
@@ -218,14 +245,7 @@ class SaplingClient:
218
245
  if user_id is not None:
219
246
  data['user_id'] = user_id
220
247
 
221
- resp = requests.post(
222
- url,
223
- json=data,
224
- timeout=self.timeout,
225
- )
226
- if 200 <= resp.status_code < 300:
227
- return
228
- raise Exception(f'HTTP {resp.status_code}: resp.text')
248
+ self._request(url, data)
229
249
 
230
250
  def spellcheck(
231
251
  self,
@@ -255,8 +275,8 @@ class SaplingClient:
255
275
  :type lang: str
256
276
  :param auto_apply: Whether to return a field with edits applied to the text. Cannot be set with multiple_edits option.
257
277
  :type auto_apply: bool
258
- :param advanced_edits: additional edit checking options
259
- :type advanced_edits: dict
278
+ :param user_data: Optional custom data (e.g. a personal dictionary) to inform spellchecking.
279
+ :type user_data: dict
260
280
  :param variety: Specifies regional English variety preference. Defaults to the configuration in the user Sapling dashboard.
261
281
  :type variety: str
262
282
  :param user_id: Track IDs representing your end users
@@ -343,15 +363,7 @@ class SaplingClient:
343
363
  if is_anon_user is not None:
344
364
  data['is_anon_user'] = is_anon_user
345
365
 
346
- resp = requests.post(
347
- url,
348
- json=data,
349
- timeout=self.timeout,
350
- )
351
- if 200 <= resp.status_code < 300:
352
- resp_json = resp.json()
353
- return resp_json.get('edits')
354
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
366
+ return self._request(url, data)
355
367
 
356
368
 
357
369
  def complete(
@@ -375,15 +387,7 @@ class SaplingClient:
375
387
  'session_id': session_id,
376
388
  }
377
389
 
378
- resp = requests.post(
379
- url,
380
- json=data,
381
- timeout=self.timeout,
382
- )
383
- if 200 <= resp.status_code < 300:
384
- resp_json = resp.json()
385
- return resp_json
386
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
390
+ return self._request(url, data)
387
391
 
388
392
  def accept_complete(
389
393
  self,
@@ -406,7 +410,7 @@ class SaplingClient:
406
410
  :type completion: str
407
411
  '''
408
412
  url = f'{self.url_endpoint}complete/{complete_uuid}/accept'
409
- session_id = session_id or uuid.uuid4()
413
+ session_id = session_id or self.default_session_id
410
414
  data = {
411
415
  'key': self.api_key,
412
416
  'session_id': session_id,
@@ -415,14 +419,7 @@ class SaplingClient:
415
419
  'completion': completion,
416
420
  }
417
421
  }
418
- resp = requests.post(
419
- url,
420
- json=data,
421
- timeout=self.timeout,
422
- )
423
- if 200 <= resp.status_code < 300:
424
- return
425
- raise Exception(f'HTTP {resp.status_code}: resp.text')
422
+ self._request(url, data)
426
423
 
427
424
  def aidetect(
428
425
  self,
@@ -432,7 +429,7 @@ class SaplingClient:
432
429
  '''
433
430
  Score a piece of text on how likely it was generated by AI.
434
431
 
435
- :param text: Text to
432
+ :param text: Text to score for AI-generated content.
436
433
  :type text: str
437
434
  :param sent_scores: If true, each sentence will also be scored individually.
438
435
  :type sent_scores: bool
@@ -451,14 +448,7 @@ class SaplingClient:
451
448
  }
452
449
  if sent_scores is not None:
453
450
  data['sent_scores'] = sent_scores
454
- resp = requests.post(
455
- url,
456
- json=data,
457
- timeout=self.timeout,
458
- )
459
- if 200 <= resp.status_code < 300:
460
- return resp.json()
461
- raise Exception(f'HTTP {resp.status_code}: resp.text')
451
+ return self._request(url, data)
462
452
 
463
453
  def chunk_text(
464
454
  self,
@@ -489,14 +479,7 @@ class SaplingClient:
489
479
  }
490
480
  if step_size is not None:
491
481
  data['step_size'] = step_size
492
- resp = requests.post(
493
- url,
494
- json=data,
495
- timeout=self.timeout,
496
- )
497
- if 200 <= resp.status_code < 300:
498
- return resp.json()
499
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
482
+ return self._request(url, data)
500
483
 
501
484
  def chunk_html(
502
485
  self,
@@ -529,14 +512,7 @@ class SaplingClient:
529
512
  }
530
513
  if step_size is not None:
531
514
  data['step_size'] = step_size
532
- resp = requests.post(
533
- url,
534
- json=data,
535
- timeout=self.timeout,
536
- )
537
- if 200 <= resp.status_code < 300:
538
- return resp.json()
539
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
515
+ return self._request(url, data)
540
516
 
541
517
  def postprocess(
542
518
  self,
@@ -580,11 +556,188 @@ class SaplingClient:
580
556
  'session_id': session_id,
581
557
  'operations': operations,
582
558
  }
583
- resp = requests.post(
584
- url,
585
- json=data,
586
- timeout=self.timeout,
587
- )
588
- if 200 <= resp.status_code < 300:
589
- return resp.json()
590
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
559
+ return self._request(url, data)
560
+
561
+ def rephrase(
562
+ self,
563
+ text,
564
+ mapping=None,
565
+ tone_mapping=None,
566
+ tense_mapping=None,
567
+ num_results=None,
568
+ session_id=None,
569
+ ):
570
+ '''
571
+ Rephrases the input text, optionally applying a style transformation.
572
+
573
+ Given an input sentence, returns output sentences that preserve meaning
574
+ but use alternative phrasings or styles.
575
+
576
+ :param text: Text to rephrase. Current maximum length is 400 characters.
577
+ :type text: str
578
+ :param mapping: The transformation to apply. Defaults to `paraphrase`.
579
+ Options include: `paraphrase`, `informal_to_formal`, `passive_to_active`,
580
+ `active_to_passive`, `sentence_split`, `expand`, and `switch_tone`.
581
+ :type mapping: str
582
+ :param tone_mapping: Target tone when `mapping` is `switch_tone`. Options
583
+ include: `straightforward`, `confident`, `friendly`, and `empathetic`.
584
+ :type tone_mapping: str
585
+ :param tense_mapping: Target tense transformation, when applicable.
586
+ :type tense_mapping: str
587
+ :param num_results: Number of results to return (currently only for
588
+ `paraphrase`). Minimum 1, maximum 8. Defaults to 5.
589
+ :type num_results: int
590
+ :param session_id: Unique name or UUID of document or portion of text that is being processed
591
+ :type session_id: str
592
+ :rtype: dict
593
+ :return:
594
+ - results: List of rephrases, each with `original`, `replacement`,
595
+ `rephrase_type`, `model_version`, and `hash`.
596
+ '''
597
+ url = self.url_endpoint + 'rephrase'
598
+ session_id = session_id or self.default_session_id
599
+ data = {
600
+ 'key': self.api_key,
601
+ 'text': text,
602
+ 'session_id': session_id,
603
+ }
604
+ if mapping is not None:
605
+ data['mapping'] = mapping
606
+ if tone_mapping is not None:
607
+ data['tone_mapping'] = tone_mapping
608
+ if tense_mapping is not None:
609
+ data['tense_mapping'] = tense_mapping
610
+ if num_results is not None:
611
+ data['num_results'] = num_results
612
+ return self._request(url, data)
613
+
614
+ def summarize(
615
+ self,
616
+ text,
617
+ ):
618
+ '''
619
+ Summarizes a longer document into a shorter, more digestible one.
620
+
621
+ :param text: Input document to summarize.
622
+ :type text: str
623
+ :rtype: dict
624
+ :return:
625
+ - result: The summarized text.
626
+ '''
627
+ url = self.url_endpoint + 'summarize'
628
+ data = {
629
+ 'key': self.api_key,
630
+ 'text': text,
631
+ }
632
+ return self._request(url, data)
633
+
634
+ def tone(
635
+ self,
636
+ text,
637
+ ):
638
+ '''
639
+ Detects the tone of the provided text across 28 fine-grained categories.
640
+
641
+ The overall tone is returned along with the tone for each sentence.
642
+
643
+ :param text: Text to analyze the tone for.
644
+ :type text: str
645
+ :rtype: dict
646
+ :return:
647
+ - sents: The sentences the text contains.
648
+ - overall: List of ``(probability, tone, emoji)`` tuples for the whole text.
649
+ - results: For each sentence, a list of ``(probability, tone, emoji)`` tuples.
650
+ '''
651
+ url = self.url_endpoint + 'tone'
652
+ data = {
653
+ 'key': self.api_key,
654
+ 'text': text,
655
+ }
656
+ return self._request(url, data)
657
+
658
+ def sentiment(
659
+ self,
660
+ text,
661
+ ):
662
+ '''
663
+ Detects the sentiment (positive, negative, or neutral) of the provided text.
664
+
665
+ The overall sentiment is returned along with the sentiment for each sentence.
666
+
667
+ :param text: Text to analyze the sentiment for.
668
+ :type text: str
669
+ :rtype: dict
670
+ :return:
671
+ - sents: The sentences the text contains.
672
+ - overall: List of ``(probability, sentiment)`` tuples for the whole text.
673
+ - results: For each sentence, a list of ``(probability, sentiment)`` tuples.
674
+ '''
675
+ url = self.url_endpoint + 'sentiment'
676
+ data = {
677
+ 'key': self.api_key,
678
+ 'text': text,
679
+ }
680
+ return self._request(url, data)
681
+
682
+ def quality(
683
+ self,
684
+ text,
685
+ ):
686
+ '''
687
+ Computes a quality score for the provided text based on how "surprising"
688
+ the text is to the model. Lower surprisal corresponds to higher scores.
689
+
690
+ :param text: Text to compute a quality score for.
691
+ :type text: str
692
+ :rtype: dict
693
+ :return:
694
+ - score: A score from 1 (low quality) to 5 (high quality).
695
+ '''
696
+ url = self.url_endpoint + 'quality'
697
+ data = {
698
+ 'key': self.api_key,
699
+ 'text': text,
700
+ }
701
+ return self._request(url, data)
702
+
703
+ def langdetect(
704
+ self,
705
+ text,
706
+ ):
707
+ '''
708
+ Identifies the language of the provided text.
709
+
710
+ :param text: Text to identify the language for.
711
+ :type text: str
712
+ :rtype: dict
713
+ :return:
714
+ - lang: The detected language code.
715
+ - conf: Confidence score for the detection result.
716
+ '''
717
+ url = self.url_endpoint + 'langdetect'
718
+ data = {
719
+ 'key': self.api_key,
720
+ 'text': text,
721
+ }
722
+ return self._request(url, data)
723
+
724
+ def profanity(
725
+ self,
726
+ text,
727
+ ):
728
+ '''
729
+ Checks the provided text for profanity, returning a 0/1 label per token.
730
+
731
+ :param text: Text to check for profanity.
732
+ :type text: str
733
+ :rtype: dict
734
+ :return:
735
+ - toks: The tokens (words) detected in the text.
736
+ - labels: For each token, 0 (not profanity) or 1 (profanity).
737
+ '''
738
+ url = self.url_endpoint + 'profanity'
739
+ data = {
740
+ 'key': self.api_key,
741
+ 'text': text,
742
+ }
743
+ return self._request(url, data)
@@ -0,0 +1 @@
1
+ __version__ = '4.1.0'
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: sapling-py
3
+ Version: 4.1.0
4
+ Summary: Sapling Python Client
5
+ Home-page: https://sapling.ai
6
+ Author: Sapling Intelligence
7
+ Author-email: info@sapling.ai
8
+ License: Apache License 2.0
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Requires-Python: >=3.7
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: requests
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest; extra == "test"
19
+ Requires-Dist: responses; extra == "test"
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license
27
+ Dynamic: license-file
28
+ Dynamic: provides-extra
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # Sapling Python Client
34
+
35
+ Python SDK wrapper for the [Sapling.ai API](https://sapling.ai/docs).
36
+
37
+ Try our [grammar check demo](https://sapling.ai/grammar-check).
38
+ Compare against leading grammar checking tools and APIs like Grammarly (Grammerly), LanguageTool, ProWritingAid and Ginger.
39
+
40
+ ## Overview
41
+
42
+ Sapling is a toolkit for helping developers build language model-powered applications.
43
+ The API provides spelling and grammar checking, autocomplete, tone detection, rephrasing, AI detection, and more.
44
+
45
+ Key Features:
46
+ - **60% More Corrections**: Outperforms [competing solutions](https://sapling.ai/comparison/api#sapling) at similar accuracy levels using state-of-the-art generative AI models.
47
+ - **Real-Time Performance**: Experience the same low-latency corrections as Sapling's native integrations.
48
+ - **Comprehensive Error Detection**: Identifies over 20 error types including prepositions, noun forms, verb tense, with both high-level and fine-grained error information.
49
+ - **Domain-Specific Models**: Get corrections optimized for your content type, from academic writing, to doctors notes, to user-generated reviews.
50
+ - **Enterprise-Grade Security**: Features include no data retention policies, [self-hosted/on-premises](https://sapling.ai/onprem) deployment options, and enterprise-grade [security policies and procedures](https://sapling.ai/security).
51
+ - **Rich Text Editor Integration**: Supports TinyMCE, CKEditor, QuillJS, Trix, ProseMirror, WordPress, Draft.js, Froala, Lexical and more. For web-based editors, consider using our [JavaScript SDK](https://sapling.ai/docs/sdk/JavaScript/quickstart) for display UI components.
52
+
53
+ ## Installation
54
+
55
+ Install the `sapling-py` package with [pip](https://pip.pypa.io/en/stable/installation/)
56
+
57
+ ```
58
+ python -m pip install sapling-py
59
+ ```
60
+
61
+ ## Documentation
62
+ Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
63
+ documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
64
+
65
+ ## Quickstart
66
+
67
+ - Register for an account at [Sapling.ai](https://sapling.ai).
68
+ - After registering and signing in, generate a development API key in your [dashboard](https://sapling.ai/user_settings).
69
+ - Install the Python client by following the installation steps above.
70
+
71
+ ```python
72
+ from sapling import SaplingClient
73
+
74
+ API_KEY = '<YOUR_API_KEY>'
75
+ client = SaplingClient(api_key=API_KEY)
76
+ edits = client.edits('Lets get started!', session_id='test_session')
77
+ ```
78
+
79
+ The result should be an array of edits of this form:
80
+
81
+ ```json
82
+ [{
83
+ "id": "aa5ee291-a073-5146-8ebc-c9c899d01278",
84
+ "sentence": "Lets get started!",
85
+ "sentence_start": 0,
86
+ "start": 0,
87
+ "end": 4,
88
+ "replacement": "Let's",
89
+ "error_type": "R:OTHER",
90
+ "general_error_type": "Other",
91
+ }]
92
+ ```
93
+
94
+ Get a production key by following [this documentation](https://sapling.ai/docs/api/api-access).
95
+
96
+ Here's some more information on [request options and response structure](https://sapling.ai/docs/api/edits-overview).
97
+
98
+ ## License
99
+
100
+ Copyright 2024--present Sapling Intelligence, Inc.
101
+
102
+ Licensed under the Apache License, Version 2.0.
@@ -9,4 +9,5 @@ sapling_py.egg-info/SOURCES.txt
9
9
  sapling_py.egg-info/dependency_links.txt
10
10
  sapling_py.egg-info/requires.txt
11
11
  sapling_py.egg-info/top_level.txt
12
- test/__init__.py
12
+ test/__init__.py
13
+ test/test_client.py
@@ -0,0 +1,5 @@
1
+ requests
2
+
3
+ [test]
4
+ pytest
5
+ responses
@@ -20,13 +20,22 @@ setup(
20
20
  author_email='info@sapling.ai',
21
21
  license='Apache License 2.0',
22
22
  classifiers=[
23
- 'Development Status :: 3 - Alpha',
23
+ 'Development Status :: 5 - Production/Stable',
24
+ 'License :: OSI Approved :: Apache Software License',
24
25
  'Programming Language :: Python :: 3',
26
+ 'Programming Language :: Python :: 3 :: Only',
25
27
  ],
26
- packages=find_packages(),
28
+ packages=find_packages(exclude=['test', 'test.*']),
27
29
  package_data={'':[]},
28
30
  include_package_data=True,
31
+ python_requires='>=3.7',
29
32
  install_requires=[
30
33
  'requests'
31
34
  ],
35
+ extras_require={
36
+ 'test': [
37
+ 'pytest',
38
+ 'responses',
39
+ ],
40
+ },
32
41
  )
@@ -0,0 +1,158 @@
1
+ import json
2
+
3
+ import pytest
4
+ import responses
5
+
6
+ from sapling import SaplingClient, SaplingError
7
+
8
+ API_KEY = 'a' * 32
9
+ BASE = 'https://api.sapling.ai/api/v1/'
10
+
11
+
12
+ @pytest.fixture
13
+ def client():
14
+ return SaplingClient(api_key=API_KEY)
15
+
16
+
17
+ def _last_request_body():
18
+ '''Return the JSON body of the most recent mocked request.'''
19
+ return json.loads(responses.calls[-1].request.body)
20
+
21
+
22
+ @responses.activate
23
+ def test_edits_returns_json_and_sends_key(client):
24
+ responses.add(
25
+ responses.POST,
26
+ BASE + 'edits',
27
+ json={'edits': [{'replacement': "Let's"}]},
28
+ status=200,
29
+ )
30
+ result = client.edits('Lets go', session_id='s1')
31
+ assert result == {'edits': [{'replacement': "Let's"}]}
32
+ body = _last_request_body()
33
+ assert body['key'] == API_KEY
34
+ assert body['text'] == 'Lets go'
35
+ assert body['session_id'] == 's1'
36
+
37
+
38
+ @responses.activate
39
+ def test_edits_uses_default_session_id(client):
40
+ responses.add(responses.POST, BASE + 'edits', json={'edits': []}, status=200)
41
+ client.edits('hi')
42
+ assert _last_request_body()['session_id'] == client.default_session_id
43
+
44
+
45
+ @responses.activate
46
+ def test_optional_params_only_sent_when_provided(client):
47
+ responses.add(responses.POST, BASE + 'edits', json={'edits': []}, status=200)
48
+ client.edits('hi')
49
+ body = _last_request_body()
50
+ for absent in ('lang', 'variety', 'medical', 'advanced_edits', 'user_id', 'is_anon_user'):
51
+ assert absent not in body
52
+ # auto_apply defaults to False and is always included
53
+ assert body['auto_apply'] is False
54
+
55
+
56
+ @responses.activate
57
+ def test_error_response_raises_saplingerror_with_body(client):
58
+ responses.add(
59
+ responses.POST,
60
+ BASE + 'edits',
61
+ json={'msg': 'Invalid text.'},
62
+ status=400,
63
+ )
64
+ with pytest.raises(SaplingError) as exc_info:
65
+ client.edits('hi')
66
+ err = exc_info.value
67
+ assert err.status_code == 400
68
+ # Regression test: the response body must be interpolated into the message.
69
+ assert 'Invalid text.' in str(err)
70
+ assert 'Invalid text.' in err.body
71
+
72
+
73
+ @responses.activate
74
+ def test_accept_edit_error_includes_body(client):
75
+ '''Regression: accept_edit previously never interpolated resp.text.'''
76
+ responses.add(
77
+ responses.POST,
78
+ BASE + 'edits/some-uuid/accept',
79
+ json={'msg': 'Not found'},
80
+ status=404,
81
+ )
82
+ with pytest.raises(SaplingError) as exc_info:
83
+ client.accept_edit('some-uuid', session_id='s1')
84
+ assert 'Not found' in str(exc_info.value)
85
+
86
+
87
+ @responses.activate
88
+ def test_accept_edit_success_returns_none(client):
89
+ responses.add(responses.POST, BASE + 'edits/some-uuid/accept', body='', status=200)
90
+ assert client.accept_edit('some-uuid') is None
91
+
92
+
93
+ @responses.activate
94
+ def test_reject_edit_success_returns_none(client):
95
+ responses.add(responses.POST, BASE + 'edits/some-uuid/reject', body='', status=200)
96
+ assert client.reject_edit('some-uuid') is None
97
+
98
+
99
+ @responses.activate
100
+ def test_accept_complete_sends_string_session_id(client):
101
+ '''Regression: session_id fallback must be a str, not a uuid.UUID.'''
102
+ responses.add(responses.POST, BASE + 'complete/c-uuid/accept', body='', status=200)
103
+ client.accept_complete('c-uuid', 'the query', 'the completion')
104
+ body = _last_request_body()
105
+ assert isinstance(body['session_id'], str)
106
+ assert body['session_id'] == client.default_session_id
107
+ assert body['context'] == {'query': 'the query', 'completion': 'the completion'}
108
+
109
+
110
+ @responses.activate
111
+ def test_aidetect_error_includes_body(client):
112
+ '''Regression: aidetect previously never interpolated resp.text.'''
113
+ responses.add(responses.POST, BASE + 'aidetect', json={'msg': 'boom'}, status=500)
114
+ with pytest.raises(SaplingError) as exc_info:
115
+ client.aidetect('some text')
116
+ assert 'boom' in str(exc_info.value)
117
+
118
+
119
+ @responses.activate
120
+ def test_rephrase_sends_expected_params(client):
121
+ responses.add(responses.POST, BASE + 'rephrase', json={'results': []}, status=200)
122
+ client.rephrase('hey wuts up', mapping='informal_to_formal', num_results=3)
123
+ body = _last_request_body()
124
+ assert body['text'] == 'hey wuts up'
125
+ assert body['mapping'] == 'informal_to_formal'
126
+ assert body['num_results'] == 3
127
+ assert 'session_id' in body
128
+ # Unset optional params should be omitted.
129
+ assert 'tone_mapping' not in body
130
+ assert 'tense_mapping' not in body
131
+
132
+
133
+ @pytest.mark.parametrize(
134
+ 'method,path,payload',
135
+ [
136
+ ('summarize', 'summarize', {'result': 'short'}),
137
+ ('tone', 'tone', {'overall': [], 'results': [], 'sents': []}),
138
+ ('sentiment', 'sentiment', {'overall': [], 'results': [], 'sents': []}),
139
+ ('quality', 'quality', {'score': 3.5}),
140
+ ('langdetect', 'langdetect', {'lang': 'en', 'conf': 0.99}),
141
+ ('profanity', 'profanity', {'toks': ['a'], 'labels': [0]}),
142
+ ],
143
+ )
144
+ @responses.activate
145
+ def test_single_text_endpoints(client, method, path, payload):
146
+ responses.add(responses.POST, BASE + path, json=payload, status=200)
147
+ result = getattr(client, method)('some text')
148
+ assert result == payload
149
+ body = _last_request_body()
150
+ assert body == {'key': API_KEY, 'text': 'some text'}
151
+
152
+
153
+ @responses.activate
154
+ def test_hostname_and_pathname_override():
155
+ client = SaplingClient(api_key=API_KEY, hostname='http://localhost:5000', pathname='/api/v1/')
156
+ responses.add(responses.POST, 'http://localhost:5000/api/v1/edits', json={'edits': []}, status=200)
157
+ client.edits('hi')
158
+ assert responses.calls[-1].request.url == 'http://localhost:5000/api/v1/edits'
sapling-py-4.0.1/PKG-INFO DELETED
@@ -1,93 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: sapling-py
3
- Version: 4.0.1
4
- Summary: Sapling Python Client
5
- Home-page: https://sapling.ai
6
- Author: Sapling Intelligence
7
- Author-email: info@sapling.ai
8
- License: Apache License 2.0
9
- Classifier: Development Status :: 3 - Alpha
10
- Classifier: Programming Language :: Python :: 3
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
-
14
- # Sapling Python Client
15
-
16
- Python wrapper for the [Sapling.ai API](https://sapling.ai/api).
17
-
18
- Try out a [grammar check demo](https://sapling.ai/grammar-check).
19
- Compare against grammar checking tools and APIs like Grammarly (Grammerly), LanguageTool, ProWritingAid and Ginger.
20
-
21
- Functionality
22
- --------
23
-
24
- Sapling is an AI messaging assistant.
25
- The API currently offers spelling and grammar checking endpoints.
26
-
27
- Benefits include:
28
- - **60% more grammar corrections**: compared to [other systems](https://sapling.ai/comparison/api) at similar accuracy using state-of-the-art machine learning systems for natural language processing.
29
- - **Low Latency**: Achieve the same real-time performance that users of Sapling's own interface experience.
30
- - **Over 20 error types**: Error categories such as preposition, noun form, and verb tense, including both high-level and fine-grained error information.
31
- - **Custom Models**: Get corrections and edits tuned to the domain of your text—for example academic writing vs. subtitles.
32
- - **Enterprise Security**: Ask us about our no data retention policies, [self-hosted/on-premises](https://sapling.ai/onprem) deployment options, and other [cybersecurity policies and procedures](https://sapling.ai/security).
33
- - **Rich Text Editor Support**: TinyMCE, CKEditor, QuillJS, Trix, ProseMirror, WordPress, Draft.js, Froala, Lexical and others. Consider using [Sapling's JavaScript SDK](https://sapling.ai/docs/sdk/JavaScript/quickstart) for displaying the API's outputs on webpage editors.
34
-
35
-
36
- Installation
37
- --------
38
-
39
- Install the `sapling-py` package with [pip](https://pip.pypa.io/en/stable/installation/)
40
-
41
-
42
- ```
43
- python -m pip install sapling-py
44
- ```
45
-
46
-
47
- Documentation
48
- -------------
49
-
50
- Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
51
- documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
52
-
53
-
54
- Quickstart
55
- -----------
56
-
57
- - Register for an account at [Sapling.ai](https://sapling.ai).
58
- - After registering and signing in, generate a development API key in your [dashboard](https://sapling.ai/user_settings).
59
- - Install the Python client by following the installation steps above.
60
-
61
- ```python
62
- from sapling import SaplingClient
63
-
64
- API_KEY = '<YOUR_API_KEY>'
65
- client = SaplingClient(api_key=API_KEY)
66
- edits = client.edits('Lets get started!', session_id='test_session')
67
- ```
68
-
69
- The result should be an array of edits of this form:
70
-
71
- ```json
72
- [{
73
- "id": "aa5ee291-a073-5146-8ebc-c9c899d01278",
74
- "sentence": "Lets get started!",
75
- "sentence_start": 0,
76
- "start": 0,
77
- "end": 4,
78
- "replacement": "Let's",
79
- "error_type": "R:OTHER",
80
- "general_error_type": "Other",
81
- }]
82
- ```
83
-
84
- Get a production key by following [this documentation](https://sapling.ai/docs/api/api-access).
85
-
86
- Here's some more information on [request options and response structure](https://sapling.ai/docs/api/edits-overview).
87
-
88
- License
89
- -------
90
-
91
- Copyright 2022 Sapling Intelligence, Inc.
92
-
93
- Licensed under the Apache License, Version 2.0.
@@ -1,80 +0,0 @@
1
- # Sapling Python Client
2
-
3
- Python wrapper for the [Sapling.ai API](https://sapling.ai/api).
4
-
5
- Try out a [grammar check demo](https://sapling.ai/grammar-check).
6
- Compare against grammar checking tools and APIs like Grammarly (Grammerly), LanguageTool, ProWritingAid and Ginger.
7
-
8
- Functionality
9
- --------
10
-
11
- Sapling is an AI messaging assistant.
12
- The API currently offers spelling and grammar checking endpoints.
13
-
14
- Benefits include:
15
- - **60% more grammar corrections**: compared to [other systems](https://sapling.ai/comparison/api) at similar accuracy using state-of-the-art machine learning systems for natural language processing.
16
- - **Low Latency**: Achieve the same real-time performance that users of Sapling's own interface experience.
17
- - **Over 20 error types**: Error categories such as preposition, noun form, and verb tense, including both high-level and fine-grained error information.
18
- - **Custom Models**: Get corrections and edits tuned to the domain of your text—for example academic writing vs. subtitles.
19
- - **Enterprise Security**: Ask us about our no data retention policies, [self-hosted/on-premises](https://sapling.ai/onprem) deployment options, and other [cybersecurity policies and procedures](https://sapling.ai/security).
20
- - **Rich Text Editor Support**: TinyMCE, CKEditor, QuillJS, Trix, ProseMirror, WordPress, Draft.js, Froala, Lexical and others. Consider using [Sapling's JavaScript SDK](https://sapling.ai/docs/sdk/JavaScript/quickstart) for displaying the API's outputs on webpage editors.
21
-
22
-
23
- Installation
24
- --------
25
-
26
- Install the `sapling-py` package with [pip](https://pip.pypa.io/en/stable/installation/)
27
-
28
-
29
- ```
30
- python -m pip install sapling-py
31
- ```
32
-
33
-
34
- Documentation
35
- -------------
36
-
37
- Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
38
- documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
39
-
40
-
41
- Quickstart
42
- -----------
43
-
44
- - Register for an account at [Sapling.ai](https://sapling.ai).
45
- - After registering and signing in, generate a development API key in your [dashboard](https://sapling.ai/user_settings).
46
- - Install the Python client by following the installation steps above.
47
-
48
- ```python
49
- from sapling import SaplingClient
50
-
51
- API_KEY = '<YOUR_API_KEY>'
52
- client = SaplingClient(api_key=API_KEY)
53
- edits = client.edits('Lets get started!', session_id='test_session')
54
- ```
55
-
56
- The result should be an array of edits of this form:
57
-
58
- ```json
59
- [{
60
- "id": "aa5ee291-a073-5146-8ebc-c9c899d01278",
61
- "sentence": "Lets get started!",
62
- "sentence_start": 0,
63
- "start": 0,
64
- "end": 4,
65
- "replacement": "Let's",
66
- "error_type": "R:OTHER",
67
- "general_error_type": "Other",
68
- }]
69
- ```
70
-
71
- Get a production key by following [this documentation](https://sapling.ai/docs/api/api-access).
72
-
73
- Here's some more information on [request options and response structure](https://sapling.ai/docs/api/edits-overview).
74
-
75
- License
76
- -------
77
-
78
- Copyright 2022 Sapling Intelligence, Inc.
79
-
80
- Licensed under the Apache License, Version 2.0.
@@ -1 +0,0 @@
1
- __version__ = '4.0.1'
@@ -1,93 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: sapling-py
3
- Version: 4.0.1
4
- Summary: Sapling Python Client
5
- Home-page: https://sapling.ai
6
- Author: Sapling Intelligence
7
- Author-email: info@sapling.ai
8
- License: Apache License 2.0
9
- Classifier: Development Status :: 3 - Alpha
10
- Classifier: Programming Language :: Python :: 3
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
-
14
- # Sapling Python Client
15
-
16
- Python wrapper for the [Sapling.ai API](https://sapling.ai/api).
17
-
18
- Try out a [grammar check demo](https://sapling.ai/grammar-check).
19
- Compare against grammar checking tools and APIs like Grammarly (Grammerly), LanguageTool, ProWritingAid and Ginger.
20
-
21
- Functionality
22
- --------
23
-
24
- Sapling is an AI messaging assistant.
25
- The API currently offers spelling and grammar checking endpoints.
26
-
27
- Benefits include:
28
- - **60% more grammar corrections**: compared to [other systems](https://sapling.ai/comparison/api) at similar accuracy using state-of-the-art machine learning systems for natural language processing.
29
- - **Low Latency**: Achieve the same real-time performance that users of Sapling's own interface experience.
30
- - **Over 20 error types**: Error categories such as preposition, noun form, and verb tense, including both high-level and fine-grained error information.
31
- - **Custom Models**: Get corrections and edits tuned to the domain of your text—for example academic writing vs. subtitles.
32
- - **Enterprise Security**: Ask us about our no data retention policies, [self-hosted/on-premises](https://sapling.ai/onprem) deployment options, and other [cybersecurity policies and procedures](https://sapling.ai/security).
33
- - **Rich Text Editor Support**: TinyMCE, CKEditor, QuillJS, Trix, ProseMirror, WordPress, Draft.js, Froala, Lexical and others. Consider using [Sapling's JavaScript SDK](https://sapling.ai/docs/sdk/JavaScript/quickstart) for displaying the API's outputs on webpage editors.
34
-
35
-
36
- Installation
37
- --------
38
-
39
- Install the `sapling-py` package with [pip](https://pip.pypa.io/en/stable/installation/)
40
-
41
-
42
- ```
43
- python -m pip install sapling-py
44
- ```
45
-
46
-
47
- Documentation
48
- -------------
49
-
50
- Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
51
- documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
52
-
53
-
54
- Quickstart
55
- -----------
56
-
57
- - Register for an account at [Sapling.ai](https://sapling.ai).
58
- - After registering and signing in, generate a development API key in your [dashboard](https://sapling.ai/user_settings).
59
- - Install the Python client by following the installation steps above.
60
-
61
- ```python
62
- from sapling import SaplingClient
63
-
64
- API_KEY = '<YOUR_API_KEY>'
65
- client = SaplingClient(api_key=API_KEY)
66
- edits = client.edits('Lets get started!', session_id='test_session')
67
- ```
68
-
69
- The result should be an array of edits of this form:
70
-
71
- ```json
72
- [{
73
- "id": "aa5ee291-a073-5146-8ebc-c9c899d01278",
74
- "sentence": "Lets get started!",
75
- "sentence_start": 0,
76
- "start": 0,
77
- "end": 4,
78
- "replacement": "Let's",
79
- "error_type": "R:OTHER",
80
- "general_error_type": "Other",
81
- }]
82
- ```
83
-
84
- Get a production key by following [this documentation](https://sapling.ai/docs/api/api-access).
85
-
86
- Here's some more information on [request options and response structure](https://sapling.ai/docs/api/edits-overview).
87
-
88
- License
89
- -------
90
-
91
- Copyright 2022 Sapling Intelligence, Inc.
92
-
93
- Licensed under the Apache License, Version 2.0.
@@ -1 +0,0 @@
1
- requests
File without changes
File without changes
File without changes