Metadata-Version: 2.4
Name: hugpy-tools
Version: 0.2.24
Summary: Hugpy tools: a slim, stdlib-only capability suite for autonomous agents (safe file/path ops, text chunking and diffing, hashing, structured-data read/write, and urllib+html.parser webpage assessment)
Author-email: putkoff <support@hugpy.ai>
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://hugpy.ai
Project-URL: Documentation, https://github.com/hugpy/hugpy/blob/main/py/foundation/hugpy_tools/README.md
Project-URL: Repository, https://github.com/hugpy/hugpy
Project-URL: Source, https://github.com/hugpy/hugpy/tree/main/py/foundation/hugpy_tools
Project-URL: Issues, https://github.com/hugpy/hugpy/issues
Project-URL: Changelog, https://github.com/hugpy/hugpy/releases
Project-URL: Architecture, https://github.com/hugpy/hugpy/blob/main/PARTITION.md
Keywords: hugpy,agent,tools,filesystem,webpage,assessment
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: abstract_essentials>=0.0.0.30
Requires-Dist: hugpy-resources<0.2,>=0.1.2.8
Provides-Extra: toml
Requires-Dist: tomli; python_version < "3.11" and extra == "toml"
Provides-Extra: yaml
Requires-Dist: PyYAML; extra == "yaml"
Provides-Extra: render
Requires-Dist: playwright; extra == "render"
Provides-Extra: images
Requires-Dist: Pillow; extra == "images"
Provides-Extra: vision
Requires-Dist: Pillow; extra == "vision"
Requires-Dist: pytesseract; extra == "vision"
Requires-Dist: numpy; extra == "vision"
Requires-Dist: opencv-python-headless; extra == "vision"
Provides-Extra: text
Requires-Dist: abstract_utilities; extra == "text"
Provides-Extra: files
Requires-Dist: abstract_utilities; extra == "files"
Requires-Dist: abstract_search; extra == "files"
Requires-Dist: abstract_paths; extra == "files"
Provides-Extra: web
Requires-Dist: abstract_webtools; extra == "web"
Provides-Extra: legacy
Requires-Dist: abstract_utilities; extra == "legacy"
Requires-Dist: abstract_search; extra == "legacy"
Requires-Dist: abstract_paths; extra == "legacy"
Requires-Dist: abstract_webtools; extra == "legacy"
Provides-Extra: documents
Requires-Dist: media_intelligence[documents]<0.3,>=0.1.3; extra == "documents"
Provides-Extra: media
Requires-Dist: hugpy-tools[documents]; extra == "media"
Provides-Extra: desktop
Requires-Dist: abstract_clicks; extra == "desktop"
Requires-Dist: abstract_windows; extra == "desktop"
Requires-Dist: Pillow; extra == "desktop"
Requires-Dist: pytesseract; extra == "desktop"
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: pytest-timeout; extra == "test"
Dynamic: license-file

# hugpy-tools

`hugpy_tools` is a standalone set of tools for autonomous agents. Its generic
implementations come from `hugpy-resources` and use the standard library. Optional backends add document extraction,
model-specific token counting, rendering, media, image, desktop and VM tools.
It does not import Toolserver, and it needs no running service, database or
fleet.

Distribution `hugpy-tools`, import `hugpy_tools`. Its base runtime dependency
is `hugpy-resources`; optional integrations retain their existing extras.

## Standalone use

```bash
pip install hugpy-tools
hugpy-tools list [--optional]         # every tool: name, description, inputSchema (JSON)
hugpy-tools call text_estimate_tokens --arguments '{"text":"hello world"}'
# The same CLI is available as python -m hugpy_tools.
```

```python
from hugpy_tools import call_tool, get_toolset, list_tools

result = call_tool("fs_read_lines", {"path": "/tmp/example.txt", "start": 1, "end": 20})
schemas = list_tools()  # name, description, inputSchema
tools = get_toolset()  # category -> route -> callable; fresh mappings per call
```

Generic functions are imported directly from `hugpy_resources.imports` or its
implementation modules. Tool capabilities remain in `hugpy_tools`. `include_optional=True` adds backend
tools to each registry API, or use `--optional` with the CLI. Discovery imports
no optional backend; an unavailable dependency fails when that tool is called.
Install only the extras needed: `files`, `text`, `web`, `legacy` (all three),
`render`, `images`, `vision`, `media`, or `desktop`. Rendering also needs a
Playwright browser installation; VM operations require host libvirt/LXD tools.

