sapling-py 4.0.2__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,14 +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
- return resp.json()
353
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
366
+ return self._request(url, data)
354
367
 
355
368
 
356
369
  def complete(
@@ -374,14 +387,7 @@ class SaplingClient:
374
387
  'session_id': session_id,
375
388
  }
376
389
 
377
- resp = requests.post(
378
- url,
379
- json=data,
380
- timeout=self.timeout,
381
- )
382
- if 200 <= resp.status_code < 300:
383
- return resp.json()
384
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
390
+ return self._request(url, data)
385
391
 
386
392
  def accept_complete(
387
393
  self,
@@ -404,7 +410,7 @@ class SaplingClient:
404
410
  :type completion: str
405
411
  '''
406
412
  url = f'{self.url_endpoint}complete/{complete_uuid}/accept'
407
- session_id = session_id or uuid.uuid4()
413
+ session_id = session_id or self.default_session_id
408
414
  data = {
409
415
  'key': self.api_key,
410
416
  'session_id': session_id,
@@ -413,14 +419,7 @@ class SaplingClient:
413
419
  'completion': completion,
414
420
  }
415
421
  }
416
- resp = requests.post(
417
- url,
418
- json=data,
419
- timeout=self.timeout,
420
- )
421
- if 200 <= resp.status_code < 300:
422
- return
423
- raise Exception(f'HTTP {resp.status_code}: resp.text')
422
+ self._request(url, data)
424
423
 
425
424
  def aidetect(
426
425
  self,
@@ -430,7 +429,7 @@ class SaplingClient:
430
429
  '''
431
430
  Score a piece of text on how likely it was generated by AI.
432
431
 
433
- :param text: Text to
432
+ :param text: Text to score for AI-generated content.
434
433
  :type text: str
435
434
  :param sent_scores: If true, each sentence will also be scored individually.
436
435
  :type sent_scores: bool
@@ -449,14 +448,7 @@ class SaplingClient:
449
448
  }
450
449
  if sent_scores is not None:
451
450
  data['sent_scores'] = sent_scores
452
- resp = requests.post(
453
- url,
454
- json=data,
455
- timeout=self.timeout,
456
- )
457
- if 200 <= resp.status_code < 300:
458
- return resp.json()
459
- raise Exception(f'HTTP {resp.status_code}: resp.text')
451
+ return self._request(url, data)
460
452
 
461
453
  def chunk_text(
462
454
  self,
@@ -487,14 +479,7 @@ class SaplingClient:
487
479
  }
488
480
  if step_size is not None:
489
481
  data['step_size'] = step_size
490
- resp = requests.post(
491
- url,
492
- json=data,
493
- timeout=self.timeout,
494
- )
495
- if 200 <= resp.status_code < 300:
496
- return resp.json()
497
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
482
+ return self._request(url, data)
498
483
 
499
484
  def chunk_html(
500
485
  self,
@@ -527,14 +512,7 @@ class SaplingClient:
527
512
  }
528
513
  if step_size is not None:
529
514
  data['step_size'] = step_size
530
- resp = requests.post(
531
- url,
532
- json=data,
533
- timeout=self.timeout,
534
- )
535
- if 200 <= resp.status_code < 300:
536
- return resp.json()
537
- raise Exception(f'HTTP {resp.status_code}: {resp.text}')
515
+ return self._request(url, data)
538
516
 
539
517
  def postprocess(
540
518
  self,
@@ -578,11 +556,188 @@ class SaplingClient:
578
556
  'session_id': session_id,
579
557
  'operations': operations,
580
558
  }
