BeanVision Python SDK
BeanVision is a typed Python SDK for working with coffee data across the full coffee lifecycle.
It provides:
- A generated OpenAPI client
- A stable high-level Python client
- Grouped service interfaces
- Coffee-focused domain objects
- Cross-domain analytics
- Deterministic recommendations
- Type checking, linting, formatting, and automated tests
- Support for Green Coffee, Roast, Extraction, and Cupping data
Requirements
Python 3.12 or newer.
Installation
From the sdk directory:
python -m pip install -e .
Install development dependencies:
python -m pip install -e ".[dev]"
Quick start
from beanvision import BeanVisionClient
with BeanVisionClient(
base_url="http://localhost:8000",
api_key="your-api-key",
) as client:
coffee = client.passports.coffee("passport-001")
print(coffee.summary.title)
print(coffee.green.moisture)
print(coffee.roast.agtron)
print(coffee.extraction.tds)
print(coffee.cupping.total_score)
Coffee Passport
CoffeePassport provides a high-level view across the complete coffee lifecycle:
coffee = client.passports.coffee("passport-001")
It exposes:
coffee.green
coffee.roast
coffee.extraction
coffee.cupping
coffee.analytics
coffee.recommendations
Green Coffee
coffee.green.moisture
coffee.green.water_activity
coffee.green.density
coffee.green.color
coffee.green.screen_size
coffee.green.defect_count
coffee.green.process
coffee.green.variety
Roast
coffee.roast.color
coffee.roast.agtron
coffee.roast.batch_size
coffee.roast.charge_temperature
coffee.roast.turning_point
coffee.roast.drying_end
coffee.roast.first_crack
coffee.roast.drop_temperature
coffee.roast.development_time
coffee.roast.development_percent
coffee.roast.total_time
coffee.roast.weight_loss
coffee.roast.machine
coffee.roast.profile
coffee.roast.notes
Extraction
coffee.extraction.dose
coffee.extraction.beverage_weight
coffee.extraction.brew_ratio
coffee.extraction.tds
coffee.extraction.extraction_yield
coffee.extraction.water_temperature
coffee.extraction.brew_time
coffee.extraction.grinder
coffee.extraction.grind_setting
coffee.extraction.recipe
coffee.extraction.notes
Extraction target helpers:
coffee.extraction.target_range
coffee.extraction.is_under_extracted
coffee.extraction.is_over_extracted
coffee.extraction.within_target_range
Cupping
coffee.cupping.total_score
coffee.cupping.fragrance
coffee.cupping.flavor
coffee.cupping.aftertaste
coffee.cupping.acidity
coffee.cupping.body
coffee.cupping.balance
coffee.cupping.sweetness
coffee.cupping.clean_cup
coffee.cupping.uniformity
coffee.cupping.overall
coffee.cupping.defects
coffee.cupping.descriptors
coffee.cupping.notes
Analytics
Every CoffeePassport exposes:
coffee.analytics
Summary
summary = coffee.analytics.summary
print(summary.green_moisture)
print(summary.roast_agtron)
print(summary.extraction_yield)
print(summary.cupping_total_score)
Completeness
completeness = coffee.analytics.completeness
print(completeness.green)
print(completeness.roast)
print(completeness.extraction)
print(completeness.cupping)
print(completeness.overall)
Completeness values are percentages from 0 to 100.
Quality flags
flags = coffee.analytics.quality_flags
print(flags.has_missing_data)
print(flags.missing_count)
print(flags.missing_green_density)
print(flags.missing_roast_agtron)
print(flags.missing_extraction_tds)
print(flags.missing_cupping_score)
Timeline summary
timeline = coffee.analytics.timeline_summary
print(timeline.total_observations)
print(timeline.current_observations)
print(timeline.non_current_observations)
print(timeline.first_observation_at)
print(timeline.latest_observation_at)
print(timeline.domains_present)
Benchmarking
Run the default light-roast benchmark:
benchmark = coffee.analytics.benchmark()
Each value is classified as:
below
within
above
missing
Useful aggregate properties:
benchmark.all_within
benchmark.has_missing
benchmark.out_of_range_count
Custom benchmark profiles are supported:
from beanvision.analytics import (
BenchmarkRange,
CoffeeBenchmarkProfile,
)
profile = CoffeeBenchmarkProfile(
green_moisture=BenchmarkRange(9.5, 11.5),
green_water_activity=BenchmarkRange(0.45, 0.60),
green_density=BenchmarkRange(680.0, 780.0),
roast_agtron=BenchmarkRange(75.0, 90.0),
roast_development_percent=BenchmarkRange(10.0, 16.0),
roast_weight_loss=BenchmarkRange(10.0, 14.5),
extraction_tds=BenchmarkRange(1.15, 1.55),
extraction_yield=BenchmarkRange(18.0, 22.0),
cupping_total_score=BenchmarkRange(80.0, 100.0),
)
benchmark = coffee.analytics.benchmark(profile)
Recommendations
Every CoffeePassport exposes:
coffee.recommendations
Generate deterministic recommendations:
recommendations = coffee.recommendations.generate()
Each recommendation provides:
recommendation.severity
recommendation.category
recommendation.title
recommendation.explanation
recommendation.suggestion
recommendation.is_actionable
Example:
for recommendation in recommendations:
print(recommendation.severity, recommendation.title)
print(recommendation.explanation)
print(recommendation.suggestion)
Recommendations are generated from:
- Data-quality flags
- Green Coffee benchmarks
- Roast benchmarks
- Extraction benchmarks
- Cupping benchmarks
A custom benchmark profile can also be supplied:
recommendations = coffee.recommendations.generate(profile)
Services
The SDK groups API operations into service objects:
client.lots
client.observations
client.passports
Lots
lot = client.lots.create(
human_id="LOT-ETH-001",
name="Ethiopia Guji",
origin_country="Ethiopia",
)
lots = client.lots.list()
Observations
observation = client.observations.create(
subject_id="lot-001",
observation_type="green.moisture",
source_type="difluid",
source_id="omix-plus-001",
value_numeric=10.5,
unit="percent",
)
observations = client.observations.list_for_lot("lot-001")
Passports
passports = client.passports.list(
organization_id="org-001",
status="draft",
country="Ethiopia",
tag="washed",
limit=50,
offset=0,
)
summary = client.passports.get("passport-001")
detail = client.passports.detail("passport-001")
timeline = client.passports.timeline(
"passport-001",
limit=100,
offset=0,
)
coffee = client.passports.coffee("passport-001")
Observation types
Use the canonical registry instead of raw strings where practical:
from beanvision.observation_types import ObservationType
ObservationType.GREEN_MOISTURE
ObservationType.ROAST_AGTRON
ObservationType.EXTRACTION_TDS
ObservationType.CUPPING_TOTAL_SCORE
Error handling
from beanvision.exceptions import (
BeanVisionAPIError,
BeanVisionAuthenticationError,
BeanVisionConflictError,
BeanVisionNotFoundError,
BeanVisionValidationError,
)
Example:
from beanvision.exceptions import BeanVisionNotFoundError
try:
coffee = client.passports.coffee("missing-passport")
except BeanVisionNotFoundError as exc:
print(exc)
Examples
A guided learning path is available in:
sdk/examples/
Start with:
python examples/01_connect.py
The flagship Analytics and Recommendations example is:
python examples/11_analytics_and_recommendations.py
See sdk/examples/README.md for the complete walkthrough.
Development
Run the quality checks from the sdk directory:
black beanvision tests examples
ruff check beanvision tests examples
mypy beanvision
pytest -q
Check formatting without modifying files:
black --check beanvision tests examples
Generated client
The generated OpenAPI client lives in:
sdk/generated/
The public beanvision package wraps the generated client with a stable, coffee-focused API.
Regenerate it from the repository root with:
./scripts/generate_sdk.sh
Continuous integration
GitHub Actions runs:
- Black
- Ruff
- MyPy
- Pytest
Workflow:
.github/workflows/sdk-quality.yml
Project structure
sdk/
├── beanvision/
│ ├── analytics/
│ ├── recommendations/
│ ├── client.py
│ ├── cupping.py
│ ├── extraction.py
│ ├── green.py
│ ├── observation_domain.py
│ ├── observation_types.py
│ ├── passport.py
│ └── roast.py
├── examples/
├── generated/
├── tests/
├── README.md
└── pyproject.toml
Versioning
The SDK follows Semantic Versioning.
Current development version:
1.2.0
License
See the repository-level LICENSE file.
Metadata
Release files for beanvision-sdk 1.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| beanvision_sdk-1.2.1.tar.gz | 35.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| beanvision_sdk-1.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.8 kB
Release files / beanvision_sdk-1.2.1.tar.gz
| Download URL | beanvision_sdk-1.2.1.tar.gz |
|---|---|
| Size | 35.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f863c0552ff63ba58a239cdc5ec9584dee575d9334784e2f835aaee883cb1254
|
|
BLAKE2b-256 checksum How to use checksums |
1c3d1799c1f5cd11a0b81b0e9ad6b5a84a69a40cb5f689f3ea973aca3fbc963c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 6, 2026.
Transparency logRelease files / beanvision_sdk-1.2.1-py3-none-any.whl
| Download URL | beanvision_sdk-1.2.1-py3-none-any.whl |
|---|---|
| Size | 26.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a459ad9ff2007eaa8b2a2b08eee126e31c625a37618cf2369b1ac9659839c14a
|
|
BLAKE2b-256 checksum How to use checksums |
53647bebaa52c97f5eea2edf9a09d1e093374b18e7720308b1bca0e32664e968
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 6, 2026.
Transparency logSecurity for beanvision-sdk 1.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for beanvision_sdk-1.2.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 6, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.