## Toolserver integration

The dependency runs one way: `abstract_toolserver` imports `hugpy_tools`.

The optional media tools call the authoritative `hugpy-media` API through
`backends.media_proxy` (default `http://127.0.0.1:7340/api`, overridable with
`HUGPY_MEDIA_API_URL`). Toolserver and `/media` therefore share the same Media
routes for Whisper, vision, extraction, summaries and the other inference
operations; model placement and task selection stay in Hugpy Media/Core.

| This package owns | Toolserver adds |
| --- | --- |
| Portable implementations, backend adapters, registry, and input schemas | HTTP/MCP transport, authentication, and discovery |
| Local screenshot rendering, image operations, grid math, and aiming helpers | Hosted screenshot URLs, cleanup, and fleet vision routing |
| Explicit local execution | Command allowlist, node delegation, and session context |

Boards, handoffs, credentials, database records, and managed browser/VM sessions
remain Toolserver services. `process.run_command(argv)` is available directly;
Toolserver exposes it through its allowlist-gated `sys_run_cmd` adapter.

`compat.py` preserves established Toolserver arguments and backend results where
the native helpers have different contracts. For example, `text_estimate_tokens`
is approximate and dependency-free; optional `text_count_tokens` retains the
`model_name` argument and model-specific backend. `fs_read_text` returns metadata
and text; `fs_read_file` retains the text-only result. Old Toolserver visual
imports alias the portable modules, so there is one implementation to maintain.

## The `imports/` layer