581
- resp = requests.post(
582
- url,
583
- json=data,
584
- timeout=self.timeout,
585
- )
586
- if 200 <= resp.status_code < 300:
587
- return resp.json()
588
- 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.2/PKG-INFO DELETED
@@ -1,94 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: sapling-py
3
- Version: 4.0.2
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
- Requires-Dist: requests
14
-
15
- # Sapling Python Client
16
-
17
- Python wrapper for the [Sapling.ai API](https://sapling.ai/api).
18
-
19
- Try out a [grammar check demo](https://sapling.ai/grammar-check).
20
- Compare against grammar checking tools and APIs like Grammarly (Grammerly), LanguageTool, ProWritingAid and Ginger.
21
-
22
- Functionality
23
- --------
24
-
25
- Sapling is an AI messaging assistant.
26
- The API currently offers spelling and grammar checking endpoints.
27
-
28
- Benefits include:
29
- - **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.
30
- - **Low Latency**: Achieve the same real-time performance that users of Sapling's own interface experience.
31
- - **Over 20 error types**: Error categories such as preposition, noun form, and verb tense, including both high-level and fine-grained error information.
32
- - **Custom Models**: Get corrections and edits tuned to the domain of your text—for example academic writing vs. subtitles.
33
- - **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).
34
- - **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.
35
-
36
-
37
- Installation
38
- --------
39
-
40
- Install the `sapling-py` package with [pip](https://pip.pypa.io/en/stable/installation/)
41
-
42
-
43
- ```
44
- python -m pip install sapling-py
45
- ```
46
-
47
-
48
- Documentation
49
- -------------
50
-
51
- Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
52
- documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
53
-
54
-
55
- Quickstart
56
- -----------
57
-
58
- - Register for an account at [Sapling.ai](https://sapling.ai).
59
- - After registering and signing in, generate a development API key in your [dashboard](https://sapling.ai/user_settings).
60
- - Install the Python client by following the installation steps above.
61
-
62
- ```python
63
- from sapling import SaplingClient
64
-
65
- API_KEY = '<YOUR_API_KEY>'
66
- client = SaplingClient(api_key=API_KEY)
67
- edits = client.edits('Lets get started!', session_id='test_session')
68
- ```
69
-
70
- The result should be an array of edits of this form:
71
-
72
- ```json
73
- [{
74
- "id": "aa5ee291-a073-5146-8ebc-c9c899d01278",
75
- "sentence": "Lets get started!",
76
- "sentence_start": 0,
77
- "start": 0,
78
- "end": 4,
79
- "replacement": "Let's",
80
- "error_type": "R:OTHER",
81
- "general_error_type": "Other",
82
- }]
83
- ```
84
-
85
- Get a production key by following [this documentation](https://sapling.ai/docs/api/api-access).
86
-
87
- Here's some more information on [request options and response structure](https://sapling.ai/docs/api/edits-overview).
88
-
89
- License
90
- -------
91
-
92
- Copyright 2022 Sapling Intelligence, Inc.
93
-
94
- 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.2'
@@ -1,94 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: sapling-py
3
- Version: 4.0.2
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
- Requires-Dist: requests
14
-
15
- # Sapling Python Client
16
-
17
- Python wrapper for the [Sapling.ai API](https://sapling.ai/api).
18
-
19
- Try out a [grammar check demo](https://sapling.ai/grammar-check).
20
- Compare against grammar checking tools and APIs like Grammarly (Grammerly), LanguageTool, ProWritingAid and Ginger.
21
-
22
- Functionality
23
- --------
24
-
25
- Sapling is an AI messaging assistant.
26
- The API currently offers spelling and grammar checking endpoints.
27
-
28
- Benefits include:
29
- - **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.
30
- - **Low Latency**: Achieve the same real-time performance that users of Sapling's own interface experience.
31
- - **Over 20 error types**: Error categories such as preposition, noun form, and verb tense, including both high-level and fine-grained error information.
32
- - **Custom Models**: Get corrections and edits tuned to the domain of your text—for example academic writing vs. subtitles.
33
- - **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).
34
- - **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.
35
-
36
-
37
- Installation
38
- --------
39
-
40
- Install the `sapling-py` package with [pip](https://pip.pypa.io/en/stable/installation/)
41
-
42
-
43
- ```
44
- python -m pip install sapling-py
45
- ```
46
-
47
-
48
- Documentation
49
- -------------
50
-
51
- Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
52
- documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
53
-
54
-
55
- Quickstart
56
- -----------
57
-
58
- - Register for an account at [Sapling.ai](https://sapling.ai).
59
- - After registering and signing in, generate a development API key in your [dashboard](https://sapling.ai/user_settings).
60
- - Install the Python client by following the installation steps above.
61
-
62
- ```python
63
- from sapling import SaplingClient
64
-
65
- API_KEY = '<YOUR_API_KEY>'
66
- client = SaplingClient(api_key=API_KEY)
67
- edits = client.edits('Lets get started!', session_id='test_session')
68
- ```
69
-
70
- The result should be an array of edits of this form:
71
-
72
- ```json
73
- [{
74
- "id": "aa5ee291-a073-5146-8ebc-c9c899d01278",
75
- "sentence": "Lets get started!",
76
- "sentence_start": 0,
77
- "start": 0,
78
- "end": 4,
79
- "replacement": "Let's",
80
- "error_type": "R:OTHER",
81
- "general_error_type": "Other",
82
- }]
83
- ```
84
-
85
- Get a production key by following [this documentation](https://sapling.ai/docs/api/api-access).
86
-
87
- Here's some more information on [request options and response structure](https://sapling.ai/docs/api/edits-overview).
88
-
89
- License
90
- -------
91
-
92
- Copyright 2022 Sapling Intelligence, Inc.
93
-
94
- Licensed under the Apache License, Version 2.0.
@@ -1 +0,0 @@
1
- requests
File without changes
File without changes
File without changes