Metadata-Version: 2.4
Name: guwenzi-tools
Version: 0.4.0
Summary: CLI and Python client for evidence-preserving historical-document and glyph tools
Author: Guwenzi contributors
License-Expression: MIT
Keywords: ocr,paleography,glyph,cli,multimodal
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Image Recognition
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1,>=0.28
Requires-Dist: filelock<4,>=3.15
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == "dev"
Requires-Dist: twine<7,>=6; extra == "dev"
Provides-Extra: fonts
Requires-Dist: fonttools<5,>=4.60; extra == "fonts"
Requires-Dist: skia-pathops<1,>=0.8; extra == "fonts"
Dynamic: license-file

# guwenzi-tools

A lightweight CLI and Python client for a remote **Guwenzi** historical-document and glyph service. Supports external multimodal agents that can run commands and view images. Python 3.10+; Linux, macOS and Windows. Client license: MIT.

安装包只包含客户端、工具schema和agent说明。模型、字库、文献和访问凭据留在各自的部署环境里。服务端需要实现 `guwenzi.tools.v1`；安装客户端不会自动获得公共推理服务，也不会在本机下载或加载Qwen、DINO、YOLO、torch或MLX。

## Install and connect

Install from PyPI:

```sh
python -m pip install guwenzi-tools
```

Alternatively, install a release wheel or this source directory:

```sh
python -m pip install /path/to/guwenzi_tools-0.4.0-py3-none-any.whl
# or, from this directory
python -m pip install .
```

### Option A: log in with an Open Glyph website account (0.3+)

If you have an account on the Open Glyph website (or another site running the same gateway), no endpoint or token is needed:

```sh
guwenzi-tools login
```

The CLI shows a connection code and opens the website in your browser. Confirm the code matches and approve; the terminal completes automatically. The issued credential belongs to your account, is stored only on this machine, and can be revoked at any time from the website workspace (账号与数据) or with:

```sh
guwenzi-tools whoami
guwenzi-tools logout   # revokes on the server and removes the local profile
```

`--website URL` points at a different site; `--no-browser` prints the approval URL instead of opening it.

### Option B: configure your own service (self-hosted)

Configure the endpoint once. Enter the service token at the hidden prompt:

```sh
guwenzi-tools configure --endpoint https://your-service.example/api/predict/guwenzi_tools
guwenzi-tools doctor --quick
guwenzi-tools doctor
```

EAS uses raw Authorization by default. For a standalone server with Bearer authentication add `--auth bearer`. HTTPS is the default requirement; explicitly use `--allow-http` for a service that only has HTTP. Loopback HTTP works for local testing. The complete service URL includes its application prefix; do not append `/health` or `/v1` when configuring it.

For agents and CI, configure a reference to an environment variable instead of storing its value:

```sh
guwenzi-tools configure --endpoint https://your-service.example/api/predict/guwenzi_tools --token-env GWZ_REMOTE_TOKEN
```

Provide that environment variable through your agent's secret settings. `--token-stdin` is another option. `config show` redacts saved tokens. Multiple profiles are supported with `--profile NAME`; `config use NAME` changes the default, and the first configured profile is selected automatically. The config location can be changed with `--config FILE` or `GWZ_CLIENT_CONFIG`. POSIX credential files are written with mode0600.

The existing `GWZ_REMOTE_URL`, `GWZ_REMOTE_TOKEN`, `GWZ_REMOTE_AUTH` environment variables and `--remote URL` alias remain supported. Explicit endpoint overrides do not inherit a different server's stored token.

## Document workflow

### Performance-enabled hosts (CLI 0.3.6+, service 0.3+)

Check `guwenzi-tools tools --server` before using new arguments. `page image DOCUMENT_ID PAGE --output DIR` gets the original page render/source text without layout or character-slot generation. `page prepare --detail regions` is the default; choose `lines` or `slots` only when needed. Existing evidence remains unchanged.

`page image` is lightweight: its source-text trace is not a complete extraction of font programs or vector paths. Request native evidence separately when needed, preserving the same mode/DPI and original PDF. An existing clear paragraph can be read directly without layout analysis.

Use `guwenzi-tools batch crop|glyph_search|glyph_compose --json @request.json --output DIR --preview-only` for grouped work. The tool names remain compatible with the existing website gateway allowlist. Crop/compose take `items`; search takes `asset_ids`. Full per-item results and original references stay in `result.json`; preview-only downloads the labelled contact sheet, not all candidates. It is a derivative and must not be used for original crop coordinates. A search contact sheet previews at most8 candidates per query; full top32 results remain in the JSON. Generic `call` supports the same server arguments on older clients.

The server stages image work on local disk and commits durable artifacts before completing jobs. Original pixels and hashes are preserved; enlarged views are created only when requested. Exact operation caches are tied to source hashes, options and model/bundle versions. Similarity remains unverified evidence, and batching does not imply automatic acceptance.

