penampakan 0.1.0__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.
- penampakan-0.1.0/.github/workflows/ci.yml +38 -0
- penampakan-0.1.0/.gitignore +14 -0
- penampakan-0.1.0/CHANGELOG +12 -0
- penampakan-0.1.0/IMPLEMENTATION_SPEC.md +51 -0
- penampakan-0.1.0/LICENSE +21 -0
- penampakan-0.1.0/PKG-INFO +200 -0
- penampakan-0.1.0/README.md +160 -0
- penampakan-0.1.0/benchmarks/benchmark_metadata.py +783 -0
- penampakan-0.1.0/benchmarks/metadata_latency.png +0 -0
- penampakan-0.1.0/pyproject.toml +87 -0
- penampakan-0.1.0/src/penampakan/__init__.py +214 -0
- penampakan-0.1.0/src/penampakan/_version.py +3 -0
- penampakan-0.1.0/src/penampakan/backends/__init__.py +17 -0
- penampakan-0.1.0/src/penampakan/backends/callable.py +106 -0
- penampakan-0.1.0/src/penampakan/backends/pillow.py +206 -0
- penampakan-0.1.0/src/penampakan/backends/tesseract.py +635 -0
- penampakan-0.1.0/src/penampakan/backends/transformers.py +593 -0
- penampakan-0.1.0/src/penampakan/client.py +398 -0
- penampakan-0.1.0/src/penampakan/config.py +154 -0
- penampakan-0.1.0/src/penampakan/errors.py +363 -0
- penampakan-0.1.0/src/penampakan/evaluation.py +174 -0
- penampakan-0.1.0/src/penampakan/image/__init__.py +5 -0
- penampakan-0.1.0/src/penampakan/image/assets.py +462 -0
- penampakan-0.1.0/src/penampakan/image/canonical.py +39 -0
- penampakan-0.1.0/src/penampakan/image/geometry.py +407 -0
- penampakan-0.1.0/src/penampakan/image/loader.py +344 -0
- penampakan-0.1.0/src/penampakan/image/transforms.py +307 -0
- penampakan-0.1.0/src/penampakan/llms/__init__.py +5 -0
- penampakan-0.1.0/src/penampakan/llms/callable.py +60 -0
- penampakan-0.1.0/src/penampakan/models.py +957 -0
- penampakan-0.1.0/src/penampakan/perception/__init__.py +1 -0
- penampakan-0.1.0/src/penampakan/perception/cache.py +364 -0
- penampakan-0.1.0/src/penampakan/perception/normalize.py +413 -0
- penampakan-0.1.0/src/penampakan/perception/registry.py +175 -0
- penampakan-0.1.0/src/penampakan/perception/router.py +599 -0
- penampakan-0.1.0/src/penampakan/perception/store.py +300 -0
- penampakan-0.1.0/src/penampakan/protocols.py +88 -0
- penampakan-0.1.0/src/penampakan/py.typed +0 -0
- penampakan-0.1.0/src/penampakan/reasoning/__init__.py +1 -0
- penampakan-0.1.0/src/penampakan/reasoning/actions.py +279 -0
- penampakan-0.1.0/src/penampakan/reasoning/answer.py +125 -0
- penampakan-0.1.0/src/penampakan/reasoning/budget.py +279 -0
- penampakan-0.1.0/src/penampakan/reasoning/context.py +530 -0
- penampakan-0.1.0/src/penampakan/reasoning/policy.py +84 -0
- penampakan-0.1.0/src/penampakan/reasoning/prompts.py +372 -0
- penampakan-0.1.0/src/penampakan/session.py +1410 -0
- penampakan-0.1.0/src/penampakan/sync.py +478 -0
- penampakan-0.1.0/src/penampakan/tools/__init__.py +1 -0
- penampakan-0.1.0/src/penampakan/tools/builtin.py +254 -0
- penampakan-0.1.0/src/penampakan/tools/vision.py +225 -0
- penampakan-0.1.0/src/penampakan/tracing.py +591 -0
- penampakan-0.1.0/tests/__init__.py +1 -0
- penampakan-0.1.0/tests/contract/__init__.py +1 -0
- penampakan-0.1.0/tests/contract/test_backend_contract.py +81 -0
- penampakan-0.1.0/tests/contract/test_distribution.py +45 -0
- penampakan-0.1.0/tests/contract/test_json_schemas.py +313 -0
- penampakan-0.1.0/tests/contract/test_protocols.py +279 -0
- penampakan-0.1.0/tests/contract/test_trace_redaction.py +126 -0
- penampakan-0.1.0/tests/e2e/__init__.py +1 -0
- penampakan-0.1.0/tests/e2e/test_workflows.py +266 -0
- penampakan-0.1.0/tests/fixtures/__init__.py +1 -0
- penampakan-0.1.0/tests/fixtures/images.py +52 -0
- penampakan-0.1.0/tests/integration/__init__.py +1 -0
- penampakan-0.1.0/tests/unit/__init__.py +1 -0
- penampakan-0.1.0/tests/unit/backends/__init__.py +1 -0
- penampakan-0.1.0/tests/unit/backends/test_callable.py +114 -0
- penampakan-0.1.0/tests/unit/backends/test_pillow.py +94 -0
- penampakan-0.1.0/tests/unit/backends/test_tesseract.py +415 -0
- penampakan-0.1.0/tests/unit/backends/test_transformers.py +426 -0
- penampakan-0.1.0/tests/unit/image/__init__.py +1 -0
- penampakan-0.1.0/tests/unit/image/test_assets.py +120 -0
- penampakan-0.1.0/tests/unit/image/test_fuzz.py +15 -0
- penampakan-0.1.0/tests/unit/image/test_geometry.py +97 -0
- penampakan-0.1.0/tests/unit/image/test_geometry_contract.py +133 -0
- penampakan-0.1.0/tests/unit/image/test_loader.py +193 -0
- penampakan-0.1.0/tests/unit/image/test_transforms.py +121 -0
- penampakan-0.1.0/tests/unit/llms/__init__.py +0 -0
- penampakan-0.1.0/tests/unit/llms/test_callable.py +96 -0
- penampakan-0.1.0/tests/unit/perception/__init__.py +0 -0
- penampakan-0.1.0/tests/unit/perception/test_cache.py +317 -0
- penampakan-0.1.0/tests/unit/perception/test_normalize.py +348 -0
- penampakan-0.1.0/tests/unit/perception/test_registry.py +129 -0
- penampakan-0.1.0/tests/unit/perception/test_router.py +440 -0
- penampakan-0.1.0/tests/unit/perception/test_store.py +436 -0
- penampakan-0.1.0/tests/unit/reasoning/__init__.py +0 -0
- penampakan-0.1.0/tests/unit/reasoning/helpers.py +214 -0
- penampakan-0.1.0/tests/unit/reasoning/test_actions.py +86 -0
- penampakan-0.1.0/tests/unit/reasoning/test_answer.py +208 -0
- penampakan-0.1.0/tests/unit/reasoning/test_budget.py +260 -0
- penampakan-0.1.0/tests/unit/reasoning/test_context.py +296 -0
- penampakan-0.1.0/tests/unit/reasoning/test_policy.py +155 -0
- penampakan-0.1.0/tests/unit/reasoning/test_prompts.py +150 -0
- penampakan-0.1.0/tests/unit/test_client.py +571 -0
- penampakan-0.1.0/tests/unit/test_config.py +62 -0
- penampakan-0.1.0/tests/unit/test_errors.py +43 -0
- penampakan-0.1.0/tests/unit/test_evaluation.py +118 -0
- penampakan-0.1.0/tests/unit/test_imports.py +55 -0
- penampakan-0.1.0/tests/unit/test_models.py +146 -0
- penampakan-0.1.0/tests/unit/test_session.py +1024 -0
- penampakan-0.1.0/tests/unit/test_sync.py +524 -0
- penampakan-0.1.0/tests/unit/test_tracing.py +232 -0
- penampakan-0.1.0/tests/unit/tools/__init__.py +0 -0
- penampakan-0.1.0/tests/unit/tools/test_builtin.py +107 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
cache: pip
|
|
20
|
+
- run: python -m pip install -e '.[dev]'
|
|
21
|
+
- run: ruff check .
|
|
22
|
+
- run: ruff format --check .
|
|
23
|
+
- run: mypy --strict src/penampakan
|
|
24
|
+
- run: pytest -m 'not models and not ocr' --cov=penampakan --cov-fail-under=90
|
|
25
|
+
- run: python -m build
|
|
26
|
+
- run: python -m twine check dist/*
|
|
27
|
+
|
|
28
|
+
windows-smoke:
|
|
29
|
+
runs-on: windows-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
- uses: actions/setup-python@v5
|
|
33
|
+
with:
|
|
34
|
+
python-version: "3.13"
|
|
35
|
+
cache: pip
|
|
36
|
+
- run: python -m pip install -e '.[dev]'
|
|
37
|
+
- run: python -c "import penampakan"
|
|
38
|
+
- run: pytest -m "not models and not ocr"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Penampakan 0.1.0
|
|
2
|
+
Released 2026-08-10
|
|
3
|
+
|
|
4
|
+
Initial release of bounded visual tool orchestration for text-only language
|
|
5
|
+
models. It includes strict public contracts, safe image normalization,
|
|
6
|
+
deterministic perception routing, evidence-grounded question answering,
|
|
7
|
+
reusable asynchronous and synchronous sessions, optional local model adapters,
|
|
8
|
+
redacted tracing, and experimental evaluation metrics.
|
|
9
|
+
|
|
10
|
+
Metadata inspection now reuses the loader's canonical image, uses balanced PNG
|
|
11
|
+
compression, and reports reusable-session latency plus executed normalization
|
|
12
|
+
and safety checks in the benchmark.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Penampakan implementation specification
|
|
2
|
+
|
|
3
|
+
Status: proposed v0.1 specification
|
|
4
|
+
|
|
5
|
+
Last updated: 2026-08-10
|
|
6
|
+
|
|
7
|
+
Penampakan is a Python library that lets a text-only language model answer
|
|
8
|
+
questions about static images. It does this by translating image evidence into
|
|
9
|
+
typed, attributable text observations and letting the language model request
|
|
10
|
+
additional bounded vision operations when the initial evidence is insufficient.
|
|
11
|
+
|
|
12
|
+
This document set is normative for the first implementation. Read it in order:
|
|
13
|
+
|
|
14
|
+
1. [Research and product scope](docs/specs/00-research-and-product.md)
|
|
15
|
+
2. [Architecture](docs/specs/01-architecture.md)
|
|
16
|
+
3. [Public API and data contracts](docs/specs/02-public-api-and-contracts.md)
|
|
17
|
+
4. [Runtime, tools, and backends](docs/specs/03-runtime-tools-and-backends.md)
|
|
18
|
+
5. [Delivery plan and quality gates](docs/specs/04-delivery-and-quality.md)
|
|
19
|
+
|
|
20
|
+
The words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are requirements in the
|
|
21
|
+
sense used by RFC 2119. A coding agent must not silently replace a MUST with a
|
|
22
|
+
different design. If a requirement proves impractical, record the deviation in
|
|
23
|
+
an architecture decision record before implementing it.
|
|
24
|
+
|
|
25
|
+
## One-paragraph implementation target
|
|
26
|
+
|
|
27
|
+
Build an async-first, provider-neutral package named `penampakan`. It must load
|
|
28
|
+
and normalize safe raster image inputs, retain immutable original and derived
|
|
29
|
+
image assets, invoke registered vision backends through typed capabilities,
|
|
30
|
+
store every result as a provenance-bearing observation, and run a bounded JSON
|
|
31
|
+
action loop in which a text-only LLM either calls a declared vision tool or
|
|
32
|
+
answers using observation citations. Ship a synchronous facade, deterministic
|
|
33
|
+
fake backends, core Pillow tools, at least one usable caption/detection adapter
|
|
34
|
+
and one OCR adapter, complete contract tests, and examples that work without a
|
|
35
|
+
multimodal LLM.
|
|
36
|
+
|
|
37
|
+
## Definition of success for v0.1
|
|
38
|
+
|
|
39
|
+
The release is complete only when all of the following are true:
|
|
40
|
+
|
|
41
|
+
- A user can call `Penampakan.ask(image, question)` with a text-only LLM and
|
|
42
|
+
receive a `VisionAnswer` containing inspectable evidence references.
|
|
43
|
+
- A user can call `inspect` without configuring any LLM.
|
|
44
|
+
- Vision capabilities are replaceable independently; the orchestrator never
|
|
45
|
+
imports a concrete model package.
|
|
46
|
+
- Unsupported or inconclusive perception produces an explicit
|
|
47
|
+
`insufficient_evidence` result instead of a fabricated answer.
|
|
48
|
+
- Image bytes, OCR text, and prompts are not logged or persisted by default.
|
|
49
|
+
- No LLM-generated Python or shell code is executed in v0.1.
|
|
50
|
+
- Unit, backend-contract, orchestration, security, and end-to-end tests described
|
|
51
|
+
in the quality spec pass on all supported Python versions.
|
penampakan-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Penampakan contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: penampakan
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Safe visual tool orchestration for text-only language models
|
|
5
|
+
Author: Penampakan contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: <3.14,>=3.10
|
|
17
|
+
Requires-Dist: pillow>=10
|
|
18
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
19
|
+
Requires-Dist: typing-extensions>=4.10; python_version < '3.12'
|
|
20
|
+
Provides-Extra: benchmark
|
|
21
|
+
Requires-Dist: imageio>=2.31; extra == 'benchmark'
|
|
22
|
+
Requires-Dist: matplotlib>=3.8; extra == 'benchmark'
|
|
23
|
+
Requires-Dist: numpy>=1.23; extra == 'benchmark'
|
|
24
|
+
Requires-Dist: opencv-python-headless>=4.8; extra == 'benchmark'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
28
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
33
|
+
Requires-Dist: twine>=5; extra == 'dev'
|
|
34
|
+
Provides-Extra: ocr
|
|
35
|
+
Requires-Dist: pytesseract>=0.3.10; extra == 'ocr'
|
|
36
|
+
Provides-Extra: transformers
|
|
37
|
+
Requires-Dist: torch>=2.1; extra == 'transformers'
|
|
38
|
+
Requires-Dist: transformers<5,>=4.40; extra == 'transformers'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# Penampakan
|
|
42
|
+
|
|
43
|
+
Penampakan is an async-first Python library that lets a text-only language
|
|
44
|
+
model reason about static images. It turns results from replaceable vision
|
|
45
|
+
backends into typed, attributable observations, then runs a bounded tool loop
|
|
46
|
+
that requires the model to cite those observations in its answer.
|
|
47
|
+
|
|
48
|
+
The package can also be used without a language model for deterministic image
|
|
49
|
+
inspection. Its built-in Pillow backend provides normalized metadata and
|
|
50
|
+
dominant colors; optional adapters add Tesseract OCR and local Transformers
|
|
51
|
+
captioning or detection.
|
|
52
|
+
|
|
53
|
+
Penampakan is currently alpha software.
|
|
54
|
+
|
|
55
|
+
## Why Penampakan?
|
|
56
|
+
|
|
57
|
+
- Provider-neutral protocols for text LLMs and vision backends.
|
|
58
|
+
- Async clients and sessions, plus a blocking facade for synchronous programs.
|
|
59
|
+
- Immutable Pydantic contracts for assets, observations, evidence, and traces.
|
|
60
|
+
- Bounded tool, backend, LLM, image, and timeout budgets.
|
|
61
|
+
- Safe raster normalization with provenance for derived images.
|
|
62
|
+
- Evidence-grounded answers and redacted tracing by default.
|
|
63
|
+
- No generated Python or shell execution.
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
Penampakan requires Python 3.10 through 3.13.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
python -m pip install penampakan
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Optional integrations are installed separately:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
python -m pip install 'penampakan[ocr]'
|
|
77
|
+
python -m pip install 'penampakan[transformers]'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The OCR extra installs the Python adapter; the Tesseract executable must also
|
|
81
|
+
be available on the system.
|
|
82
|
+
|
|
83
|
+
## Quick start
|
|
84
|
+
|
|
85
|
+
Inspect an image without configuring an LLM:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from penampakan import (
|
|
89
|
+
ColorsRequest,
|
|
90
|
+
InspectionOperation,
|
|
91
|
+
InspectionPlan,
|
|
92
|
+
MetadataRequest,
|
|
93
|
+
Penampakan,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
plan = InspectionPlan(
|
|
97
|
+
operations=(
|
|
98
|
+
InspectionOperation(request=MetadataRequest()),
|
|
99
|
+
InspectionOperation(request=ColorsRequest(count=5)),
|
|
100
|
+
),
|
|
101
|
+
include_available_overview=False,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
with Penampakan() as vision:
|
|
105
|
+
result = vision.inspect("photo.png", plan)
|
|
106
|
+
|
|
107
|
+
for observation in result.observations:
|
|
108
|
+
print(observation.payload)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For evidence-grounded question answering, construct `AsyncPenampakan` or
|
|
112
|
+
`Penampakan` with a `TextLLM` implementation and the vision backends needed for
|
|
113
|
+
the task, then call `ask(image, question)`. `CallableTextLLM` and
|
|
114
|
+
`CallableVisionBackend` are available as adapters for application functions.
|
|
115
|
+
|
|
116
|
+
Input images may be PNG, JPEG, or WebP paths, encoded bytes, binary streams, or
|
|
117
|
+
Pillow images. Remote URLs are rejected by default. Images are orientation
|
|
118
|
+
corrected, bounded by configurable limits, and normalized to canonical RGB or
|
|
119
|
+
RGBA assets before a backend sees them.
|
|
120
|
+
|
|
121
|
+
## Benchmark
|
|
122
|
+
|
|
123
|
+
The benchmark is at
|
|
124
|
+
[`benchmarks/benchmark_metadata.py`](benchmarks/benchmark_metadata.py). It
|
|
125
|
+
compares the latency of an end-to-end Penampakan metadata inspection with
|
|
126
|
+
direct metadata decoding through Pillow, OpenCV, and ImageIO. The fixture is
|
|
127
|
+
generated locally, competitors are run in rotating order over multiple rounds,
|
|
128
|
+
and unavailable optional libraries are clearly reported as skipped. It also
|
|
129
|
+
measures Penampakan's reusable-session workflow and executes representative
|
|
130
|
+
normalization, safety, and attribution checks so the additional work in the
|
|
131
|
+
end-to-end path remains visible.
|
|
132
|
+
|
|
133
|
+
Install the benchmark dependencies and run it from the repository root:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
python -m pip install -e '.[benchmark]'
|
|
137
|
+
python benchmarks/benchmark_metadata.py
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use `--help` to change the image size, warmups, iterations, rounds, or output
|
|
141
|
+
format. For example:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
python benchmarks/benchmark_metadata.py --iterations 50 --rounds 7
|
|
145
|
+
python benchmarks/benchmark_metadata.py --reuse-count 50
|
|
146
|
+
python benchmarks/benchmark_metadata.py --format json
|
|
147
|
+
python benchmarks/benchmark_metadata.py --plot benchmarks/metadata_latency.png
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Reference results captured on 10 August 2026 with Python 3.13.15 on x86-64
|
|
151
|
+
WSL2 (Linux 6.18.33.2, glibc 2.43) are shown below. The run used the default
|
|
152
|
+
640x480 RGBA PNG fixture (45,019 encoded bytes), 3 warmups, 20 iterations, and
|
|
153
|
+
5 rounds. The primary table is the one-shot end-to-end comparison.
|
|
154
|
+
|
|
155
|
+

|
|
156
|
+
|
|
157
|
+
| Library | Version | Median (ms) | Min (ms) | Max (ms) | Calls/s | vs fastest |
|
|
158
|
+
| --- | --- | ---: | ---: | ---: | ---: | ---: |
|
|
159
|
+
| Penampakan | 0.1.0 | 18.846 | 18.781 | 19.030 | 53.1 | 6.85x |
|
|
160
|
+
| Pillow (direct) | 12.3.0 | 2.753 | 2.737 | 2.806 | 363.2 | 1.00x |
|
|
161
|
+
| OpenCV | 5.0.0 | 3.237 | 3.129 | 3.461 | 309.0 | 1.18x |
|
|
162
|
+
| ImageIO | 2.37.4 | 4.431 | 4.347 | 4.470 | 225.7 | 1.61x |
|
|
163
|
+
|
|
164
|
+
The reusable workflow opens and normalizes one image, performs 20 typed
|
|
165
|
+
metadata inspections, and then closes the session. Its median open latency was
|
|
166
|
+
18.114 ms, each warm inspection was 0.494 ms, and close latency was 0.096 ms.
|
|
167
|
+
Including the open and close costs, that is **1.432 ms per inspection
|
|
168
|
+
amortized**, 13.16x faster than Penampakan's one-shot path. This is a
|
|
169
|
+
Penampakan lifecycle measurement, not a ranking against the direct-library
|
|
170
|
+
cases above.
|
|
171
|
+
|
|
172
|
+
The same run passed six executed contract checks:
|
|
173
|
+
|
|
174
|
+
- equivalent normalized metadata across PNG, JPEG, and WebP;
|
|
175
|
+
- EXIF orientation before reported dimensions;
|
|
176
|
+
- removal of opaque alpha while preserving real transparency;
|
|
177
|
+
- documented rejection of malformed and animated inputs;
|
|
178
|
+
- remote-source policy and input-byte bounds; and
|
|
179
|
+
- authoritative backend provenance with a completed redacted trace.
|
|
180
|
+
|
|
181
|
+
This is an overhead benchmark, not a capability or accuracy ranking. The
|
|
182
|
+
Penampakan path performs bounded input handling, orientation and mode
|
|
183
|
+
normalization, canonical encoding and hashing, typed result validation,
|
|
184
|
+
routing, tracing, and session cleanup. The direct alternatives only decode the
|
|
185
|
+
fixture and return equivalent dimensions and alpha metadata. Contract checks
|
|
186
|
+
are reported separately from latency rankings. Compare results on the same
|
|
187
|
+
machine and treat very short runs as smoke tests rather than stable
|
|
188
|
+
measurements.
|
|
189
|
+
|
|
190
|
+
## Development
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
python -m pip install -e '.[dev]'
|
|
194
|
+
ruff check .
|
|
195
|
+
ruff format --check .
|
|
196
|
+
mypy --strict src/penampakan
|
|
197
|
+
pytest -m 'not models and not ocr'
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Penampakan is licensed under the MIT License. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Penampakan
|
|
2
|
+
|
|
3
|
+
Penampakan is an async-first Python library that lets a text-only language
|
|
4
|
+
model reason about static images. It turns results from replaceable vision
|
|
5
|
+
backends into typed, attributable observations, then runs a bounded tool loop
|
|
6
|
+
that requires the model to cite those observations in its answer.
|
|
7
|
+
|
|
8
|
+
The package can also be used without a language model for deterministic image
|
|
9
|
+
inspection. Its built-in Pillow backend provides normalized metadata and
|
|
10
|
+
dominant colors; optional adapters add Tesseract OCR and local Transformers
|
|
11
|
+
captioning or detection.
|
|
12
|
+
|
|
13
|
+
Penampakan is currently alpha software.
|
|
14
|
+
|
|
15
|
+
## Why Penampakan?
|
|
16
|
+
|
|
17
|
+
- Provider-neutral protocols for text LLMs and vision backends.
|
|
18
|
+
- Async clients and sessions, plus a blocking facade for synchronous programs.
|
|
19
|
+
- Immutable Pydantic contracts for assets, observations, evidence, and traces.
|
|
20
|
+
- Bounded tool, backend, LLM, image, and timeout budgets.
|
|
21
|
+
- Safe raster normalization with provenance for derived images.
|
|
22
|
+
- Evidence-grounded answers and redacted tracing by default.
|
|
23
|
+
- No generated Python or shell execution.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
Penampakan requires Python 3.10 through 3.13.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
python -m pip install penampakan
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Optional integrations are installed separately:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python -m pip install 'penampakan[ocr]'
|
|
37
|
+
python -m pip install 'penampakan[transformers]'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The OCR extra installs the Python adapter; the Tesseract executable must also
|
|
41
|
+
be available on the system.
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
Inspect an image without configuring an LLM:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from penampakan import (
|
|
49
|
+
ColorsRequest,
|
|
50
|
+
InspectionOperation,
|
|
51
|
+
InspectionPlan,
|
|
52
|
+
MetadataRequest,
|
|
53
|
+
Penampakan,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
plan = InspectionPlan(
|
|
57
|
+
operations=(
|
|
58
|
+
InspectionOperation(request=MetadataRequest()),
|
|
59
|
+
InspectionOperation(request=ColorsRequest(count=5)),
|
|
60
|
+
),
|
|
61
|
+
include_available_overview=False,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
with Penampakan() as vision:
|
|
65
|
+
result = vision.inspect("photo.png", plan)
|
|
66
|
+
|
|
67
|
+
for observation in result.observations:
|
|
68
|
+
print(observation.payload)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For evidence-grounded question answering, construct `AsyncPenampakan` or
|
|
72
|
+
`Penampakan` with a `TextLLM` implementation and the vision backends needed for
|
|
73
|
+
the task, then call `ask(image, question)`. `CallableTextLLM` and
|
|
74
|
+
`CallableVisionBackend` are available as adapters for application functions.
|
|
75
|
+
|
|
76
|
+
Input images may be PNG, JPEG, or WebP paths, encoded bytes, binary streams, or
|
|
77
|
+
Pillow images. Remote URLs are rejected by default. Images are orientation
|
|
78
|
+
corrected, bounded by configurable limits, and normalized to canonical RGB or
|
|
79
|
+
RGBA assets before a backend sees them.
|
|
80
|
+
|
|
81
|
+
## Benchmark
|
|
82
|
+
|
|
83
|
+
The benchmark is at
|
|
84
|
+
[`benchmarks/benchmark_metadata.py`](benchmarks/benchmark_metadata.py). It
|
|
85
|
+
compares the latency of an end-to-end Penampakan metadata inspection with
|
|
86
|
+
direct metadata decoding through Pillow, OpenCV, and ImageIO. The fixture is
|
|
87
|
+
generated locally, competitors are run in rotating order over multiple rounds,
|
|
88
|
+
and unavailable optional libraries are clearly reported as skipped. It also
|
|
89
|
+
measures Penampakan's reusable-session workflow and executes representative
|
|
90
|
+
normalization, safety, and attribution checks so the additional work in the
|
|
91
|
+
end-to-end path remains visible.
|
|
92
|
+
|
|
93
|
+
Install the benchmark dependencies and run it from the repository root:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
python -m pip install -e '.[benchmark]'
|
|
97
|
+
python benchmarks/benchmark_metadata.py
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use `--help` to change the image size, warmups, iterations, rounds, or output
|
|
101
|
+
format. For example:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
python benchmarks/benchmark_metadata.py --iterations 50 --rounds 7
|
|
105
|
+
python benchmarks/benchmark_metadata.py --reuse-count 50
|
|
106
|
+
python benchmarks/benchmark_metadata.py --format json
|
|
107
|
+
python benchmarks/benchmark_metadata.py --plot benchmarks/metadata_latency.png
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Reference results captured on 10 August 2026 with Python 3.13.15 on x86-64
|
|
111
|
+
WSL2 (Linux 6.18.33.2, glibc 2.43) are shown below. The run used the default
|
|
112
|
+
640x480 RGBA PNG fixture (45,019 encoded bytes), 3 warmups, 20 iterations, and
|
|
113
|
+
5 rounds. The primary table is the one-shot end-to-end comparison.
|
|
114
|
+
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+
| Library | Version | Median (ms) | Min (ms) | Max (ms) | Calls/s | vs fastest |
|
|
118
|
+
| --- | --- | ---: | ---: | ---: | ---: | ---: |
|
|
119
|
+
| Penampakan | 0.1.0 | 18.846 | 18.781 | 19.030 | 53.1 | 6.85x |
|
|
120
|
+
| Pillow (direct) | 12.3.0 | 2.753 | 2.737 | 2.806 | 363.2 | 1.00x |
|
|
121
|
+
| OpenCV | 5.0.0 | 3.237 | 3.129 | 3.461 | 309.0 | 1.18x |
|
|
122
|
+
| ImageIO | 2.37.4 | 4.431 | 4.347 | 4.470 | 225.7 | 1.61x |
|
|
123
|
+
|
|
124
|
+
The reusable workflow opens and normalizes one image, performs 20 typed
|
|
125
|
+
metadata inspections, and then closes the session. Its median open latency was
|
|
126
|
+
18.114 ms, each warm inspection was 0.494 ms, and close latency was 0.096 ms.
|
|
127
|
+
Including the open and close costs, that is **1.432 ms per inspection
|
|
128
|
+
amortized**, 13.16x faster than Penampakan's one-shot path. This is a
|
|
129
|
+
Penampakan lifecycle measurement, not a ranking against the direct-library
|
|
130
|
+
cases above.
|
|
131
|
+
|
|
132
|
+
The same run passed six executed contract checks:
|
|
133
|
+
|
|
134
|
+
- equivalent normalized metadata across PNG, JPEG, and WebP;
|
|
135
|
+
- EXIF orientation before reported dimensions;
|
|
136
|
+
- removal of opaque alpha while preserving real transparency;
|
|
137
|
+
- documented rejection of malformed and animated inputs;
|
|
138
|
+
- remote-source policy and input-byte bounds; and
|
|
139
|
+
- authoritative backend provenance with a completed redacted trace.
|
|
140
|
+
|
|
141
|
+
This is an overhead benchmark, not a capability or accuracy ranking. The
|
|
142
|
+
Penampakan path performs bounded input handling, orientation and mode
|
|
143
|
+
normalization, canonical encoding and hashing, typed result validation,
|
|
144
|
+
routing, tracing, and session cleanup. The direct alternatives only decode the
|
|
145
|
+
fixture and return equivalent dimensions and alpha metadata. Contract checks
|
|
146
|
+
are reported separately from latency rankings. Compare results on the same
|
|
147
|
+
machine and treat very short runs as smoke tests rather than stable
|
|
148
|
+
measurements.
|
|
149
|
+
|
|
150
|
+
## Development
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
python -m pip install -e '.[dev]'
|
|
154
|
+
ruff check .
|
|
155
|
+
ruff format --check .
|
|
156
|
+
mypy --strict src/penampakan
|
|
157
|
+
pytest -m 'not models and not ocr'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Penampakan is licensed under the MIT License. See [LICENSE](LICENSE).
|