`hugpy_tools/imports/` is the reference for the workspace-wide `imports/`
layout ([docs/packages.md](../../README.md#the-imports-layout-convention)).
Importing it performs no I/O and loads no optional backend:

| module | contents |
| --- | --- |
| `constants.py` | static defaults: `DEFAULT_COMMAND_TIMEOUT`, `DEFAULT_VM_URI`, `DEFAULT_RENDER_TIMEOUT_MS` |
| `functions.py` | `lazy("module:attr")` (import on first call, so a missing backend raises `ToolError` instead of breaking startup), `first_lazy(*paths)`, `strip_remote_kwargs(kwargs)` |
| `classes.py` | `ToolError` |
| `init_imports.py` | one-stop: `json`, `logging`, `os`, `re`, `sys`, `time`, `C` (the constants) and the shared names |

```python
from hugpy_tools.imports import constants as C
from hugpy_tools.imports.functions import lazy
from hugpy_tools.imports.classes import ToolError
from hugpy_tools.imports.init_imports import *   # the house one-stop surface
```

Other shared modules: `schema.build_parameters(func)` builds a JSON-schema
`inputSchema` from a function signature, and `toolset.get_toolset()`,
`list_tools()` and `call_tool()` are the registry.

## Built-in tools

Every `fs` function takes an **already-confined** absolute path — call
`paths.confine(root, path)` first to enforce a workspace jail (rejects `..`,
absolute-outside, and symlink escapes in one check).

| Resources group / Tools capability | Function | What it does |
| --- | --- | --- |
| `paths` | `confine(root, path, for_write=False)` | Resolve a path under `root`; raise `PathEscape` on any escape. |
| `paths` | `file_parts(path)` | Decompose into dir/base/name/ext + two enclosing dir levels. |
| `paths` | `sanitize_filename(name)` | Reduce to one safe path component, length-bounded. |
| `fs` | `read_text(path, max_bytes=None)` | Read with encoding detection (BOM → utf-8 → cp1252). |
| `fs` | `read_lines(path, start, end, number=False)` | 1-based inclusive line-range read. |
| `fs` | `atomic_write(path, content, append=False)` | Durable write (temp file + `os.replace`); append supported. |
| `fs` | `edit_replace(path, old, new, count=None)` | Exact string replace with an occurrence-count contract; atomic. |
| `fs` | `file_info(path, hash_files=False)` | Size, mtime, type, symlink flag, optional sha256. |
| `fs` | `list_dir(path, glob=None, ...)` | Immediate entries (dirs first), optional fnmatch. |
| `fs` | `tree(path, max_depth=3, max_entries=500)` | Bounded recursive tree text (never follows dir symlinks). |
| `hashkit` | `sha256_text` / `sha256_file` / `quick_hash` | Content hashing (files streamed in 1 MiB windows). |
| `text` | `count_tokens(text)` | Dependency-free approximate BPE token count. |
| `text` | `chunk_by_lines` / `chunk_by_tokens` | Chunk on line or token budgets (paragraph-aware). |
| `text` | `unified_diff(before, after)` | Unified diff between two texts. |
| `data` | `read_data(path, fmt=None)` | Parse JSON always, TOML via stdlib `tomllib`, YAML if PyYAML present. |
| `data` | `write_json(path, obj)` / `safe_json_dumps(obj)` | Atomic JSON write / non-exploding dumps. |
| `timekit` | `now_iso` / `now_epoch` / `epoch_to_iso` / `iso_to_epoch` | UTC time conversions. |
| `web` | `assess_webpage(url, ...)` | assessManager-parity structured page assessment over urllib + `html.parser`. |
| `web` | `prescreen_webpage(url, ...)` | Cheap title + description + lede relevance pre-screen. |

### Web assessment

`assess_webpage` returns the same dict shape as
`abstract_webtools.assessManager.assess_webpage`
(`{url, title, description, text, metadata, jsonld, links, truncated, render}`)
plus additive keys `final_url`, `status`, `canonical`, `lang`, `headings`, and
`error`. The fetch is http(s)-only, refuses redirects to other schemes,
disables `file://` / `ftp://`, caps the body, times out, handles gzip/deflate,
and detects charset (Content-Type → BOM → `<meta charset>` → utf-8 → cp1252).

JS rendering is opt-in and lazy: `force_render=True` (or an automatic fall-back
when the cheap fetch yields almost no text) drives a headless browser **only if
Playwright or Selenium is installed**, and raises a clear "install X" error
otherwise. The default path never needs a browser.

## Layer

Foundation. Core imports use only the standard library. Optional integrations
are loaded on use from `compat`, `backends`, and `visual`; they must not import
`abstract_toolserver`. The host owns authorization and resource lifecycle.

## Shared document tools

Install `hugpy-tools[documents]` or `abstract-toolserver[documents]` to use
`document_extract`, `document_inspect`, `document_page`, and `document_search`.
They call the same `media_intelligence.documents` engine as Media directly,
without a Media HTTP server. Extraction runs in a bounded child process and
returns cached artifacts, page references, geometry and completeness status.
OCR defaults to `off`; `auto` and `force` use the configured Hugpy OCR backend.
Pass the same `store_root` to extraction and later page/search calls when using
a custom artifact store. File authorization belongs to the caller, as for the
other local file tools.

The base package remains light. Parsers load only when a document tool is
called. The `media` extra includes document support, and no longer installs
legacy OCR/pandas packages or local model runtimes. Existing `media_*` model
tools retain the Media transport and their existing contracts.


## Shared implementations in Resources

The dependency runs one way: **hugpy-tools → hugpy-resources**. Resources owns
reusable functions and constants. Tools owns registration, argument schemas,
dispatch, compatibility with backend contracts, and optional integrations.

| Generic capability | Resources implementation |
| --- | --- |
| `paths` | `imports/functions/paths.py` and `imports/classes.py` |
| `hashkit` | `imports/functions/hashing.py` |
| `fs` | `imports/functions/filesystem.py` |
| `data` | `imports/functions/structured_data.py` and `json_utils.py` |
| `text` | `imports/functions/text.py` |
| `timekit` | `imports/functions/time_utils.py` |
| `search` | `imports/functions/search.py` |
| `process.run_command` | `imports/functions/processes.py`, with Tools supplying its timeout policy |

The old generic Tools modules have been removed. Consumers import Resources
directly; the Tools registry registers the same callables under its existing
tool names. Function signatures, return structures, exception behavior and
search filters retain their contracts. Search cancellation, provider objects
and reports live only in Resources. JSON serialization and file
hashing retain their distinct contracts; a quick hash remains a prefilter, and
a plain SHA256 string remains separate from a stable-file proof record.

Local install scripts install an archived Resources wheel before Tools. Offline
builders include the staged Resources wheel, and push prerequisite ordering
places Resources before Tools. Resources' new `ops.sh` uses the same archive,
local install and publish workflows as the other staged Python packages.