Start with one page, inspect the evidence, and continue according to the reading task:

```sh
guwenzi-tools document open paper.pdf
guwenzi-tools page image DOCUMENT_ID 1 --output work/page-1
```

For a terminal tool with a short waiting limit, submit and collect separately:

```sh
guwenzi-tools page image DOCUMENT_ID 1 --output work/page-1 --detach
guwenzi-tools status JOB_ID --wait --output work/page-1
```

The second command downloads the completed page's evidence and prints a compact summary. It can be repeated after an interruption. Crop, line splitting and glyph search/resolve also support `--detach`. Cold instances may need extra time to prepare their model/index cache: resume by job ID, never replay an uncertain submission. Repeat-cache latency is not a promise about every new query.

Or prepare selected pages in one resumable operation:

```sh
guwenzi-tools document prepare paper.pdf --pages 1-3 --output work/paper
guwenzi-tools document prepare paper.pdf --pages 1-3 --output work/paper --resume
```

`document prepare` defaults to all pages if `--pages` is omitted. Mode/DPI, endpoint, page range and source hash must agree when resuming. Preparation stays sequential and resumable; independent calls may wait in the server's bounded queue. A complete evidence preparation is not a completed OCR transcription.

Output folders contain:

| File | Purpose |
|---|---|
| `brief.json` | Compact navigation and original image paths |
| `result.json` | Full server response, with source text and unresolved status |
| `materials.json` | Original/viewing-derivative files and SHA256 verification |
| `index.html` | Local visual review page; image links open original files |
| `transcriptions.draft.json` | Per-block requests to fill after reading the images |
| `assets/` | Exact original bytes and separately labelled enlarged views |

Native extraction retains source-layer text and original glyph evidence. For scans, inspect the page first; use remote YOLO regions and projection lines/slots only when needed, then the calling multimodal model supplies text. Word/PPT are converted by the server. The CLI itself does not infer the reading of an image.

## Uncertain glyphs

```sh
guwenzi-tools line split LINE_ASSET_ID --output work/line
guwenzi-tools crop LINE_ASSET_ID --box 10 0 60 80 --output work/crop
guwenzi-tools glyph search CROP_ASSET_ID --output work/search
```

View the query and candidate originals returned by the search. Select a matching candidate using its request-scoped ID:

```sh
guwenzi-tools glyph select SEARCH_ID c3 --output work/binding.json
```

The choice of `c3` is an example, not a default decision. If no candidate matches, expand when more evidence is useful or register the unresolved original. Policy-enabled hosts support further expansion without repeating earlier candidates:

```sh
guwenzi-tools glyph search CROP_ASSET_ID --expand-search SEARCH_ID --output work/expanded
guwenzi-tools glyph register CROP_ASSET_ID --output work/unresolved.json
guwenzi-tools glyph resolve PERMANENT_CODE --output work/identity
```

Projection proposes boundaries. Crop boxes always use original-image pixels. `gwz:` codes identify a specific original/source asset; similarity, selection and a valid code do not prove a Unicode reading. Source identities and original forms are preserved.

## Transcribe and export

Fill `transcriptions.draft.json` with text and structured glyph references, then:

```sh
guwenzi-tools transcribe work/page-1/transcriptions.draft.json
guwenzi-tools document result DOCUMENT_ID --offset 0 --limit 20
guwenzi-tools document export DOCUMENT_ID --output work/export
```

Use `--skip-empty` only when intentionally submitting a partially filled draft. Every `part` has exactly one string field: `text`, `binding_id`, `glyph_code`, or `asset_id`. Bindings must belong to the selected block or its recorded crops/slots; the server enforces the association. The exported JSON keeps original source text, transcriptions, figures/tables and verification status. JSON preserves codepoints exactly; it performs no Unicode normalization or traditional/simplified conversion. Markdown is a readable derivative and references the preserved image files.

Export saves assets referenced by the v1 API and the archived original document. It does not claim to enumerate every intermediate file kept inside the server. For large exports, consider `--no-images` first or a page-specific evidence packet.

## Agents and low-level access

```sh
guwenzi-tools agent guide --plain
guwenzi-tools agent install-skill --directory /your-agent/skills/guwenzi-remote
guwenzi-tools tools
guwenzi-tools tools --server
guwenzi-tools call glyph_search --json @request.json
guwenzi-tools call page_prepare --json @request.json --detach
guwenzi-tools status JOB_ID --wait
guwenzi-tools asset ASSET_ID --output original.png
```

Convenience commands output one JSON envelope on stdout and progress on stderr. Full page/search responses are stored locally while summaries point the agent to image files; image base64 is not printed. `call` and `status` retain the v1 job envelope. Tasks already submitted continue on the server if the CLI stops waiting. Receipts retain job IDs without credentials. An uncertain POST outcome and a 429 admission rejection are never automatically repeated; only idempotent GET requests have bounded transient-network retries.

