zotero-pdf2zh-pro
Local Python server for the Zotero zotero-pdf2zh-pro plugin.
uv tool install --python 3.13 zotero-pdf2zh-pro
zotero-pdf2zh-pro
The default service URL is http://127.0.0.1:8890.
Runtime maintenance
GET /health retains existing fields and adds capabilities.updateMaintenance
and maintenance (busy, activeRequests, idleSeconds, held). PDF queue
and cancellation cleanup, provider workers still completing after a timeout,
streamed selections, glossary downloads and data requests prevent maintenance.
POST /internal/maintenance atomically pauses admission when work has finished.
It accepts JSON idleSeconds (default 60, range 0–300), only from loopback,
without an Origin, with X-PDF2ZH-Control-Token matching the private
.control-token in the data directory. It returns 409 while busy, or 403 for
unauthorized callers. DELETE with the same credential releases maintenance;
the lease also expires after 120 seconds if the controller crashes.
During maintenance, new work returns HTTP 503 with code: server_maintenance,
accepted: false, Retry-After: 3 and X-PDF2ZH-Maintenance: not-accepted.
Clients may retry only this explicit rejection, polling health through the
replacement interval. An accepted operation or an ambiguous lost reply must
never be submitted again automatically. Health and task status polling remain
available. The capability is optional for older clients and applies equally to
the Windows, uv, Docker and Homebrew server entrypoints.
The server verifies HTTPS using the operating system's trusted certificates, including trusted proxy CAs on macOS and Windows. Certificate and hostname verification remain enabled. For Docker, install any required private CA in the container's trust store; the host's trust store is not inherited.
Reading memory and short text
POST /translation-lookup accepts documentFingerprint (the original PDF SHA-256),
text, optional one-based page, side (source by default, or translation),
and optional targetLang. It returns matched: false, or matched: true with
matchType (exact / contained), source, translation, page, paragraphId,
targetLang, and fromTranslationMemory: true. A contained match returns the whole
paragraph, not word alignment. The route never constructs or calls a provider.
translation-memory.sqlite3 lives directly in the configured data directory.
Completed PDF outputs sync succeeded, nonempty, quality-approved-or-unrejected
paragraphs from their task checkpoint. Corrected text replaces earlier text.
Existing completed/incomplete task checkpoints are also read at startup; deleting
a task does not remove memory. Legacy placeholders that cannot be restored are replaced with ⟦原排版内容⟧
and flagged with formattingIncomplete: true; their prose still matches without a provider call.
The recovery database remains exclusively the task retry checkpoint.
POST /translate-text accepts text, optional context (up to 12,000 characters),
documentFingerprint, one-based page, source, target, mode
(lookup, translate, explain), service, llm_api, and glossaryEntries.
The first two modes check memory and glossary before cached results or a provider.
Explicit explain requests may use a provider even on a memory hit; the real stored
paragraph pair supplies the context. No PDF task or font preparation is created.
Optional selectionProvider is profile (legacy default) or bing; optional
memoryPolicy is paragraph (legacy default) or exact. With exact, contained
memory does not stop translation; clients can retain it separately as folded reference.
The Reader explicitly sends both fields.
Bing requires no model configuration or key, and sends only selected text and
language pair to https://www.bing.com/ttranslatev3 after obtaining session parameters
from https://www.bing.com/translator. It splits losslessly at
UTF-8 boundaries up to 1,000 bytes (within the web translator’s 1,000-character limit), prefers sentence/whitespace boundaries, and starts
at most one request per second across workers. The whole translation has a 45-second
deadline. Quota errors (provider_quota, 429), invalid/empty responses and timeouts
are not cached; partial translations never count as success. There is no automatic
fallback to the model. Free-service cache keys contain only selected text, language
and provider; no paper context or credentials are sent.
The profile text provider supports Codex and existing presets resolving to OpenAISettings,
including Chat Completions and Responses. A fixed API protocol needs no health-check
request; auto negotiates only when a model request is actually needed.
Responses contain translation or explanation, provider, model when available,
and cached. Errors have a fixed code: empty_text, invalid_request,
invalid_config, unsupported_selection_provider, provider_timeout (504),
provider_error / empty_output (502), or selection_busy (429).
Requests wait at most 45 seconds; an already-running HTTP operation may finish later,
with at most two worker requests active. Duplicate in-flight requests share work,
except individually cancellable Codex requests described below.
A 128-entry, one-hour memory cache fronts selection-cache.sqlite3 in the server data directory.
Persistent translation/context results are capped at 20,000 least-recently-used entries; personal
word entries never expire. Only result data, digests and timestamps are stored, not credentials or
raw provider configuration. Model translation keys include context and output-affecting options;
context/explanation keys additionally include the document fingerprint.
Generating Codex profile requests may include a unique requestId containing
16–128 URL-safe letters, digits, underscores or hyphens. The plugin generates a
cryptographically random ID and sends POST /cancel-text with {"requestId": "..."}
when the selection closes, is replaced, or reaches its HTTP deadline. Cancellation
returns {"status": "ok", "cancelled": true} when marked; already finished requests
return cancelled: false. A cancellation that arrives before generation is retained
for up to 60 seconds (at most 128 recent IDs). Active and recently completed duplicate
IDs return duplicate_request_id (409); invalid or non-Codex IDs on generation return
invalid_request_id (400). Cancelled generation returns selection_cancelled (409).
These requests use separate turns, interrupt the matching Codex generation, and check
cancellation before saving results. Existing clients without request IDs remain compatible.
mode: dictionary returns entry (headword, senses, optional usage, aiGenerated), a plain-text
translation, model provenance, createdAt, formatVersion and saved. It requires a short
selection (at most 3 words / 100 characters). Its key preserves case and includes only the normalized
selection, language pair and schema/prompt version, allowing reuse across papers and models.
mode: context requires nonempty document fingerprint and surrounding context different from the
selection; it bypasses document memory and uses the current model. Both modes require selectionProvider: profile.
allowGenerate: false performs a read-only cache lookup, returning status: miss without invoking
any provider. cachePolicy: refresh bypasses saved results, memory and glossary; successful results
replace the corresponding cache entry, failures preserve the prior entry. Repeated concurrent
refreshes share work, and older results cannot overwrite newer refreshes or repopulate cleared caches.
saved: false indicates the result is usable but could not be persisted.
POST /selection-capabilities returns selectionLearning: true; the same capability is advertised
by /health. New clients verify it before using these fields. POST /selection-cache/clear clears
translation/context caches but preserves personal dictionary entries and document memory. Existing
request modes remain supported. Empty/invalid AI dictionary output is rejected as invalid_output;
missing usable context is context_unavailable.
Reader usage: user guide. Manual verification: development notes.
POST /selection-capabilities also declares selectionStream: true.
POST /translate-text/stream accepts the same ordinary mode: translate,
selectionProvider: profile request with a required unique requestId.
It returns text/event-stream: start (actual provider/model), delta (new text),
done (authoritative complete result, including cache/save status), or error
(stable code and displayable message). Every data event carries the request ID
and an increasing seq; idle heartbeats are SSE comments. Cache, glossary and exact
document-memory hits send only start and done. Structured dictionary/context
responses and Bing text translation continue using /translate-text JSON.
Chat Completions requires a successful stop finish; Responses requires a completed
response. Codex only forwards confirmed final_answer message deltas within the matching
thread, turn and item, then corrects the full text at completion. Unknown phases remain
hidden until a completed final answer. Reasoning and tool output are never displayed.
All streaming requests support /cancel-text, including cancellation arriving before
generation. Established upstream HTTP streams are closed on cancellation; connection
setup is bounded by the existing timeout. Disconnect, timeout, cancellation, missing
completion and truncated output do not persist partial translations. No automatic
model retry follows a stream failure; stream_unsupported permits an explicit JSON retry.
The plugin checks capabilities and uses JSON when connected to an older server.
Youdao and Bing dictionary lookups, pronunciation and favorites run in the plugin, independently of the Python service. They are distinct from Bing text translation.
The Reader collects at most two paragraphs / 800 characters from each adjacent page,
with a 1.5-second per-page timeout and a 12,000-character total context limit. Failed
adjacent-page reads are skipped. Exact-selection support is advertised through
/health.capabilities.exactSelectionTranslation. Dictionary installation validates
SHA-256, compressed content and entry structure, skipping empty entries or entries
without usable Chinese senses. Dictionary data is loaded on demand and shared across Readers.
Model discovery
POST /list-models accepts apiUrl, optional apiKey, and optional
apiProtocol (auto, chat_completions, or responses). It strips a recognized
translation endpoint suffix and requests <base>/models, preserving custom
paths and never adding /v1. A successful response is
{"status":"ok","models":["model-id"]}; an empty list is valid.
Discovery uses a 15-second timeout and does not follow redirects, persist keys,
or echo upstream response bodies in errors. Errors return status and message
with HTTP 400 for invalid input, 502 for provider failures, or 504 for timeout.
Clients should allow manual model entry on failure. /health advertises
supportsModelDiscovery: true; existing translation endpoints are unchanged.
Codex CLI provider
/health.capabilities.codexCli: true means this server includes the adapter; it does
not mean the CLI is installed, signed in, or permitted to use a particular model.
The minimum CLI version is 0.153.4; protocol and isolation checks must also pass.
Discovery runs on the service computer as the service user. Desktop login does not
guarantee that the service can reuse it. Docker does not include Codex or a login.
Use service: "codex" with llm_api.model (default gpt-6-luna), optional
llm_api.cliPath, and optional llm_api.reasoningEffort. An omitted effort uses
the model default, while "none" is an explicit effort. The provider uses Standard
speed; API keys, API URLs, Responses options and OpenAI reasoning switches do not
configure this provider. Profiles keep an explicit model; unavailable models fail
with an actionable error instead of silently selecting a different one.
POST /list-models also accepts {"service":"codex","cliPath":""}. It returns
models plus modelDetails, whose entries contain id, displayName,
defaultReasoningEffort, and supportedReasoningEfforts (an array of strings).
Catalog visibility is not proof of account entitlement. Discovery performs no
generation. /validate-config checks readiness without a model call when
liveTest: false; liveTest: true performs one short translation and reports its
result through the existing liveTest and diagnostics fields. resolvedProtocol
is null for Codex.
Local runtime discovery
/health.capabilities.codexDiscovery: true advertises POST /codex/discover.
Send an object with optional cliPath, proxyMode, proxyUrl, force (boolean,
default false), and requestId (8–80 ASCII letters, digits, underscores or hyphens).
An empty path means automatic selection; an explicit path is authoritative.
This endpoint starts only local version/protocol/isolation probes: no login and no
translation. Compatibility does not prove authentication or model entitlement.
Success returns {"status":"ok","codexRuntime":{"source":"desktop","path":"...","version":"0.162.0-alpha.17.2","compatible":true,"attempts":[]}}.
attempts contains skipped candidates' path, source, code, and fixed local
message; it never includes raw process output or credentials. Sources are
manual, desktop, install_dir, path, standalone, homebrew, and npm.
/list-models and successful Codex /validate-config responses also carry
codexRuntime. Errors retain code, message, and candidate attempts.
With requestId, clients can cancel discovery, model listing, or a Codex connection
test via POST /codex/discover/cancel with the same ID. Cancellation is idempotent,
returns status: ok, and the cancelled request returns codex_cancelled (499).
Cancellation stops probes; generation in an explicitly requested live connection
test is interrupted through the existing turn cancellation mechanism. IDs must be
unique per request. Duplicate active IDs return 409; excessive registrations 429.
Automatic order: desktop bundles (newest full semantic version first),
CODEX_INSTALL_DIR, all PATH entries in order, then known standalone/Homebrew/npm
locations. macOS checks system/user Applications and registered App locations;
Windows checks desktop version directories and skips staging entries. Official npm
launchers resolve to their matching native binary, including old vendor and new
platform package layouts; no Node lookup or Windows shell wrapper is required.
Search never traverses the whole disk, runs shell startup files, or mutates PATH.
Each candidate has a 5-second validation budget, with 30 seconds overall including
lock waits; owned probe processes are cleaned up on timeout/cancel. Local installation
and protocol errors fall back; authentication, network, quota and isolation errors
do not. Successful selection is cached in-process for 60 seconds and invalidated
by file identity, candidate set or environment changes. force: true refreshes it.
Automatic paths are never persisted. Each translator pins its installation for the
whole PDF or selection request; refreshing discovery does not replay or redirect it.
Clients must check codexCli for every Codex model query, including inherited proxy
mode. Without codexDiscovery, hide the detection control and retain the original
model/test flow. The existing codexCli and codexProxy contracts remain unchanged.
Proxy environment
Codex profiles also accept llm_api.proxyMode: inherit (default), manual, or
direct. In manual mode llm_api.proxyUrl is an HTTP(S) proxy URL with no
credentials, path, query or fragment; for example http://127.0.0.1:7897.
Use the HTTP/Mixed listener of a local proxy application, not a SOCKS-only port.
/list-models accepts the same proxyMode and proxyUrl fields at the top level.
/health.capabilities.codexProxy and /selection-capabilities.codexProxy advertise
support, so clients can refuse unsupported explicit proxy choices on older servers.
Only the app-server child environment changes: manual mode sets both cases of
HTTP_PROXY, HTTPS_PROXY and ALL_PROXY, and replaces NO_PROXY with loopback hosts;
direct mode removes inherited proxy addresses and sets NO_PROXY to * in both
cases. Inherit mode preserves the service environment as it was at startup.
The parent service and system settings are untouched. Clients are shared only
within the same process, CLI path and proxy configuration; changing proxy starts
an independent connection without interrupting other configurations' requests.
Proxy changes do not invalidate translation caches. Invalid settings produce
codex_invalid_proxy with a fixed message that does not echo the supplied URL.
Each client lazily shares an owned stdio app-server, bounded to two active generations. Requests use isolated temporary conversations. Only completed final answers enter output validation and caching. Cancellation interrupts the turn; an unresponsive owned process is recycled. The adapter never copies login tokens or closes the shared client after a selection request. Context/dictionary output uses the same structured validators as other profile providers, and selection cache identity includes the model and reasoning effort.
Normal server shutdown and SIGTERM cancel selection work and close owned Codex clients before exiting. On Windows each app-server belongs to a kill-on-close Job Object, so forced termination of the Python service also stops its owned Codex process tree.
Codex selection failures return a safe code and fixed local message, including codex_not_installed,
codex_invalid_path, codex_dependency_missing, codex_start_failed,
codex_permission_denied, codex_arch_mismatch, codex_version_too_old,
codex_version_unrecognized, codex_incompatible, codex_not_logged_in, codex_model_unavailable,
codex_invalid_reasoning, codex_isolation_failed, codex_timeout,
codex_process_exited, codex_protocol_error, codex_quota_exhausted, and
codex_request_failed. Full-document/configuration errors contain a safe
explanation. Ambiguous failures are not automatically replayed. Token usage is
reported when supplied by the CLI and otherwise remains unknown.
Translation protocols and request options
The optional llm_api.apiProtocol field accepts auto, chat_completions, or
responses; llm_api.requestOptions accepts a JSON object of extra request
parameters. /health advertises supportedApiProtocols, and /validate-config
returns resolvedProtocol. Older servers support the Chat Completions fallback;
Responses and extra request options require a compatible server update.
New task metrics omit cost. Token and upstream cache values may be null, with
availability indicating unavailable, partial, or complete. Legacy cost
fields are ignored when reading historical records.
Translation completeness and repair
Task details and SSE events expose translationSummary, failedParagraphs, and
canRepair. The incomplete status indicates that paragraphs requiring
translation are still unresolved. A terminal task with an existing PDF exposes
canDownloadResult: true; its PDF can be downloaded and is automatically imported
into Zotero with an incomplete label. Repair creates a new attachment and preserves
previous versions and annotations. Older servers without this field retain the
completed-only import behavior. Import retries do not trigger translation requests.
POST /tasks/{taskId}/repair accepts incomplete, completed, and cancelled tasks
and returns the snapshot for the new attempt. Active tasks return HTTP 409.
Repair preserves validated translations and requests the remaining paragraphs,
using QPS 2 and concurrency 4 by default. Pure URL footnotes are preserved without
translation; ordinary body text containing a URL still requires translation.
Paragraph checkpoints are stored in paragraph-recovery.sqlite3 inside the task
directory, allowing repair after a server restart. Deleting a task also removes
its checkpoints. JSON structure, paragraph IDs, nonempty translations, and
placeholders are validated before translations are cached.
Runtime and data paths
Run zotero-pdf2zh-pro --help for supported options: --host, --port,
--log-level, --data-dir, and --log-file. The default listener is
127.0.0.1:8890. /health reports the effective workspace.path, writability,
free space, versions, and task counts. Inspect that path before backing up data
or uninstalling; its location depends on the installation method.
The corresponding environment variables are PDF2ZH_HOST, PDF2ZH_PORT,
PDF2ZH_LOG_LEVEL, PDF2ZH_DATA_DIR, and PDF2ZH_LOG_FILE. By default, task data
is stored in translates alongside the installed server module. The optional
log file rotates at 10 MiB with three backups.
For the Windows and macOS installation walkthrough, see the main tutorial.
On-demand glossary packs
The server manages optional English-to-Simplified-Chinese glossary packs under
<effective-data-dir>/glossaries/. Installation packages contain catalog metadata
only. Downloads and update checks are explicit actions from Zotero preferences;
installed packs work offline. Each translation task stores the resolved terms and
source versions, so retries and repairs survive pack updates or removal.
/health advertises capabilities.glossaryPacks. GET /glossaries is local-only;
POST /glossaries/check-updates refreshes the catalog. Download, cancel and remove
operations use /glossaries/<id>/download, /glossaries/<id>/cancel, and
DELETE /glossaries/<id>. Translation requests may include glossaryPacks with
id, version, and sha256 alongside custom glossaryEntries.
See glossary downloads for storage, API and merge rules, and glossary sources for attribution.
Task diagnostics and bounded cancellation
/health.capabilities advertises diagnosticsExport, boundedCancellation, and detailedTaskProgress. GET /diagnostics and GET /tasks/{taskId}/diagnostics return versioned, size-bounded, allowlisted JSON; they never export request payloads, paper content, keys or raw historical logs. Structured rotating logs live under <data-dir>/diagnostics/. The plugin combines this snapshot with its own records into a local ZIP.
Each attempt runs in a spawn process, contained by a POSIX process group or Windows Job Object. Cancellation escalates at 10/12 seconds and reports cleanup failure at 15 seconds rather than waiting indefinitely. A failed cleanup pauses the queue. Stalls alone only trigger diagnostics, never automatic cancellation. See the owning specification for fields, retention, recovery and acceptance.
Provider audit and configuration schema v4
The plugin exposes 11 platform presets backed by 9 services. OpenRouter uses
service: "openai" and apiUrl: "https://openrouter.ai/api/v1"; the optional
providerPreset profile field is UI metadata, not a new server service ID.
New profiles use Chat Completions; saved protocols and custom URLs are preserved.
Zhipu, Grok, Groq, ModelScope, QwenMT, Azure OpenAI, Azure Translator, XInference
and AnythingLLM adapters are removed. Schema v4 keeps these profiles and their
references for manual reconfiguration, and prevents translation or tests through
them. There is no automatic replacement. The earlier removal of
openaicompatible, tencentmechinetranslation, dify, and siliconflowfree remains
in effect. Missing or removed service IDs return a validation error.
POST /list-models retains its existing request and response shape. Supported
HTTP catalogs are OpenAI-compatible endpoints (including OpenRouter), DeepSeek,
Gemini and SiliconFlow. SiliconFlow includes sub_type=chat; Codex discovery
uses app-server. Unsupported catalogs and failures allow manual model entry.
See the dated audit for migration and profile acceptance checklist for UI verification.
Glossary pack API
| 接口 | 行为 |
|---|---|
GET /glossaries |
本地目录、安装版本、条数、字节数和下载状态,不联网 |
POST /glossaries/check-updates |
手动刷新远端目录,失败时保留原目录 |
POST /glossaries/<id>/download |
异步下载当前目录版本;可传 {"version":"…"} |
POST /glossaries/<id>/cancel |
取消进行中的下载,保留已安装版本 |
DELETE /glossaries/<id> |
移除全部安装版本;进行中的下载须先取消 |
下载接口不接受调用者指定 URL 或路径。正文地址限定项目公开仓库的固定提交,并校验 SHA-256、文件大小、格式和词条数量。临时文件通过全部校验后才安装。
POST /tasks 增加可选 glossaryPacks 数组:
{
"glossaryPacks": [
{"id": "medicine", "version": "目录返回的版本", "sha256": "目录返回的完整 SHA-256"}
],
"glossaryEntries": [
{"source": "自定义原词", "target": "自定义译法", "tgt_lng": "zh-CN"}
]
}
Usage and merge rules: glossary guide.
Metadata
Release files for zotero-pdf2zh-pro 1.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| zotero_pdf2zh_pro-1.9.0.tar.gz | 21.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zotero_pdf2zh_pro-1.9.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 42.0 MB
Release files / zotero_pdf2zh_pro-1.9.0.tar.gz
| Download URL | zotero_pdf2zh_pro-1.9.0.tar.gz |
|---|---|
| Size | 21.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d0bf59808a8ba4431ff998dd350922bd061434966af900e80808f421b95c4388
|
|
BLAKE2b-256 checksum How to use checksums |
fd602eeaa7c2c7fd7b879fb4499f9e21b998dcffd59644f39ddeb86f023a1c74
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / zotero_pdf2zh_pro-1.9.0-py3-none-any.whl
| Download URL | zotero_pdf2zh_pro-1.9.0-py3-none-any.whl |
|---|---|
| Size | 21.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4f6975ec2bbe1984a5ad4f8199af5ff1a743baabe7f221e8ab42b636ee097bec
|
|
BLAKE2b-256 checksum How to use checksums |
3024d08476fed2f51492da3dea6c0ab53a3dd8e4867bcebd5286bd152039eac1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|