daktilo 0.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.
- daktilo-0.1.0/LICENSE +9 -0
- daktilo-0.1.0/PKG-INFO +81 -0
- daktilo-0.1.0/README.md +66 -0
- daktilo-0.1.0/pyproject.toml +27 -0
- daktilo-0.1.0/setup.cfg +4 -0
- daktilo-0.1.0/src/daktilo/__init__.py +3 -0
- daktilo-0.1.0/src/daktilo/cli.py +28 -0
- daktilo-0.1.0/src/daktilo/client.py +47 -0
- daktilo-0.1.0/src/daktilo/py.typed +0 -0
- daktilo-0.1.0/src/daktilo.egg-info/PKG-INFO +81 -0
- daktilo-0.1.0/src/daktilo.egg-info/SOURCES.txt +14 -0
- daktilo-0.1.0/src/daktilo.egg-info/dependency_links.txt +1 -0
- daktilo-0.1.0/src/daktilo.egg-info/entry_points.txt +2 -0
- daktilo-0.1.0/src/daktilo.egg-info/requires.txt +1 -0
- daktilo-0.1.0/src/daktilo.egg-info/top_level.txt +1 -0
- daktilo-0.1.0/tests/test_client.py +43 -0
daktilo-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Omneity Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
daktilo-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: daktilo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Transcribe audio recordings with Sawalni API
|
|
5
|
+
Author-email: Omar Kamali <omar@omneitylabs.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://api.sawalni.com
|
|
8
|
+
Project-URL: Documentation, https://api.sawalni.com/docs
|
|
9
|
+
Project-URL: Examples, https://github.com/omneity-labs/starter-transcription
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: httpx<1,>=0.27
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# Daktilo
|
|
17
|
+
|
|
18
|
+
**Multilingual audio transcription, with Darija and Amazigh support.**
|
|
19
|
+
|
|
20
|
+
Turn an audio recording into text with [Sawalni API](https://api.sawalni.com). Use it for interviews, voice notes or the transcript step of a caption workflow. Built by [Omneity Labs](https://www.omneitylabs.com).
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
pip install daktilo
|
|
26
|
+
export SAWALNI_API_KEY="your-key"
|
|
27
|
+
daktilo transcribe interview.wav --output transcript.txt
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
[Create an API key](https://api.sawalni.com) and enable transcription access. Requests use API credits. This package calls the hosted service; it does not run a model locally.
|
|
31
|
+
|
|
32
|
+
## Python
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from daktilo import Daktilo
|
|
37
|
+
|
|
38
|
+
with Daktilo() as client:
|
|
39
|
+
result = client.transcribe("interview.wav")
|
|
40
|
+
|
|
41
|
+
Path("transcript.txt").write_text(result["text"], encoding="utf-8")
|
|
42
|
+
print(result.get("language"))
|
|
43
|
+
print(result.get("warnings", []))
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The response preserves the service's text, detected language, segments, warnings and usage when available. Review the words before using the transcript for captions or another downstream task.
|
|
47
|
+
|
|
48
|
+
## Languages
|
|
49
|
+
|
|
50
|
+
Daktilo supports Darija, Amazigh and major languages including Arabic, English, French, Spanish, German, Italian, Portuguese, Chinese, Japanese, Korean, Russian, Turkish and Dutch. Leave `language` unset for automatic selection, especially for Amazigh and mixed-language audio recordings.
|
|
51
|
+
|
|
52
|
+
The API currently accepts forced hints `ar`, `ary`, `en`, `fr`, `es`, `de`, `it`, `pt`, `zh`, `ja`, `ko`, `ru`, `tr` and `nl`. A forced Amazigh hint such as `shi` is not accepted; use automatic selection. Language availability does not imply the same accuracy for every variety, accent or audio recording.
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
daktilo transcribe interview.wav --language ary --json --output result.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
with Daktilo() as client:
|
|
60
|
+
result = client.transcribe("interview.wav", language="ary")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## From transcript to timed captions
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
Audio recording → Daktilo → Review transcript → Sawalign → SRT captions
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Need word timings or subtitles? [Sawalign](https://pypi.org/project/sawalign/) aligns your reviewed transcript to the same audio recording. The [Python and TypeScript starter](https://github.com/omneity-labs/starter-transcription) covers the full workflow.
|
|
70
|
+
|
|
71
|
+
## Client reference
|
|
72
|
+
|
|
73
|
+
`Daktilo(api_key=None, base_url="https://api.sawalni.com/v1", timeout=300)` uses `SAWALNI_API_KEY` when no key is supplied. Use it as a context manager or call `close()`.
|
|
74
|
+
|
|
75
|
+
`transcribe(audio, language=None, model="daktilo")` accepts a local file path and returns the response dictionary. The default model follows the service's Daktilo alias. HTTP errors raise `httpx.HTTPStatusError`; connection failures raise `httpx.RequestError`. Uploads are not automatically retried.
|
|
76
|
+
|
|
77
|
+
Audio recordings must be at most 128 MiB and 30 minutes. The service validates duration. Inspect `warnings` and any segment truncation flags before downstream use. Empty recognized text can be a valid result for silence. The client preserves it.
|
|
78
|
+
|
|
79
|
+
Keep API keys in your terminal or server environment. Python 3.10 or later. MIT license.
|
|
80
|
+
|
|
81
|
+
[API reference](https://api.sawalni.com/docs) · [Runnable starter](https://github.com/omneity-labs/starter-transcription)
|
daktilo-0.1.0/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Daktilo
|
|
2
|
+
|
|
3
|
+
**Multilingual audio transcription, with Darija and Amazigh support.**
|
|
4
|
+
|
|
5
|
+
Turn an audio recording into text with [Sawalni API](https://api.sawalni.com). Use it for interviews, voice notes or the transcript step of a caption workflow. Built by [Omneity Labs](https://www.omneitylabs.com).
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pip install daktilo
|
|
11
|
+
export SAWALNI_API_KEY="your-key"
|
|
12
|
+
daktilo transcribe interview.wav --output transcript.txt
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
[Create an API key](https://api.sawalni.com) and enable transcription access. Requests use API credits. This package calls the hosted service; it does not run a model locally.
|
|
16
|
+
|
|
17
|
+
## Python
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from daktilo import Daktilo
|
|
22
|
+
|
|
23
|
+
with Daktilo() as client:
|
|
24
|
+
result = client.transcribe("interview.wav")
|
|
25
|
+
|
|
26
|
+
Path("transcript.txt").write_text(result["text"], encoding="utf-8")
|
|
27
|
+
print(result.get("language"))
|
|
28
|
+
print(result.get("warnings", []))
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The response preserves the service's text, detected language, segments, warnings and usage when available. Review the words before using the transcript for captions or another downstream task.
|
|
32
|
+
|
|
33
|
+
## Languages
|
|
34
|
+
|
|
35
|
+
Daktilo supports Darija, Amazigh and major languages including Arabic, English, French, Spanish, German, Italian, Portuguese, Chinese, Japanese, Korean, Russian, Turkish and Dutch. Leave `language` unset for automatic selection, especially for Amazigh and mixed-language audio recordings.
|
|
36
|
+
|
|
37
|
+
The API currently accepts forced hints `ar`, `ary`, `en`, `fr`, `es`, `de`, `it`, `pt`, `zh`, `ja`, `ko`, `ru`, `tr` and `nl`. A forced Amazigh hint such as `shi` is not accepted; use automatic selection. Language availability does not imply the same accuracy for every variety, accent or audio recording.
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
daktilo transcribe interview.wav --language ary --json --output result.json
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
with Daktilo() as client:
|
|
45
|
+
result = client.transcribe("interview.wav", language="ary")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## From transcript to timed captions
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
Audio recording → Daktilo → Review transcript → Sawalign → SRT captions
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Need word timings or subtitles? [Sawalign](https://pypi.org/project/sawalign/) aligns your reviewed transcript to the same audio recording. The [Python and TypeScript starter](https://github.com/omneity-labs/starter-transcription) covers the full workflow.
|
|
55
|
+
|
|
56
|
+
## Client reference
|
|
57
|
+
|
|
58
|
+
`Daktilo(api_key=None, base_url="https://api.sawalni.com/v1", timeout=300)` uses `SAWALNI_API_KEY` when no key is supplied. Use it as a context manager or call `close()`.
|
|
59
|
+
|
|
60
|
+
`transcribe(audio, language=None, model="daktilo")` accepts a local file path and returns the response dictionary. The default model follows the service's Daktilo alias. HTTP errors raise `httpx.HTTPStatusError`; connection failures raise `httpx.RequestError`. Uploads are not automatically retried.
|
|
61
|
+
|
|
62
|
+
Audio recordings must be at most 128 MiB and 30 minutes. The service validates duration. Inspect `warnings` and any segment truncation flags before downstream use. Empty recognized text can be a valid result for silence. The client preserves it.
|
|
63
|
+
|
|
64
|
+
Keep API keys in your terminal or server environment. Python 3.10 or later. MIT license.
|
|
65
|
+
|
|
66
|
+
[API reference](https://api.sawalni.com/docs) · [Runnable starter](https://github.com/omneity-labs/starter-transcription)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "daktilo"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Transcribe audio recordings with Sawalni API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{name = "Omar Kamali", email = "omar@omneitylabs.com"}]
|
|
13
|
+
dependencies = ["httpx>=0.27,<1"]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
daktilo = "daktilo.cli:main"
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Homepage = "https://api.sawalni.com"
|
|
20
|
+
Documentation = "https://api.sawalni.com/docs"
|
|
21
|
+
Examples = "https://github.com/omneity-labs/starter-transcription"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["src"]
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.package-data]
|
|
27
|
+
daktilo = ["py.typed"]
|
daktilo-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from . import Daktilo
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
parser=argparse.ArgumentParser(description="Transcribe an audio recording with Daktilo")
|
|
10
|
+
sub=parser.add_subparsers(dest="command",required=True)
|
|
11
|
+
cmd=sub.add_parser("transcribe")
|
|
12
|
+
cmd.add_argument("audio",type=Path)
|
|
13
|
+
cmd.add_argument("--language",help="Optional language hint; omit for automatic selection")
|
|
14
|
+
cmd.add_argument("--model",default="daktilo")
|
|
15
|
+
cmd.add_argument("--output",type=Path)
|
|
16
|
+
cmd.add_argument("--json",action="store_true",dest="as_json")
|
|
17
|
+
args=parser.parse_args()
|
|
18
|
+
with Daktilo() as client:
|
|
19
|
+
result=client.transcribe(args.audio,language=args.language,model=args.model)
|
|
20
|
+
output=json.dumps(result,ensure_ascii=False,indent=2) if args.as_json else result["text"]
|
|
21
|
+
if args.output:
|
|
22
|
+
args.output.write_text(output,encoding="utf-8")
|
|
23
|
+
else:
|
|
24
|
+
print(output)
|
|
25
|
+
for warning in result.get("warnings",[]):
|
|
26
|
+
print("Warning:",warning,file=sys.stderr)
|
|
27
|
+
if result.get("truncated") or any(x.get("truncated") for x in result.get("segments",[]) if isinstance(x,dict)):
|
|
28
|
+
print("Warning: transcription is incomplete; review before alignment.",file=sys.stderr)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Daktilo:
|
|
8
|
+
"""Audio transcription through Sawalni API. Uploads are not retried."""
|
|
9
|
+
def __init__(self, api_key: str | None = None, *,
|
|
10
|
+
base_url: str = "https://api.sawalni.com/v1",
|
|
11
|
+
timeout: float = 300, transport: httpx.BaseTransport | None = None):
|
|
12
|
+
key = api_key or os.environ.get("SAWALNI_API_KEY")
|
|
13
|
+
if not key:
|
|
14
|
+
raise ValueError("Set SAWALNI_API_KEY or pass api_key")
|
|
15
|
+
self._http = httpx.Client(base_url=base_url.rstrip("/")+"/",
|
|
16
|
+
headers={"Authorization":f"Bearer {key}"}, timeout=timeout,
|
|
17
|
+
transport=transport, follow_redirects=False)
|
|
18
|
+
|
|
19
|
+
def close(self) -> None:
|
|
20
|
+
self._http.close()
|
|
21
|
+
|
|
22
|
+
def __enter__(self):
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def __exit__(self, *args):
|
|
26
|
+
self.close()
|
|
27
|
+
|
|
28
|
+
def transcribe(self, audio: str | Path, *, language: str | None = None,
|
|
29
|
+
model: str = "daktilo") -> dict[str, Any]:
|
|
30
|
+
"""Return text and the service's language, segments, warnings and usage."""
|
|
31
|
+
if language is not None and not language.strip():
|
|
32
|
+
raise ValueError("language must not be empty; omit it for automatic selection")
|
|
33
|
+
path = Path(audio)
|
|
34
|
+
data = {"model":model}
|
|
35
|
+
if language is not None:
|
|
36
|
+
data["language"] = language
|
|
37
|
+
with path.open("rb") as stream:
|
|
38
|
+
size = os.fstat(stream.fileno()).st_size
|
|
39
|
+
if size == 0 or size > 128*1024*1024:
|
|
40
|
+
raise ValueError("Audio recording must contain 1 byte to 128 MiB")
|
|
41
|
+
response = self._http.post("speech/transcribe",
|
|
42
|
+
files={"audio":(path.name,stream,"application/octet-stream")},data=data)
|
|
43
|
+
response.raise_for_status()
|
|
44
|
+
result = response.json()
|
|
45
|
+
if not isinstance(result,dict) or not isinstance(result.get("text"),str):
|
|
46
|
+
raise ValueError("Invalid transcription response: missing text")
|
|
47
|
+
return result
|
|
File without changes
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: daktilo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Transcribe audio recordings with Sawalni API
|
|
5
|
+
Author-email: Omar Kamali <omar@omneitylabs.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://api.sawalni.com
|
|
8
|
+
Project-URL: Documentation, https://api.sawalni.com/docs
|
|
9
|
+
Project-URL: Examples, https://github.com/omneity-labs/starter-transcription
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: httpx<1,>=0.27
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# Daktilo
|
|
17
|
+
|
|
18
|
+
**Multilingual audio transcription, with Darija and Amazigh support.**
|
|
19
|
+
|
|
20
|
+
Turn an audio recording into text with [Sawalni API](https://api.sawalni.com). Use it for interviews, voice notes or the transcript step of a caption workflow. Built by [Omneity Labs](https://www.omneitylabs.com).
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
pip install daktilo
|
|
26
|
+
export SAWALNI_API_KEY="your-key"
|
|
27
|
+
daktilo transcribe interview.wav --output transcript.txt
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
[Create an API key](https://api.sawalni.com) and enable transcription access. Requests use API credits. This package calls the hosted service; it does not run a model locally.
|
|
31
|
+
|
|
32
|
+
## Python
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from daktilo import Daktilo
|
|
37
|
+
|
|
38
|
+
with Daktilo() as client:
|
|
39
|
+
result = client.transcribe("interview.wav")
|
|
40
|
+
|
|
41
|
+
Path("transcript.txt").write_text(result["text"], encoding="utf-8")
|
|
42
|
+
print(result.get("language"))
|
|
43
|
+
print(result.get("warnings", []))
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The response preserves the service's text, detected language, segments, warnings and usage when available. Review the words before using the transcript for captions or another downstream task.
|
|
47
|
+
|
|
48
|
+
## Languages
|
|
49
|
+
|
|
50
|
+
Daktilo supports Darija, Amazigh and major languages including Arabic, English, French, Spanish, German, Italian, Portuguese, Chinese, Japanese, Korean, Russian, Turkish and Dutch. Leave `language` unset for automatic selection, especially for Amazigh and mixed-language audio recordings.
|
|
51
|
+
|
|
52
|
+
The API currently accepts forced hints `ar`, `ary`, `en`, `fr`, `es`, `de`, `it`, `pt`, `zh`, `ja`, `ko`, `ru`, `tr` and `nl`. A forced Amazigh hint such as `shi` is not accepted; use automatic selection. Language availability does not imply the same accuracy for every variety, accent or audio recording.
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
daktilo transcribe interview.wav --language ary --json --output result.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
with Daktilo() as client:
|
|
60
|
+
result = client.transcribe("interview.wav", language="ary")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## From transcript to timed captions
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
Audio recording → Daktilo → Review transcript → Sawalign → SRT captions
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Need word timings or subtitles? [Sawalign](https://pypi.org/project/sawalign/) aligns your reviewed transcript to the same audio recording. The [Python and TypeScript starter](https://github.com/omneity-labs/starter-transcription) covers the full workflow.
|
|
70
|
+
|
|
71
|
+
## Client reference
|
|
72
|
+
|
|
73
|
+
`Daktilo(api_key=None, base_url="https://api.sawalni.com/v1", timeout=300)` uses `SAWALNI_API_KEY` when no key is supplied. Use it as a context manager or call `close()`.
|
|
74
|
+
|
|
75
|
+
`transcribe(audio, language=None, model="daktilo")` accepts a local file path and returns the response dictionary. The default model follows the service's Daktilo alias. HTTP errors raise `httpx.HTTPStatusError`; connection failures raise `httpx.RequestError`. Uploads are not automatically retried.
|
|
76
|
+
|
|
77
|
+
Audio recordings must be at most 128 MiB and 30 minutes. The service validates duration. Inspect `warnings` and any segment truncation flags before downstream use. Empty recognized text can be a valid result for silence. The client preserves it.
|
|
78
|
+
|
|
79
|
+
Keep API keys in your terminal or server environment. Python 3.10 or later. MIT license.
|
|
80
|
+
|
|
81
|
+
[API reference](https://api.sawalni.com/docs) · [Runnable starter](https://github.com/omneity-labs/starter-transcription)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/daktilo/__init__.py
|
|
5
|
+
src/daktilo/cli.py
|
|
6
|
+
src/daktilo/client.py
|
|
7
|
+
src/daktilo/py.typed
|
|
8
|
+
src/daktilo.egg-info/PKG-INFO
|
|
9
|
+
src/daktilo.egg-info/SOURCES.txt
|
|
10
|
+
src/daktilo.egg-info/dependency_links.txt
|
|
11
|
+
src/daktilo.egg-info/entry_points.txt
|
|
12
|
+
src/daktilo.egg-info/requires.txt
|
|
13
|
+
src/daktilo.egg-info/top_level.txt
|
|
14
|
+
tests/test_client.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpx<1,>=0.27
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
daktilo
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
import unittest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import httpx
|
|
5
|
+
from daktilo import Daktilo
|
|
6
|
+
|
|
7
|
+
class Tests(unittest.TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
self.tmp=tempfile.TemporaryDirectory();self.addCleanup(self.tmp.cleanup)
|
|
10
|
+
self.path=Path(self.tmp.name)/"test.wav";self.path.write_bytes(b"RIFF-fixture")
|
|
11
|
+
def client(self,handler):
|
|
12
|
+
c=Daktilo("fixture-key",transport=httpx.MockTransport(handler));self.addCleanup(c.close);return c
|
|
13
|
+
def test_response_and_multipart(self):
|
|
14
|
+
def handler(req):
|
|
15
|
+
self.assertEqual(req.url.path,"/v1/speech/transcribe")
|
|
16
|
+
self.assertEqual(req.headers["Authorization"],"Bearer fixture-key")
|
|
17
|
+
self.assertIn(b"RIFF-fixture",req.content);self.assertIn(b"daktilo",req.content)
|
|
18
|
+
self.assertNotIn(b'name="language"',req.content)
|
|
19
|
+
return httpx.Response(200,json={"text":"أزول", "language":"shi", "warnings":["example"],"segments":[]})
|
|
20
|
+
result=self.client(handler).transcribe(self.path)
|
|
21
|
+
self.assertEqual(result["text"],"أزول");self.assertEqual(result["warnings"],["example"])
|
|
22
|
+
def test_hint(self):
|
|
23
|
+
def handler(req):
|
|
24
|
+
self.assertIn(b'name="language"',req.content);self.assertIn(b"ary",req.content)
|
|
25
|
+
return httpx.Response(200,json={"text":"سلام"})
|
|
26
|
+
self.assertEqual(self.client(handler).transcribe(self.path,language="ary")["text"],"سلام")
|
|
27
|
+
def test_no_retry(self):
|
|
28
|
+
seen=[]
|
|
29
|
+
def handler(req):seen.append(req);return httpx.Response(503)
|
|
30
|
+
with self.assertRaises(httpx.HTTPStatusError):self.client(handler).transcribe(self.path)
|
|
31
|
+
self.assertEqual(len(seen),1)
|
|
32
|
+
def test_silence_preserved(self):
|
|
33
|
+
self.assertEqual(self.client(lambda req:httpx.Response(200,json={"text":""})).transcribe(self.path),{"text":""})
|
|
34
|
+
def test_invalid_response(self):
|
|
35
|
+
with self.assertRaises(ValueError):self.client(lambda req:httpx.Response(200,json={})).transcribe(self.path)
|
|
36
|
+
def test_empty_file(self):
|
|
37
|
+
self.path.write_bytes(b"")
|
|
38
|
+
with self.assertRaises(ValueError):self.client(lambda req:self.fail()).transcribe(self.path)
|
|
39
|
+
def test_redirect(self):
|
|
40
|
+
with self.assertRaises(httpx.HTTPStatusError):self.client(lambda req:httpx.Response(302,headers={"Location":"https://other.invalid"})).transcribe(self.path)
|
|
41
|
+
def test_invalid_hint(self):
|
|
42
|
+
with self.assertRaises(ValueError):self.client(lambda req:self.fail()).transcribe(self.path,language=" ")
|
|
43
|
+
if __name__=="__main__":unittest.main()
|