Any multimodal agent with shell access and an image-viewing function can use the same CLI. Hosted chat interfaces that cannot run commands need an application-side tool adapter; installing a Python package alone does not give a hosted model filesystem or shell access. No MCP connection or specific model provider is required.

## Python SDK

```python
from gwz_client import Client
from gwz_client.workflow import Preparation

with Client.from_config() as client:
    print(client.health())
    result = Preparation(client, "work/paper").run("paper.pdf", page_spec="1")
    print(result["document_id"])
    job = client.call("document_result", {"document_id": result["document_id"], "limit": 20})
    print(job["result"])
```

Credentials can also be supplied to `Client(endpoint, token, auth="raw")` by your application's secret store. The SDK and CLI share the same transport and integrity checks.

## Distribution and service access

PyPI distributes the client, not an inference entitlement. Each deployment owner provides an endpoint and access credentials separately. The current v1 service has one EAS access domain and a shared document store; profiles are client connection settings, not server-side tenant isolation. A public multi-user SaaS would need separate identity, quotas and data isolation on the service side.

This repository can build a wheel and source distribution with `python -m build`, and validate them with `python -m twine check dist/*`. Publication is a separate owner action. See `PUBLISHING.md` in the source distribution for TestPyPI/PyPI release steps. This MIT license covers the client only; server dependencies, model weights and external glyph datasets have their own licenses.

## Inline glyphs and real fonts

Page preparation and document export write `reading.html`: permanent gwz markers display as inline glyph images, with links to the enlarged originals. Long IDs remain in the evidence JSON. This view can be printed even when no usable font outline exists.

For a native PDF packet with preserved `.path` programs, install the optional font dependencies and export an ordinary OpenType CFF font:

```sh
python -m pip install 'guwenzi-tools[fonts]'
guwenzi-tools font build --packet work/page-1 --output work/font
guwenzi-tools font encode text.txt --map work/font/glyph-map.json --output work/typeset.txt
```

Install the generated `.otf` in your word processor, or explicitly embed it in your typesetting tool. `font-preview.html` embeds it for local browser viewing. `glyph-map.json` records its SHA256, font-local PUA characters, gwz identities, original path hashes and geometric transforms. Keep the font and mapping with the document. A gwz identifier alone is not an installed font character; PUA has meaning only with the matching font.

Supported original fill paths retain Bezier curves; independent fills are unioned before font encoding. Unsupported paint/clipping operations and conflicting outlines are reported, not silently traced. CFF has finite coordinate precision, and the export normalizes the glyph into a common em box; it does not recover the original font's complete metrics. Original evidence remains unchanged.

For a **printed bitmap** without usable original outlines, first reuse an existing glyph or assemble existing components. The agent chooses structure and variants; the server generates all KAGE and fonts. Do not ask the agent to write stroke coordinates or a self-contained KAGE program.

~~~sh
guwenzi-tools glyph lookup 旣 --output work/variants
guwenzi-tools glyph lookup u65e3-j --mode parts --output work/parts
guwenzi-tools glyph compose --ids '⿱艹⿰日月' --output work/composed
guwenzi-tools glyph compose --glyph '<glyph_ref from lookup>' --unicode-codepoint U+65E3 --output work/reused
~~~

Use `--structure @tree.json` for selected components and relative weights. Each node is a glyph reference or `{op:"⿰",children:[{glyph:REF},{glyph:REF}],weights:[1,2]}`. The service resolves the exact dependency closure; missing parts fail explicitly, never as a blank or silently substituted glyph. `--hint-asset` attaches the original crop for final-font comparison. Generic `call glyph_compose` and batches accept the same high-level fields. The old `kage_json` surface is retired.

DINO search remains on the existing service. A selected public `gwz:` code can enter `glyph_lookup`; a GlyphWiki-backed candidate with a matching source snapshot can also enter compose directly. Images from other providers do not imply a reusable KAGE program. The code library is queried on disk, not loaded into each agent sandbox. Check live schemas before using these new fields; update an old host instead of reverting to raw stroke programming.

Print-font `ready` is not source-fidelity approval. Inspect actual output fonts and fix selected variants or structural layout. Original outlines remain preferable; handwriting and rubbings remain source evidence. Node, KAGE, and the component catalog are service-side resources, not added dependencies of this lightweight client.

`glyph search` deliberately shows images and request-scoped candidate IDs before revealing labels. `glyph select` returns the complete selected identity; `glyph resolve` saves it in `result.json` and includes an `identity` summary on stdout: source codepoints, font/source hashes, version, glyph ID/name and available source aliases/relations. `reading_status=no_confirmed_reading_in_v1` means the API does not provide a verified scholarly reading. A source font can borrow an unassigned, private-use or unrelated codepoint; neither a `U+...` label nor a glyph name confirms Unicode identity. `source_material_in_bundle=false` also means the metadata is available but the original font program is not in that deployed bundle.
