translatarr-client 0.2.1__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,208 @@
1
+ # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
2
+
3
+ # Logs
4
+
5
+ logs
6
+ _.log
7
+ npm-debug.log_
8
+ yarn-debug.log*
9
+ yarn-error.log*
10
+ lerna-debug.log*
11
+ .pnpm-debug.log*
12
+
13
+ # Caches
14
+
15
+ .cache
16
+
17
+ # Diagnostic reports (https://nodejs.org/api/report.html)
18
+
19
+ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
20
+
21
+ # Runtime data
22
+
23
+ pids
24
+ _.pid
25
+ _.seed
26
+ *.pid.lock
27
+
28
+ # Directory for instrumented libs generated by jscoverage/JSCover
29
+
30
+ lib-cov
31
+
32
+ # Coverage directory used by tools like istanbul
33
+
34
+ coverage
35
+ *.lcov
36
+
37
+ # nyc test coverage
38
+
39
+ .nyc_output
40
+
41
+ # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
42
+
43
+ .grunt
44
+
45
+ # Bower dependency directory (https://bower.io/)
46
+
47
+ bower_components
48
+
49
+ # node-waf configuration
50
+
51
+ .lock-wscript
52
+
53
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
54
+
55
+ build/Release
56
+
57
+ # Dependency directories
58
+
59
+ node_modules/
60
+ jspm_packages/
61
+
62
+ # Snowpack dependency directory (https://snowpack.dev/)
63
+
64
+ web_modules/
65
+
66
+ # TypeScript cache
67
+
68
+ *.tsbuildinfo
69
+
70
+ # Optional npm cache directory
71
+
72
+ .npm
73
+
74
+ # Optional eslint cache
75
+
76
+ .eslintcache
77
+
78
+ # Optional stylelint cache
79
+
80
+ .stylelintcache
81
+
82
+ # Microbundle cache
83
+
84
+ .rpt2_cache/
85
+ .rts2_cache_cjs/
86
+ .rts2_cache_es/
87
+ .rts2_cache_umd/
88
+
89
+ # Optional REPL history
90
+
91
+ .node_repl_history
92
+
93
+ # Output of 'npm pack'
94
+
95
+ *.tgz
96
+
97
+ # Yarn Integrity file
98
+
99
+ .yarn-integrity
100
+
101
+ # dotenv environment variable files
102
+
103
+ .env
104
+ .env.development.local
105
+ .env.test.local
106
+ .env.production.local
107
+ .env.local
108
+
109
+ # parcel-bundler cache (https://parceljs.org/)
110
+
111
+ .parcel-cache
112
+
113
+ # Next.js build output
114
+
115
+ .next
116
+ out
117
+ next-env.d.ts
118
+
119
+ # Nuxt.js build / generate output
120
+
121
+ .nuxt
122
+ dist
123
+
124
+ # Gatsby files
125
+
126
+ # Comment in the public line in if your project uses Gatsby and not Next.js
127
+
128
+ # https://nextjs.org/blog/next-9-1#public-directory-support
129
+
130
+ # public
131
+
132
+ # vuepress build output
133
+
134
+ .vuepress/dist
135
+
136
+ # vuepress v2.x temp and cache directory
137
+
138
+ .temp
139
+
140
+ # Docusaurus cache and generated files
141
+
142
+ .docusaurus
143
+
144
+ # Serverless directories
145
+
146
+ .serverless/
147
+
148
+ # FuseBox cache
149
+
150
+ .fusebox/
151
+
152
+ # DynamoDB Local files
153
+
154
+ .dynamodb/
155
+
156
+ # TernJS port file
157
+
158
+ .tern-port
159
+
160
+ # Stores VSCode versions used for testing VSCode extensions
161
+
162
+ .vscode-test
163
+
164
+ # yarn v2
165
+
166
+ .yarn/cache
167
+ .yarn/unplugged
168
+ .yarn/build-state.yml
169
+ .yarn/install-state.gz
170
+ .pnp.*
171
+
172
+ # IntelliJ based IDEs
173
+ .idea
174
+
175
+ # Finder (MacOS) folder config
176
+ .DS_Store
177
+
178
+
179
+ # app data
180
+ data
181
+
182
+ # generated Swagger UI assets (copied from swagger-ui-dist at dev/build time)
183
+ .swagger-ui
184
+
185
+ clients/python/.venv
186
+ clients/python/openapi.json
187
+
188
+ # TypeScript client: regenerated spec, build output, deps, packed tarballs
189
+ clients/typescript/openapi.json
190
+ clients/typescript/dist/
191
+ clients/typescript/node_modules/
192
+ clients/typescript/*.tgz
193
+
194
+ # Rust client: regenerated spec/schema, build output, lockfile (library crate)
195
+ clients/rust/openapi.json
196
+ clients/rust/schema.json
197
+ clients/rust/target/
198
+ clients/rust/Cargo.lock
199
+ __pycache__
200
+ .venv/
201
+ __pycache__/
202
+ *.pyc
203
+ *.egg-info/
204
+ build/
205
+ dist/
206
+ .mypy_cache/
207
+ .pytest_cache/
208
+ .ruff_cache/
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.4
2
+ Name: translatarr-client
3
+ Version: 0.2.1
4
+ Summary: Synchronous and asynchronous Python client for the Translatarr API.
5
+ Author: joshrmcdaniel
6
+ License: AGPL-3.0-or-later
7
+ Keywords: api,client,llm,translatarr,translation
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: httpx>=0.24
10
+ Requires-Dist: pydantic>=2.4
11
+ Provides-Extra: codegen
12
+ Requires-Dist: datamodel-code-generator>=0.65; extra == 'codegen'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # translatarr-client
16
+
17
+ A typed Python client for the [Translatarr](../../README.md) API — synchronous
18
+ and asynchronous, with pydantic response models generated from the server's
19
+ OpenAPI spec.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install translatarr-client
25
+ ```
26
+
27
+ Published to [PyPI](https://pypi.org/project/translatarr-client/) for each `v*`
28
+ tag (the version matches the app's tag). The same wheel + sdist are also
29
+ attached to the corresponding GitHub Release, and you can install straight from
30
+ this repo:
31
+
32
+ ```bash
33
+ # from a published GitHub Release (version matches the app's v* tag)
34
+ pip install https://github.com/joshrmcdaniel/translatarr/releases/download/v1.2.3/translatarr_client-1.2.3-py3-none-any.whl
35
+
36
+ # or straight from this repo
37
+ pip install -e clients/python
38
+ ```
39
+
40
+ Requires Python 3.10+.
41
+
42
+ ## Authentication
43
+
44
+ Mint a personal API key in the web UI under **Settings → API keys** (tokens are
45
+ prefixed `tra_` and shown only once). Pass it as `token`:
46
+
47
+ ```python
48
+ from translatarr import TranslatarrClient
49
+
50
+ with TranslatarrClient("https://translatarr.example", token="tra_…") as tra:
51
+ result = tra.translate("Good morning", source_lang="en", target_lang="ja")
52
+ best = result.translations[0]
53
+ print(best.text, best.romanization)
54
+ ```
55
+
56
+ A browser session cookie works too, via `session_cookie="…"` instead of `token`.
57
+
58
+ ## Async
59
+
60
+ `AsyncTranslatarrClient` mirrors the sync client method-for-method:
61
+
62
+ ```python
63
+ import asyncio
64
+ from translatarr import AsyncTranslatarrClient
65
+
66
+ async def main() -> None:
67
+ async with AsyncTranslatarrClient("https://translatarr.example", token="tra_…") as tra:
68
+ result = await tra.translate("Bonjour", source_lang="auto", target_lang="en")
69
+ print(result.detected_source_language, result.translations[0].text)
70
+
71
+ asyncio.run(main())
72
+ ```
73
+
74
+ ## What you can do
75
+
76
+ | Area | Methods |
77
+ | --------- | ------- |
78
+ | Translate | `translate` |
79
+ | Chats | `list_chats`, `create_chat`, `get_chat`, `rename_chat`, `clear_chat`, `delete_chat` |
80
+ | Turns | `add_turn`, `select_option`, `retranslate_turn`, `switch_branch` |
81
+ | Speech | `transcribe`, `synthesize` |
82
+ | API keys | `list_keys`, `create_key`, `revoke_key` |
83
+
84
+ ### Persisting a chat without paying for a second translation
85
+
86
+ `translate` does not store anything; `add_turn` translates *and* persists. To
87
+ avoid a duplicate LLM call, pass the `result` you already have:
88
+
89
+ ```python
90
+ chat = tra.create_chat(source_lang="en", target_lang="ja")
91
+ preview = tra.translate("Let's ship it", source_lang="en", target_lang="ja", chat_id=chat.id)
92
+ chat = tra.add_turn(chat.id, "Let's ship it", source_lang="en", target_lang="ja", result=preview)
93
+ ```
94
+
95
+ ### Errors
96
+
97
+ Failures raise a subclass of `translatarr.APIError`, each carrying `status_code`,
98
+ the server's `code` (when present), and the raw `response`:
99
+
100
+ | Exception | Status |
101
+ | --------- | ------ |
102
+ | `InvalidRequestError` | 400 |
103
+ | `AuthenticationError` | 401 |
104
+ | `ForbiddenError` | 403 |
105
+ | `NotFoundError` | 404 |
106
+ | `ConflictError` | 409 |
107
+ | `MalformedResponseError`| 422 |
108
+ | `ProviderError` | 502 |
109
+
110
+ ```python
111
+ from translatarr import NotFoundError
112
+
113
+ try:
114
+ tra.get_chat("does-not-exist")
115
+ except NotFoundError:
116
+ ...
117
+ ```
118
+
119
+ ## Development
120
+
121
+ The response models in `src/translatarr/_models.py` are **generated** from the
122
+ server's OpenAPI document — do not edit them by hand. To regenerate after the
123
+ API schema changes:
124
+
125
+ ```bash
126
+ # one-time tooling install
127
+ python -m venv clients/python/.venv
128
+ clients/python/.venv/bin/pip install -e 'clients/python[codegen]'
129
+
130
+ # regenerate openapi.json + _models.py
131
+ clients/python/scripts/regenerate.sh
132
+ ```
133
+
134
+ The script dumps the spec via `bun scripts/dump-openapi.ts` (the same document
135
+ served at `/api/docs/openapi.json`) and runs `datamodel-codegen` over it. Only
136
+ `_models.py` is committed, so the client builds and installs without a running
137
+ server; `openapi.json` is a regenerated, gitignored artifact.
@@ -0,0 +1,123 @@
1
+ # translatarr-client
2
+
3
+ A typed Python client for the [Translatarr](../../README.md) API — synchronous
4
+ and asynchronous, with pydantic response models generated from the server's
5
+ OpenAPI spec.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install translatarr-client
11
+ ```
12
+
13
+ Published to [PyPI](https://pypi.org/project/translatarr-client/) for each `v*`
14
+ tag (the version matches the app's tag). The same wheel + sdist are also
15
+ attached to the corresponding GitHub Release, and you can install straight from
16
+ this repo:
17
+
18
+ ```bash
19
+ # from a published GitHub Release (version matches the app's v* tag)
20
+ pip install https://github.com/joshrmcdaniel/translatarr/releases/download/v1.2.3/translatarr_client-1.2.3-py3-none-any.whl
21
+
22
+ # or straight from this repo
23
+ pip install -e clients/python
24
+ ```
25
+
26
+ Requires Python 3.10+.
27
+
28
+ ## Authentication
29
+
30
+ Mint a personal API key in the web UI under **Settings → API keys** (tokens are
31
+ prefixed `tra_` and shown only once). Pass it as `token`:
32
+
33
+ ```python
34
+ from translatarr import TranslatarrClient
35
+
36
+ with TranslatarrClient("https://translatarr.example", token="tra_…") as tra:
37
+ result = tra.translate("Good morning", source_lang="en", target_lang="ja")
38
+ best = result.translations[0]
39
+ print(best.text, best.romanization)
40
+ ```
41
+
42
+ A browser session cookie works too, via `session_cookie="…"` instead of `token`.
43
+
44
+ ## Async
45
+
46
+ `AsyncTranslatarrClient` mirrors the sync client method-for-method:
47
+
48
+ ```python
49
+ import asyncio
50
+ from translatarr import AsyncTranslatarrClient
51
+
52
+ async def main() -> None:
53
+ async with AsyncTranslatarrClient("https://translatarr.example", token="tra_…") as tra:
54
+ result = await tra.translate("Bonjour", source_lang="auto", target_lang="en")
55
+ print(result.detected_source_language, result.translations[0].text)
56
+
57
+ asyncio.run(main())
58
+ ```
59
+
60
+ ## What you can do
61
+
62
+ | Area | Methods |
63
+ | --------- | ------- |
64
+ | Translate | `translate` |
65
+ | Chats | `list_chats`, `create_chat`, `get_chat`, `rename_chat`, `clear_chat`, `delete_chat` |
66
+ | Turns | `add_turn`, `select_option`, `retranslate_turn`, `switch_branch` |
67
+ | Speech | `transcribe`, `synthesize` |
68
+ | API keys | `list_keys`, `create_key`, `revoke_key` |
69
+
70
+ ### Persisting a chat without paying for a second translation
71
+
72
+ `translate` does not store anything; `add_turn` translates *and* persists. To
73
+ avoid a duplicate LLM call, pass the `result` you already have:
74
+
75
+ ```python
76
+ chat = tra.create_chat(source_lang="en", target_lang="ja")
77
+ preview = tra.translate("Let's ship it", source_lang="en", target_lang="ja", chat_id=chat.id)
78
+ chat = tra.add_turn(chat.id, "Let's ship it", source_lang="en", target_lang="ja", result=preview)
79
+ ```
80
+
81
+ ### Errors
82
+
83
+ Failures raise a subclass of `translatarr.APIError`, each carrying `status_code`,
84
+ the server's `code` (when present), and the raw `response`:
85
+
86
+ | Exception | Status |
87
+ | --------- | ------ |
88
+ | `InvalidRequestError` | 400 |
89
+ | `AuthenticationError` | 401 |
90
+ | `ForbiddenError` | 403 |
91
+ | `NotFoundError` | 404 |
92
+ | `ConflictError` | 409 |
93
+ | `MalformedResponseError`| 422 |
94
+ | `ProviderError` | 502 |
95
+
96
+ ```python
97
+ from translatarr import NotFoundError
98
+
99
+ try:
100
+ tra.get_chat("does-not-exist")
101
+ except NotFoundError:
102
+ ...
103
+ ```
104
+
105
+ ## Development
106
+
107
+ The response models in `src/translatarr/_models.py` are **generated** from the
108
+ server's OpenAPI document — do not edit them by hand. To regenerate after the
109
+ API schema changes:
110
+
111
+ ```bash
112
+ # one-time tooling install
113
+ python -m venv clients/python/.venv
114
+ clients/python/.venv/bin/pip install -e 'clients/python[codegen]'
115
+
116
+ # regenerate openapi.json + _models.py
117
+ clients/python/scripts/regenerate.sh
118
+ ```
119
+
120
+ The script dumps the spec via `bun scripts/dump-openapi.ts` (the same document
121
+ served at `/api/docs/openapi.json`) and runs `datamodel-codegen` over it. Only
122
+ `_models.py` is committed, so the client builds and installs without a running
123
+ server; `openapi.json` is a regenerated, gitignored artifact.
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "translatarr-client"
7
+ version = "0.2.1"
8
+ description = "Synchronous and asynchronous Python client for the Translatarr API."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "AGPL-3.0-or-later" }
12
+ authors = [{ name = "joshrmcdaniel" }]
13
+ keywords = ["translatarr", "translation", "llm", "api", "client"]
14
+ dependencies = [
15
+ "httpx>=0.24",
16
+ "pydantic>=2.4",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ # Tooling for regenerating the pydantic models from the OpenAPI spec
21
+ # (clients/python/scripts/regenerate.sh).
22
+ codegen = ["datamodel-code-generator>=0.65"]
23
+
24
+ [tool.hatch.build.targets.wheel]
25
+ packages = ["src/translatarr"]
@@ -0,0 +1,5 @@
1
+ # AUTO-GENERATED FILE — DO NOT EDIT.
2
+ #
3
+ # Pydantic models for the Translatarr API, rendered from
4
+ # clients/python/openapi.json (dumped from the server's Zod schemas in app/lib).
5
+ # Regenerate with: clients/python/scripts/regenerate.sh
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Regenerate the Python client's pydantic models from the server's OpenAPI spec.
4
+ #
5
+ # Step 1 dumps the spec (built from the Zod schemas in app/lib) to
6
+ # clients/python/openapi.json; step 2 renders it to src/translatarr/_models.py.
7
+ # Both outputs are committed, so the client is usable without a running server.
8
+ #
9
+ # Requires: bun (to run the dump script) and datamodel-code-generator (installed
10
+ # in clients/python/.venv, or otherwise on PATH).
11
+
12
+ set -euo pipefail
13
+
14
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ repo_root="$(cd "$script_dir/../../.." && pwd)"
16
+ client_dir="$repo_root/clients/python"
17
+ spec="$client_dir/openapi.json"
18
+ out="$client_dir/src/translatarr/_models.py"
19
+
20
+ echo "==> Dumping OpenAPI spec to $spec"
21
+ bun "$repo_root/scripts/dump-openapi.ts" "$spec"
22
+
23
+ codegen="$client_dir/.venv/bin/datamodel-codegen"
24
+ if [ ! -x "$codegen" ]; then
25
+ codegen="datamodel-codegen"
26
+ fi
27
+
28
+ echo "==> Generating pydantic models with $codegen"
29
+ "$codegen" \
30
+ --input "$spec" \
31
+ --input-file-type openapi \
32
+ --output-model-type pydantic_v2.BaseModel \
33
+ --output-datetime-class datetime \
34
+ --enum-field-as-literal all \
35
+ --collapse-root-models \
36
+ --snake-case-field \
37
+ --use-union-operator \
38
+ --use-standard-collections \
39
+ --use-annotated \
40
+ --use-schema-description \
41
+ --disable-timestamp \
42
+ --custom-file-header-path "$script_dir/model_header.txt" \
43
+ --target-python-version 3.10 \
44
+ --output "$out"
45
+
46
+ echo "==> Wrote $out"
@@ -0,0 +1,76 @@
1
+ """Python client for the Translatarr API.
2
+
3
+ Synchronous and asynchronous clients over the documented REST endpoints, with
4
+ pydantic response models generated from the server's OpenAPI spec.
5
+
6
+ from translatarr import TranslatarrClient
7
+
8
+ with TranslatarrClient("https://translatarr.example", token="tra_…") as tra:
9
+ result = tra.translate("Good morning", source_lang="en", target_lang="ja")
10
+ print(result.translations[0].text)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from importlib.metadata import PackageNotFoundError, version as _package_version
16
+
17
+ from ._errors import (
18
+ APIError,
19
+ AuthenticationError,
20
+ ConflictError,
21
+ ForbiddenError,
22
+ InvalidRequestError,
23
+ MalformedResponseError,
24
+ NotFoundError,
25
+ ProviderError,
26
+ TranslatarrError,
27
+ )
28
+ from ._models import (
29
+ ApiKey,
30
+ ChatDetail,
31
+ ChatSummary,
32
+ ChatTurn,
33
+ CreatedApiKey,
34
+ KeyWord,
35
+ Translation,
36
+ TranslationResponse,
37
+ )
38
+ from .aio import AsyncTranslatarrClient
39
+ from .client import TranslatarrClient
40
+ from .languages import AUTO_DETECT, SUPPORTED_LANGUAGE_CODES, SourceLang, TargetLang
41
+
42
+ try:
43
+ __version__ = _package_version("translatarr-client")
44
+ except PackageNotFoundError: # running from a source checkout without an install
45
+ __version__ = "0.0.0+unknown"
46
+
47
+ __all__ = [
48
+ "__version__",
49
+ # clients
50
+ "TranslatarrClient",
51
+ "AsyncTranslatarrClient",
52
+ # models
53
+ "ApiKey",
54
+ "ChatDetail",
55
+ "ChatSummary",
56
+ "ChatTurn",
57
+ "CreatedApiKey",
58
+ "KeyWord",
59
+ "Translation",
60
+ "TranslationResponse",
61
+ # languages
62
+ "AUTO_DETECT",
63
+ "SUPPORTED_LANGUAGE_CODES",
64
+ "SourceLang",
65
+ "TargetLang",
66
+ # errors
67
+ "TranslatarrError",
68
+ "APIError",
69
+ "InvalidRequestError",
70
+ "AuthenticationError",
71
+ "ForbiddenError",
72
+ "NotFoundError",
73
+ "ConflictError",
74
+ "MalformedResponseError",
75
+ "ProviderError",
76
+ ]