daktilo 0.1.0__py3-none-any.whl

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/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .client import Daktilo
2
+ __version__ = "0.1.0"
3
+ __all__ = ["Daktilo"]
daktilo/cli.py 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)
daktilo/client.py ADDED
@@ -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
daktilo/py.typed ADDED
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,10 @@
1
+ daktilo/__init__.py,sha256=_Rdx897oevUuZQwV_Yxd6pDsHVE_9JTD54daCtYuYS4,72
2
+ daktilo/cli.py,sha256=o2fkQk7r5qYJ_ezox-wTWzXiNloc1XylRIXcc9ceN6w,1238
3
+ daktilo/client.py,sha256=CrGxKYot9wTHPaylNx3xWUwJF6KQf4JnUMyhMSxGTes,1997
4
+ daktilo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ daktilo-0.1.0.dist-info/licenses/LICENSE,sha256=nRQxaiQTOi3f1G6BfpYfKOMwtIj7mM6gREvd8qyjIqY,1069
6
+ daktilo-0.1.0.dist-info/METADATA,sha256=2ZwuEqbp0p6lQY5AbaStHa8siKjgd0dimo-eX7LvWto,3738
7
+ daktilo-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
8
+ daktilo-0.1.0.dist-info/entry_points.txt,sha256=1IG0G91B6HhqrRyGuH8XPOS-m7nqsYcTj-5_llNBCXk,45
9
+ daktilo-0.1.0.dist-info/top_level.txt,sha256=S6mY3ohN06GwkKzJnpNI5ubguQ-F2GYAsSR0cDMdzqw,8
10
+ daktilo-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ daktilo = daktilo.cli:main
@@ -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.
@@ -0,0 +1 @@
1
+ daktilo