sapling-py 4.2.0__tar.gz → 4.4.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.
- {sapling_py-4.2.0/sapling_py.egg-info → sapling_py-4.4.0}/PKG-INFO +16 -19
- {sapling_py-4.2.0 → sapling_py-4.4.0}/README.md +11 -4
- sapling_py-4.4.0/pyproject.toml +42 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/sapling/client.py +67 -0
- sapling_py-4.4.0/sapling/version.py +1 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0/sapling_py.egg-info}/PKG-INFO +16 -19
- {sapling_py-4.2.0 → sapling_py-4.4.0}/sapling_py.egg-info/SOURCES.txt +3 -1
- sapling_py-4.4.0/setup.py +53 -0
- sapling_py-4.4.0/test/test_safety.py +56 -0
- sapling_py-4.4.0/test/test_translate.py +81 -0
- sapling_py-4.2.0/sapling/version.py +0 -1
- sapling_py-4.2.0/setup.py +0 -41
- sapling_py-4.2.0/test/test_safety.py +0 -30
- {sapling_py-4.2.0 → sapling_py-4.4.0}/LICENSE +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/sapling/__init__.py +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/sapling_py.egg-info/dependency_links.txt +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/sapling_py.egg-info/requires.txt +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/sapling_py.egg-info/top_level.txt +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/setup.cfg +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/test/test_classify.py +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/test/test_client.py +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/test/test_extract.py +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/test/test_pii.py +0 -0
- {sapling_py-4.2.0 → sapling_py-4.4.0}/test/test_seo.py +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sapling-py
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.4.0
|
|
4
4
|
Summary: Sapling Python Client
|
|
5
|
-
|
|
6
|
-
Author: Sapling Intelligence
|
|
7
|
-
Author-email: info@sapling.ai
|
|
5
|
+
Author-email: Sapling Intelligence <info@sapling.ai>
|
|
8
6
|
License: Apache License 2.0
|
|
7
|
+
Project-URL: Homepage, https://sapling.ai
|
|
8
|
+
Project-URL: Documentation, https://sapling.readthedocs.io/
|
|
9
|
+
Project-URL: Repository, https://github.com/saplingai/sapling-py
|
|
9
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -17,18 +18,7 @@ Requires-Dist: requests
|
|
|
17
18
|
Provides-Extra: test
|
|
18
19
|
Requires-Dist: pytest; extra == "test"
|
|
19
20
|
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
21
|
Dynamic: license-file
|
|
28
|
-
Dynamic: provides-extra
|
|
29
|
-
Dynamic: requires-dist
|
|
30
|
-
Dynamic: requires-python
|
|
31
|
-
Dynamic: summary
|
|
32
22
|
|
|
33
23
|
# Sapling Python Client
|
|
34
24
|
|
|
@@ -41,8 +31,8 @@ Compare against leading grammar checking tools and APIs like Grammarly (Grammerl
|
|
|
41
31
|
|
|
42
32
|
Sapling is a toolkit for helping developers build language model-powered applications.
|
|
43
33
|
The API provides spelling and grammar checking, autocomplete, tone detection, rephrasing, AI detection,
|
|
44
|
-
zero-shot text classification, structured data extraction, PII detection and redaction,
|
|
45
|
-
scoring, SEO analysis, and more.
|
|
34
|
+
zero-shot text classification, structured data extraction, translation, PII detection and redaction,
|
|
35
|
+
content safety scoring, SEO analysis, and more.
|
|
46
36
|
|
|
47
37
|
Key Features:
|
|
48
38
|
- **60% More Corrections**: Outperforms [competing solutions](https://sapling.ai/comparison/api#sapling) at similar accuracy levels using state-of-the-art generative AI models.
|
|
@@ -54,12 +44,19 @@ Key Features:
|
|
|
54
44
|
|
|
55
45
|
## Installation
|
|
56
46
|
|
|
57
|
-
Install the `sapling-py` package with
|
|
47
|
+
Install the `sapling-py` package from PyPI with
|
|
48
|
+
[pip](https://pip.pypa.io/en/stable/installation/):
|
|
58
49
|
|
|
59
|
-
```
|
|
50
|
+
```sh
|
|
60
51
|
python -m pip install sapling-py
|
|
61
52
|
```
|
|
62
53
|
|
|
54
|
+
Or add it to a [uv](https://docs.astral.sh/uv/) project:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
uv add sapling-py
|
|
58
|
+
```
|
|
59
|
+
|
|
63
60
|
## Documentation
|
|
64
61
|
Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
|
|
65
62
|
documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
|
|
@@ -9,8 +9,8 @@ Compare against leading grammar checking tools and APIs like Grammarly (Grammerl
|
|
|
9
9
|
|
|
10
10
|
Sapling is a toolkit for helping developers build language model-powered applications.
|
|
11
11
|
The API provides spelling and grammar checking, autocomplete, tone detection, rephrasing, AI detection,
|
|
12
|
-
zero-shot text classification, structured data extraction, PII detection and redaction,
|
|
13
|
-
scoring, SEO analysis, and more.
|
|
12
|
+
zero-shot text classification, structured data extraction, translation, PII detection and redaction,
|
|
13
|
+
content safety scoring, SEO analysis, and more.
|
|
14
14
|
|
|
15
15
|
Key Features:
|
|
16
16
|
- **60% More Corrections**: Outperforms [competing solutions](https://sapling.ai/comparison/api#sapling) at similar accuracy levels using state-of-the-art generative AI models.
|
|
@@ -22,12 +22,19 @@ Key Features:
|
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
25
|
-
Install the `sapling-py` package with
|
|
25
|
+
Install the `sapling-py` package from PyPI with
|
|
26
|
+
[pip](https://pip.pypa.io/en/stable/installation/):
|
|
26
27
|
|
|
27
|
-
```
|
|
28
|
+
```sh
|
|
28
29
|
python -m pip install sapling-py
|
|
29
30
|
```
|
|
30
31
|
|
|
32
|
+
Or add it to a [uv](https://docs.astral.sh/uv/) project:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
uv add sapling-py
|
|
36
|
+
```
|
|
37
|
+
|
|
31
38
|
## Documentation
|
|
32
39
|
Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
|
|
33
40
|
documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sapling-py"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Sapling Python Client"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
license = { text = "Apache License 2.0" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Sapling Intelligence", email = "info@sapling.ai" },
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 5 - Production/Stable",
|
|
17
|
+
"License :: OSI Approved :: Apache Software License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"requests",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
test = [
|
|
27
|
+
"pytest",
|
|
28
|
+
"responses",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://sapling.ai"
|
|
33
|
+
Documentation = "https://sapling.readthedocs.io/"
|
|
34
|
+
Repository = "https://github.com/saplingai/sapling-py"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.dynamic]
|
|
37
|
+
version = { attr = "sapling.version.__version__" }
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
include = ["sapling", "sapling.*"]
|
|
41
|
+
exclude = ["test", "test.*"]
|
|
42
|
+
namespaces = false
|
|
@@ -718,6 +718,63 @@ class SaplingClient:
|
|
|
718
718
|
data['context'] = context
|
|
719
719
|
return self._request(url, data)
|
|
720
720
|
|
|
721
|
+
def translate(
|
|
722
|
+
self,
|
|
723
|
+
text,
|
|
724
|
+
target_lang,
|
|
725
|
+
source_lang=None,
|
|
726
|
+
formality=None,
|
|
727
|
+
):
|
|
728
|
+
'''
|
|
729
|
+
Translates text into a target language with an LLM, preserving line
|
|
730
|
+
breaks, markup and placeholders in place. The response reports the
|
|
731
|
+
language the text was actually written in (detected, not assumed from
|
|
732
|
+
the hint).
|
|
733
|
+
|
|
734
|
+
Example::
|
|
735
|
+
|
|
736
|
+
client.translate(
|
|
737
|
+
'Hello world.\\nSee you at 5pm.',
|
|
738
|
+
target_lang='fr',
|
|
739
|
+
)
|
|
740
|
+
# {'translation': 'Bonjour le monde.\\nÀ 17h.',
|
|
741
|
+
# 'source_lang': 'en', 'source_lang_name': 'English',
|
|
742
|
+
# 'target_lang': 'fr', 'target_lang_name': 'French'}
|
|
743
|
+
|
|
744
|
+
:param text: Text to translate, up to 5,000 characters. Markup and
|
|
745
|
+
whitespace are preserved, not stripped.
|
|
746
|
+
:type text: str
|
|
747
|
+
:param target_lang: Language to translate into: an ISO 639 code
|
|
748
|
+
(``'fr'``, ``'zh-TW'``) or an English language name
|
|
749
|
+
(``'French'``). Region variants ``zh-CN``/``zh-TW``/``zh-HK``,
|
|
750
|
+
``pt-BR``/``pt-PT``, ``en-GB``/``en-US``, ``fr-CA`` and ``es-419``
|
|
751
|
+
are supported as targets.
|
|
752
|
+
:type target_lang: str
|
|
753
|
+
:param source_lang: Optional hint for the source language, same forms
|
|
754
|
+
as ``target_lang``. The text itself wins if they disagree.
|
|
755
|
+
:type source_lang: str
|
|
756
|
+
:param formality: Optional register: ``'formal'`` (vous/Sie/usted) or
|
|
757
|
+
``'informal'`` (tu/du/tú). Omit for the model default.
|
|
758
|
+
:type formality: str
|
|
759
|
+
:rtype: dict
|
|
760
|
+
:return:
|
|
761
|
+
- translation: The translated text, structure preserved.
|
|
762
|
+
- source_lang / source_lang_name: The detected source language
|
|
763
|
+
(``'und'`` and ``None`` when undetermined).
|
|
764
|
+
- target_lang / target_lang_name: The normalized target.
|
|
765
|
+
'''
|
|
766
|
+
url = self.url_endpoint + 'translate'
|
|
767
|
+
data = {
|
|
768
|
+
'key': self.api_key,
|
|
769
|
+
'text': text,
|
|
770
|
+
'target_lang': target_lang,
|
|
771
|
+
}
|
|
772
|
+
if source_lang is not None:
|
|
773
|
+
data['source_lang'] = source_lang
|
|
774
|
+
if formality is not None:
|
|
775
|
+
data['formality'] = formality
|
|
776
|
+
return self._request(url, data)
|
|
777
|
+
|
|
721
778
|
def tone(
|
|
722
779
|
self,
|
|
723
780
|
text,
|
|
@@ -869,6 +926,7 @@ class SaplingClient:
|
|
|
869
926
|
self,
|
|
870
927
|
text,
|
|
871
928
|
threshold=None,
|
|
929
|
+
spans=None,
|
|
872
930
|
):
|
|
873
931
|
'''
|
|
874
932
|
Scores the provided text on seven content-safety categories: toxicity,
|
|
@@ -879,12 +937,19 @@ class SaplingClient:
|
|
|
879
937
|
:param threshold: Score at or above which a category is flagged.
|
|
880
938
|
Between 0 and 1 inclusive; the API defaults to 0.5.
|
|
881
939
|
:type threshold: float
|
|
940
|
+
:param spans: When True, the response also includes the offending
|
|
941
|
+
passages as spans, each with its text, start/end character offsets
|
|
942
|
+
into the submitted text, per-passage category scores, and
|
|
943
|
+
flagged_categories. The API defaults to False.
|
|
944
|
+
:type spans: bool
|
|
882
945
|
:rtype: dict
|
|
883
946
|
:return:
|
|
884
947
|
- scores: A probability from 0 to 1 for each category.
|
|
885
948
|
- flagged: True if any category scored at or above the threshold.
|
|
886
949
|
- flagged_categories: Categories that scored at or above the threshold.
|
|
887
950
|
- threshold: The threshold that was applied.
|
|
951
|
+
- spans: Only with spans=True — the offending passages, most
|
|
952
|
+
severe first, as {text, start, end, scores, flagged_categories}.
|
|
888
953
|
'''
|
|
889
954
|
url = self.url_endpoint + 'safety'
|
|
890
955
|
data = {
|
|
@@ -893,6 +958,8 @@ class SaplingClient:
|
|
|
893
958
|
}
|
|
894
959
|
if threshold is not None:
|
|
895
960
|
data['threshold'] = threshold
|
|
961
|
+
if spans is not None:
|
|
962
|
+
data['spans'] = spans
|
|
896
963
|
return self._request(url, data)
|
|
897
964
|
|
|
898
965
|
def pii(
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '4.4.0'
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sapling-py
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.4.0
|
|
4
4
|
Summary: Sapling Python Client
|
|
5
|
-
|
|
6
|
-
Author: Sapling Intelligence
|
|
7
|
-
Author-email: info@sapling.ai
|
|
5
|
+
Author-email: Sapling Intelligence <info@sapling.ai>
|
|
8
6
|
License: Apache License 2.0
|
|
7
|
+
Project-URL: Homepage, https://sapling.ai
|
|
8
|
+
Project-URL: Documentation, https://sapling.readthedocs.io/
|
|
9
|
+
Project-URL: Repository, https://github.com/saplingai/sapling-py
|
|
9
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -17,18 +18,7 @@ Requires-Dist: requests
|
|
|
17
18
|
Provides-Extra: test
|
|
18
19
|
Requires-Dist: pytest; extra == "test"
|
|
19
20
|
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
21
|
Dynamic: license-file
|
|
28
|
-
Dynamic: provides-extra
|
|
29
|
-
Dynamic: requires-dist
|
|
30
|
-
Dynamic: requires-python
|
|
31
|
-
Dynamic: summary
|
|
32
22
|
|
|
33
23
|
# Sapling Python Client
|
|
34
24
|
|
|
@@ -41,8 +31,8 @@ Compare against leading grammar checking tools and APIs like Grammarly (Grammerl
|
|
|
41
31
|
|
|
42
32
|
Sapling is a toolkit for helping developers build language model-powered applications.
|
|
43
33
|
The API provides spelling and grammar checking, autocomplete, tone detection, rephrasing, AI detection,
|
|
44
|
-
zero-shot text classification, structured data extraction, PII detection and redaction,
|
|
45
|
-
scoring, SEO analysis, and more.
|
|
34
|
+
zero-shot text classification, structured data extraction, translation, PII detection and redaction,
|
|
35
|
+
content safety scoring, SEO analysis, and more.
|
|
46
36
|
|
|
47
37
|
Key Features:
|
|
48
38
|
- **60% More Corrections**: Outperforms [competing solutions](https://sapling.ai/comparison/api#sapling) at similar accuracy levels using state-of-the-art generative AI models.
|
|
@@ -54,12 +44,19 @@ Key Features:
|
|
|
54
44
|
|
|
55
45
|
## Installation
|
|
56
46
|
|
|
57
|
-
Install the `sapling-py` package with
|
|
47
|
+
Install the `sapling-py` package from PyPI with
|
|
48
|
+
[pip](https://pip.pypa.io/en/stable/installation/):
|
|
58
49
|
|
|
59
|
-
```
|
|
50
|
+
```sh
|
|
60
51
|
python -m pip install sapling-py
|
|
61
52
|
```
|
|
62
53
|
|
|
54
|
+
Or add it to a [uv](https://docs.astral.sh/uv/) project:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
uv add sapling-py
|
|
58
|
+
```
|
|
59
|
+
|
|
63
60
|
## Documentation
|
|
64
61
|
Documentation for the client is available at [Read the Docs](https://sapling.readthedocs.io/) and
|
|
65
62
|
documentation for the HTTP API is available at [Sapling.ai](https://sapling.ai/docs).
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
README.md
|
|
3
|
+
pyproject.toml
|
|
3
4
|
setup.py
|
|
4
5
|
sapling/__init__.py
|
|
5
6
|
sapling/client.py
|
|
@@ -14,4 +15,5 @@ test/test_client.py
|
|
|
14
15
|
test/test_extract.py
|
|
15
16
|
test/test_pii.py
|
|
16
17
|
test/test_safety.py
|
|
17
|
-
test/test_seo.py
|
|
18
|
+
test/test_seo.py
|
|
19
|
+
test/test_translate.py
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Compatibility entry point for tools that still invoke ``setup.py`` directly.
|
|
2
|
+
|
|
3
|
+
Modern build frontends use the metadata in ``pyproject.toml``. The explicit
|
|
4
|
+
fallback keeps direct invocations working with setuptools versions that predate
|
|
5
|
+
PEP 621 support.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import setuptools
|
|
11
|
+
from setuptools import find_packages, setup
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _legacy_setup_kwargs():
|
|
15
|
+
this_directory = Path(__file__).parent
|
|
16
|
+
version = {}
|
|
17
|
+
exec((this_directory / 'sapling/version.py').read_text(), version)
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
'name': 'sapling-py',
|
|
21
|
+
'version': version['__version__'],
|
|
22
|
+
'description': 'Sapling Python Client',
|
|
23
|
+
'long_description': (this_directory / 'README.md').read_text(),
|
|
24
|
+
'long_description_content_type': 'text/markdown',
|
|
25
|
+
'url': 'https://sapling.ai',
|
|
26
|
+
'author': 'Sapling Intelligence',
|
|
27
|
+
'author_email': 'info@sapling.ai',
|
|
28
|
+
'license': 'Apache License 2.0',
|
|
29
|
+
'classifiers': [
|
|
30
|
+
'Development Status :: 5 - Production/Stable',
|
|
31
|
+
'License :: OSI Approved :: Apache Software License',
|
|
32
|
+
'Programming Language :: Python :: 3',
|
|
33
|
+
'Programming Language :: Python :: 3 :: Only',
|
|
34
|
+
],
|
|
35
|
+
'packages': find_packages(exclude=['test', 'test.*']),
|
|
36
|
+
'include_package_data': True,
|
|
37
|
+
'python_requires': '>=3.7',
|
|
38
|
+
'install_requires': ['requests'],
|
|
39
|
+
'extras_require': {
|
|
40
|
+
'test': [
|
|
41
|
+
'pytest',
|
|
42
|
+
'responses',
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
setuptools_major_version = int(setuptools.__version__.partition('.')[0])
|
|
50
|
+
except (AttributeError, ValueError):
|
|
51
|
+
setuptools_major_version = 0
|
|
52
|
+
|
|
53
|
+
setup(**_legacy_setup_kwargs() if setuptools_major_version < 61 else {})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import responses
|
|
5
|
+
|
|
6
|
+
from sapling import SaplingClient
|
|
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
|
+
@responses.activate
|
|
18
|
+
def test_safety_threshold_sent_when_provided(client):
|
|
19
|
+
responses.add(responses.POST, BASE + 'safety', json={'flagged': False}, status=200)
|
|
20
|
+
client.safety('some text', threshold=0.8)
|
|
21
|
+
body = json.loads(responses.calls[-1].request.body)
|
|
22
|
+
assert body == {'key': API_KEY, 'text': 'some text', 'threshold': 0.8}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@responses.activate
|
|
26
|
+
def test_safety_threshold_omitted_by_default(client):
|
|
27
|
+
responses.add(responses.POST, BASE + 'safety', json={'flagged': False}, status=200)
|
|
28
|
+
client.safety('some text')
|
|
29
|
+
body = json.loads(responses.calls[-1].request.body)
|
|
30
|
+
assert 'threshold' not in body
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@responses.activate
|
|
34
|
+
def test_safety_spans_sent_when_provided(client):
|
|
35
|
+
responses.add(responses.POST, BASE + 'safety', json={'flagged': False}, status=200)
|
|
36
|
+
client.safety('some text', spans=True)
|
|
37
|
+
body = json.loads(responses.calls[-1].request.body)
|
|
38
|
+
assert body == {'key': API_KEY, 'text': 'some text', 'spans': True}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@responses.activate
|
|
42
|
+
def test_safety_spans_false_still_sent_explicitly(client):
|
|
43
|
+
# spans=False is a real value (not "unset"): send it rather than silently
|
|
44
|
+
# dropping it, mirroring the threshold-style None sentinel.
|
|
45
|
+
responses.add(responses.POST, BASE + 'safety', json={'flagged': False}, status=200)
|
|
46
|
+
client.safety('some text', spans=False)
|
|
47
|
+
body = json.loads(responses.calls[-1].request.body)
|
|
48
|
+
assert body['spans'] is False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@responses.activate
|
|
52
|
+
def test_safety_spans_omitted_by_default(client):
|
|
53
|
+
responses.add(responses.POST, BASE + 'safety', json={'flagged': False}, status=200)
|
|
54
|
+
client.safety('some text')
|
|
55
|
+
body = json.loads(responses.calls[-1].request.body)
|
|
56
|
+
assert 'spans' not in body
|
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
TEXT = 'Hello world.\nSee you at 5pm.'
|
|
12
|
+
|
|
13
|
+
TRANSLATE_RESPONSE = {
|
|
14
|
+
'translation': 'Bonjour le monde.\nÀ 17h.',
|
|
15
|
+
'source_lang': 'en',
|
|
16
|
+
'source_lang_name': 'English',
|
|
17
|
+
'target_lang': 'fr',
|
|
18
|
+
'target_lang_name': 'French',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def client():
|
|
24
|
+
return SaplingClient(api_key=API_KEY)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _last_request_body():
|
|
28
|
+
return json.loads(responses.calls[-1].request.body)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@responses.activate
|
|
32
|
+
def test_translate_returns_json_and_sends_key(client):
|
|
33
|
+
responses.add(responses.POST, BASE + 'translate',
|
|
34
|
+
json=TRANSLATE_RESPONSE, status=200)
|
|
35
|
+
result = client.translate(TEXT, target_lang='fr')
|
|
36
|
+
assert result == TRANSLATE_RESPONSE
|
|
37
|
+
body = _last_request_body()
|
|
38
|
+
assert body == {'key': API_KEY, 'text': TEXT, 'target_lang': 'fr'}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@responses.activate
|
|
42
|
+
def test_translate_posts_to_translate_endpoint(client):
|
|
43
|
+
responses.add(responses.POST, BASE + 'translate',
|
|
44
|
+
json=TRANSLATE_RESPONSE, status=200)
|
|
45
|
+
client.translate(TEXT, target_lang='fr')
|
|
46
|
+
assert responses.calls[-1].request.url == BASE + 'translate'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@responses.activate
|
|
50
|
+
def test_translate_optional_params_sent_only_when_given(client):
|
|
51
|
+
responses.add(responses.POST, BASE + 'translate',
|
|
52
|
+
json=TRANSLATE_RESPONSE, status=200)
|
|
53
|
+
client.translate(TEXT, target_lang='fr')
|
|
54
|
+
body = _last_request_body()
|
|
55
|
+
assert 'source_lang' not in body
|
|
56
|
+
assert 'formality' not in body
|
|
57
|
+
|
|
58
|
+
client.translate(TEXT, target_lang='fr',
|
|
59
|
+
source_lang='en', formality='formal')
|
|
60
|
+
body = _last_request_body()
|
|
61
|
+
assert body['source_lang'] == 'en'
|
|
62
|
+
assert body['formality'] == 'formal'
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@responses.activate
|
|
66
|
+
def test_translate_accepts_names_and_region_variants(client):
|
|
67
|
+
responses.add(responses.POST, BASE + 'translate',
|
|
68
|
+
json=dict(TRANSLATE_RESPONSE, target_lang='zh-TW',
|
|
69
|
+
target_lang_name='Traditional Chinese'),
|
|
70
|
+
status=200)
|
|
71
|
+
result = client.translate(TEXT, target_lang='Traditional Chinese')
|
|
72
|
+
assert result['target_lang'] == 'zh-TW'
|
|
73
|
+
assert _last_request_body()['target_lang'] == 'Traditional Chinese'
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@responses.activate
|
|
77
|
+
def test_translate_http_error_raises_sapling_error(client):
|
|
78
|
+
responses.add(responses.POST, BASE + 'translate',
|
|
79
|
+
json={'msg': 'Unknown language: "Klingon".'}, status=400)
|
|
80
|
+
with pytest.raises(SaplingError):
|
|
81
|
+
client.translate(TEXT, target_lang='Klingon')
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '4.2.0'
|
sapling_py-4.2.0/setup.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# Always prefer setuptools over distutils
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from setuptools import setup, find_packages
|
|
4
|
-
|
|
5
|
-
this_directory = Path(__file__).parent
|
|
6
|
-
print(this_directory / 'sapling/version.py')
|
|
7
|
-
print(this_directory / "README.md")
|
|
8
|
-
# load module version
|
|
9
|
-
exec(open(this_directory / 'sapling/version.py').read())
|
|
10
|
-
long_description = (this_directory / "README.md").read_text()
|
|
11
|
-
|
|
12
|
-
setup(
|
|
13
|
-
name='sapling-py',
|
|
14
|
-
version=__version__,
|
|
15
|
-
description='Sapling Python Client',
|
|
16
|
-
long_description=long_description,
|
|
17
|
-
long_description_content_type='text/markdown',
|
|
18
|
-
url='https://sapling.ai',
|
|
19
|
-
author='Sapling Intelligence',
|
|
20
|
-
author_email='info@sapling.ai',
|
|
21
|
-
license='Apache License 2.0',
|
|
22
|
-
classifiers=[
|
|
23
|
-
'Development Status :: 5 - Production/Stable',
|
|
24
|
-
'License :: OSI Approved :: Apache Software License',
|
|
25
|
-
'Programming Language :: Python :: 3',
|
|
26
|
-
'Programming Language :: Python :: 3 :: Only',
|
|
27
|
-
],
|
|
28
|
-
packages=find_packages(exclude=['test', 'test.*']),
|
|
29
|
-
package_data={'':[]},
|
|
30
|
-
include_package_data=True,
|
|
31
|
-
python_requires='>=3.7',
|
|
32
|
-
install_requires=[
|
|
33
|
-
'requests'
|
|
34
|
-
],
|
|
35
|
-
extras_require={
|
|
36
|
-
'test': [
|
|
37
|
-
'pytest',
|
|
38
|
-
'responses',
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
import responses
|
|
5
|
-
|
|
6
|
-
from sapling import SaplingClient
|
|
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
|
-
@responses.activate
|
|
18
|
-
def test_safety_threshold_sent_when_provided(client):
|
|
19
|
-
responses.add(responses.POST, BASE + 'safety', json={'flagged': False}, status=200)
|
|
20
|
-
client.safety('some text', threshold=0.8)
|
|
21
|
-
body = json.loads(responses.calls[-1].request.body)
|
|
22
|
-
assert body == {'key': API_KEY, 'text': 'some text', 'threshold': 0.8}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@responses.activate
|
|
26
|
-
def test_safety_threshold_omitted_by_default(client):
|
|
27
|
-
responses.add(responses.POST, BASE + 'safety', json={'flagged': False}, status=200)
|
|
28
|
-
client.safety('some text')
|
|
29
|
-
body = json.loads(responses.calls[-1].request.body)
|
|
30
|
-
assert 'threshold' not in body
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|