Skip to content

Commit 33f01a2

Browse files
authored
doc: documentation mega issue sprint (#492)
#### Relevant issue or PR Fixes #489 Fixes #462 Fixes #461 (minus "guidance around composing complex pipelines with Tesseract", should we open a new issue for that?) Fixes #452 Fixes #450 Fixes #258 #### Description of changes Tons of new docs. New README, too. #### Testing done :eyes:
1 parent 8c51aaf commit 33f01a2

12 files changed

Lines changed: 686 additions & 51 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,6 @@ uv.lock.bak
147147

148148
# Logs folders written by tests
149149
logs/
150+
151+
# Claude Code
152+
.claude/

AGENTS.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Notes for AI Agents
2+
3+
This file contains counter-intuitive aspects of the Tesseract codebase that AI agents should know.
4+
5+
## Environment setup
6+
7+
- **Use `uv` for dependency management.** Install with `uv pip install -e ".[dev]"`. This is faster and more reliable than plain pip.
8+
- **Run `pre-commit install`** after cloning to set up git hooks.
9+
10+
## Testing
11+
12+
- **Prefer end-to-end tests over unit tests.** Tests that build and run real Tesseracts catch more bugs than mocked unit tests.
13+
- **Avoid mocks.** If you need complex mocking, write an end-to-end test instead.
14+
- **Don't test implementation details.** Tests should verify behavior, not internal structure.
15+
- **Be mindful of slow tests.** End-to-end tests are slow. Check if an existing test can be extended before adding a new one, especially if adding new Docker images to build.
16+
- **Fast tests vs end-to-end tests.** Tests in `tests/endtoend_tests/` require Docker and are skipped automatically when Docker isn't available (or via `--skip-endtoend`). Other tests in `tests/` are fast and don't require Docker. Use fast tests for quick iteration during development.
17+
- **Don't add mocks for Docker.** Tests that need Docker should be marked as end-to-end tests and skipped in fast test runs.
18+
- **Rarely test exceptions.** Only test exception handling when control flow is complex or the error message is critical for UX. Don't write tests that just verify an exception is raised.
19+
- **Never skip or disable tests without asking.** If a test is failing and you want to skip it, ask the user first. Don't add `@pytest.skip`, `@pytest.mark.xfail`, or comment out tests without explicit approval.
20+
21+
## Code style
22+
23+
- **Follow existing patterns.** Look at similar code in the codebase and match its style. Don't introduce new patterns without good reason.
24+
- **Use pre-commit.** Run `pre-commit run --all-files` before committing. Hooks include Ruff for linting/formatting.
25+
- **Follow conventional commits.** PR titles must follow the format: `type[(scope)]: description` (e.g., `feat(sdk): add new feature`).
26+
27+
## Architecture
28+
29+
- **The runtime is separate from the CLI.** `tesseract_core.runtime` runs inside containers; `tesseract_core.sdk` and CLI run on the host.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
See [AGENTS.md](AGENTS.md) for project conventions, testing guidelines, and architecture notes.

CONTRIBUTING.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,50 @@ $ pytest --skip-endtoend
8888
$ pytest --always-run-endtoend tests/endtoend_tests
8989
```
9090

91+
#### Testing philosophy
92+
93+
We follow these principles when writing tests:
94+
95+
- **Prefer end-to-end tests over unit tests** — Tests that exercise real
96+
Tesseract builds and invocations catch more bugs than isolated unit tests.
97+
When in doubt, write an end-to-end test.
98+
99+
- **Avoid mocks where feasible** — Mocks can hide real integration issues.
100+
If a test requires complex mocking to work, consider whether an end-to-end
101+
test would be more valuable.
102+
103+
- **Don't test implementation details** — Tests should verify behavior, not
104+
internal structure. If refactoring breaks your test but not the actual
105+
functionality, the test was too tightly coupled.
106+
107+
- **Be mindful of slow tests** — End-to-end tests that build Tesseracts are
108+
slow. Before adding a new one, check if an existing test can be extended,
109+
or if a faster unit test would suffice for your specific case.
110+
111+
### Linting and code quality
112+
113+
We use [pre-commit](https://pre-commit.com/) to run linters and formatters
114+
automatically before each commit. The hooks are configured in
115+
`.pre-commit-config.yaml` and include:
116+
117+
- **Ruff** — Fast Python linter and formatter (replaces flake8, isort, black)
118+
- **Various file checks** — Trailing whitespace, YAML validation, etc.
119+
120+
To run all pre-commit hooks manually on all files:
121+
122+
```console
123+
$ pre-commit run --all-files
124+
```
125+
126+
To run a specific hook:
127+
128+
```console
129+
$ pre-commit run ruff --all-files
130+
```
131+
132+
Pre-commit also handles automatic dependency updates via Dependabot-style
133+
version bumps. These are configured to run periodically in CI.
134+
91135
### GitHub workflow
92136

93137
This project uses Git for version control and follows a GitHub workflow. To

README.md

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,68 +5,99 @@
55

66
### Tesseract Core
77

8-
Universal, autodiff-native software components for Simulation Intelligence. :package:
8+
Universal, autodiff-native software components for [Simulation Intelligence](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/misc/faq.html#what-is-simulation-intelligence) 📦
99

1010
[Read the docs](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/) |
1111
[Report an issue](https://github.com/pasteurlabs/tesseract-core/issues) |
12-
[Talk to the community](https://si-tesseract.discourse.group/) |
12+
[Community forum](https://si-tesseract.discourse.group/) |
1313
[Contribute](https://github.com/pasteurlabs/tesseract-core/blob/main/CONTRIBUTING.md)
1414

1515
---
1616

1717
[![DOI](https://joss.theoj.org/papers/10.21105/joss.08385/status.svg)](https://doi.org/10.21105/joss.08385)
18+
[![SciPy](https://img.shields.io/badge/SciPy-2025-blue)](https://proceedings.scipy.org/articles/kvfm5762)
1819

19-
**Tesseract Core** bundles:
20+
## The problem
2021

21-
1. Tools to define, create, and run Tesseracts, via the `tesseract` CLI and `tesseract_core` Python API.
22-
2. The Tesseract Runtime, a lightweight, high-performance execution environment for Tesseracts.
22+
Real-world scientific workflows span multiple tools, languages, and computing environments. You might have a mesh generator in C++, a solver in Julia, and post-processing in Python. Getting these to work together is painful. Getting gradients to flow through them for optimization is nearly impossible.
2323

24-
## What is a Tesseract?
24+
Existing autodiff frameworks work great within a single codebase, but fall short when your pipeline crosses framework boundaries or includes legacy/commercial tools.
2525

26-
Tesseracts are components that expose experimental, research-grade software to the world. They are self-contained, self-documenting, and self-executing, via command line and HTTP. They are designed to be easy to create, easy to use, and easy to share, including in a production environment. This repository contains all you need to define your own and execute them.
26+
## The solution
2727

28-
Tesseracts provide built-in support for [differentiable programming](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/introduction/differentiable-programming.html) by propagating gradient information at the level of individual components, making it easy to build complex, diverse software pipelines that can be optimized end-to-end.
28+
Tesseract packages scientific software into **self-contained, portable components** that:
29+
30+
- **Run anywhere** — Local machines, cloud, HPC clusters. Same container, same results.
31+
- **Expose clean interfaces** — CLI, REST API, and Python SDK. No more deciphering undocumented scripts.
32+
- **Propagate gradients** — Each component can expose derivatives, enabling end-to-end optimization across heterogeneous pipelines.
33+
- **Self-document** — Schemas, types, and API docs are generated automatically.
34+
35+
## Who is this for?
36+
37+
- **Researchers** interfacing with (differentiable) simulators or probabilistic models, or who need to combine tools from different ecosystems
38+
- **R&D engineers** packaging research code for use by others, without spending weeks on DevOps
39+
- **Platform engineers** deploying scientific workloads at scale with consistent interfaces and dependency isolation
40+
41+
## Example: Shape optimization across tools
42+
43+
The [rocket fin optimization case study](https://si-tesseract.discourse.group/t/parametric-shape-optimization-of-rocket-fins-with-ansys-spaceclaim-pyansys-and-tesseract/109) combines three Tesseracts:
44+
45+
```
46+
[SpaceClaim geometry] → [Mesh + SDF] → [PyMAPDL FEA solver]
47+
↑ |
48+
└──────── gradients flow back ─────────┘
49+
```
50+
51+
Each component uses a different differentiation strategy (analytic adjoints, finite differences, JAX autodiff), yet they compose into a single optimizable pipeline.
2952

3053
## Quick start
3154

3255
> [!NOTE]
33-
> Before proceeding, make sure you have a [working installation of Docker](https://docs.docker.com/engine/install/) and a modern Python installation (Python 3.10+); if you prefer Docker Desktop for your platform, see [our extended installation instructions](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/introduction/installation.html#basic-installation).
34-
35-
1. Install Tesseract Core:
56+
> Requires [Docker](https://docs.docker.com/engine/install/) and Python 3.10+.
3657
37-
```bash
38-
$ pip install tesseract-core
39-
```
58+
```bash
59+
$ pip install tesseract-core
4060

41-
2. Build an example Tesseract:
61+
# Clone and build an example
62+
$ git clone https://github.com/pasteurlabs/tesseract-core
63+
$ tesseract build tesseract-core/examples/vectoradd
4264

43-
```bash
44-
$ git clone https://github.com/pasteurlabs/tesseract-core
45-
$ tesseract build tesseract-core/examples/vectoradd
46-
```
65+
# Run it
66+
$ tesseract run vectoradd apply '{"inputs": {"a": [1, 2], "b": [3, 4]}}'
67+
# → {"result": [4.0, 6.0], ...}
4768

48-
3. Display its API documentation:
69+
# Compute the Jacobian
70+
$ tesseract run vectoradd jacobian '{"inputs": {"a": [1, 2], "b": [3, 4]}, "jac_inputs": ["a"], "jac_outputs": ["result"]}'
4971

50-
```bash
51-
$ tesseract apidoc vectoradd
52-
```
72+
# See auto-generated API docs
73+
$ tesseract apidoc vectoradd
74+
```
5375

5476
<p align="center">
5577
<img src="https://github.com/pasteurlabs/tesseract-core/blob/main/docs/img/apidoc-screenshot.png" width="600">
5678
</p>
5779

58-
4. Run the Tesseract:
80+
## Core features
81+
82+
- **Containerized** — Docker-based packaging ensures reproducibility and dependency isolation
83+
- **Multi-interface** — CLI, REST API, and Python SDK for the same component
84+
- **Differentiable** — First-class support for Jacobians, JVPs, and VJPs across component and network boundaries
85+
- **Schema-validated** — Pydantic models define explicit input/output contracts
86+
- **Language-agnostic** — Wrap Python, Julia, C++, [Fortran](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/examples/building-blocks/fortran.html), or any executable behind a thin Python API
87+
88+
## Ecosystem
89+
90+
- **[tesseract-core](https://github.com/pasteurlabs/tesseract-core)** — CLI, Python API, and runtime (this repo)
91+
- **[Tesseract-JAX](https://github.com/pasteurlabs/tesseract-jax)** — Embed Tesseracts as JAX primitives into end-to-end differentiable JAX programs
92+
- **[Tesseract-Streamlit](https://github.com/pasteurlabs/tesseract-streamlit)** — Auto-generate interactive web apps from Tesseracts
5993

60-
```bash
61-
$ tesseract run vectoradd apply '{"inputs": {"a": [1], "b": [2]}}'
62-
{"result":{"object_type":"array","shape":[1],"dtype":"float64","data":{"buffer":[3.0],"encoding":"json"}}}⏎
63-
```
94+
## Learn more
6495

65-
> [!TIP]
66-
> Now you're ready to dive into the [documentation](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/) for more information on
67-
> [installation](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/introduction/installation.html),
68-
> [creating Tesseracts](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/creating-tesseracts/create.html), and
69-
> [invoking them](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/using-tesseracts/use.html).
96+
- [Documentation](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/)
97+
- [Creating your first Tesseract](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/creating-tesseracts/create.html)
98+
- [Differentiable programming guide](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/introduction/differentiable-programming.html)
99+
- [Design patterns](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/creating-tesseracts/design-patterns.html)
100+
- [Example gallery](https://docs.pasteurlabs.ai/projects/tesseract-core/latest/content/examples/example_gallery.html)
70101

71102
## License
72103

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Tesseract Design Patterns
2+
3+
This page provides guidance on common questions around Tesseract design: what should (and shouldn't) be a Tesseract, and how to structure your workflow.
4+
5+
## When to use a Tesseract
6+
7+
Tesseracts are most useful when you need to:
8+
9+
- **Share software across teams** — Package your code so others can use it without understanding implementation details
10+
- **Combine heterogeneous software** — Integrate components written in different languages/frameworks into a unified pipeline
11+
- **Deploy to diverse hardware** — Run the same component on different machines (local, cloud, HPC) without modification
12+
- **Enable gradient-based optimization** — Expose derivatives for use in optimization or calibration workflows, and mix differentiation strategies (finite differences, analytic adjoints, automatic differentiation) across components in the same pipeline
13+
- **Ensure reproducibility** — Capture dependencies and environment in a container for consistent execution
14+
15+
## When NOT to use a Tesseract
16+
17+
Tesseracts add overhead that isn't always justified:
18+
19+
- **Single-user, single-environment workflows** — If you're the only one running the code on a single machine, the containerization overhead may not be worth it
20+
- **Sub-second latency requirements** — Tesseracts are designed for compute kernels that run for at least several seconds; for very fast operations, the container and HTTP overhead becomes significant
21+
- **Tightly coupled iterations** — If your inner loop calls a function millions of times, that function shouldn't be a Tesseract; instead, wrap the entire loop
22+
- **Simple scripts** — A quick data transformation script that won't be reused doesn't need Tesseract packaging
23+
24+
## How granular should Tesseracts be?
25+
26+
One of the most common questions is: _"Should I make one big Tesseract or many small ones?"_
27+
28+
### Prefer fewer, coarser Tesseracts when:
29+
30+
- Operations are tightly coupled and always run together
31+
- Data transfer between steps would be expensive (e.g., large meshes or tensors)
32+
- The combined operation is what users actually want to call
33+
- You need maximum performance (fewer container invocations)
34+
35+
### Prefer more, finer Tesseracts when:
36+
37+
- Components have different hardware requirements (e.g., one needs GPU, one doesn't)
38+
- Components have conflicting dependencies
39+
- Different team members own different parts
40+
- You want to swap out implementations (e.g., different solvers for the same interface)
41+
- Components are reusable across multiple workflows
42+
43+
## Designing good interfaces
44+
45+
### Keep schemas focused
46+
47+
Your `InputSchema` and `OutputSchema` should contain only what's needed for the computation. Avoid:
48+
49+
- Configuration that rarely changes (put it in the Tesseract itself or make it a build-time option)
50+
- Metadata that's not used in computation
51+
- Redundant fields that can be derived from others
52+
53+
### Use meaningful types
54+
55+
```python
56+
# Less clear
57+
class InputSchema(BaseModel):
58+
data: Array[(None, None), Float64] # What is this?
59+
60+
# More clear
61+
class InputSchema(BaseModel):
62+
mesh_coordinates: Array[(None, 3), Float64] = Field(
63+
description="Node coordinates of the mesh (N nodes x 3 dimensions)"
64+
)
65+
```
66+
67+
### Design for composability
68+
69+
If your Tesseract might be chained with others, design interfaces that make this natural:
70+
71+
```python
72+
# Mesh generator output
73+
class MeshOutput(BaseModel):
74+
nodes: Array[(None, 3), Float64]
75+
elements: Array[(None, 4), Int32]
76+
77+
# Solver input (matches mesh output)
78+
class SolverInput(BaseModel):
79+
nodes: Array[(None, 3), Float64]
80+
elements: Array[(None, 4), Int32]
81+
boundary_conditions: BoundaryConditions
82+
```
83+
84+
## Example: Simulation workflow
85+
86+
Consider a CFD simulation workflow with these steps:
87+
88+
1. Generate mesh from CAD geometry
89+
2. Run CFD solver
90+
3. Post-process results
91+
4. Visualize output
92+
93+
**Option A: One Tesseract**
94+
95+
```
96+
CAD → [Mesh + Solve + Post-process + Visualize] → Report
97+
```
98+
99+
Pros: Simple to deploy, no intermediate data transfer\
100+
Cons: Can't swap solver, can't run meshing on CPU while solving on GPU
101+
102+
**Option B: Four Tesseracts**
103+
104+
```
105+
CAD → [Mesh] → [Solve] → [Post-process] → [Visualize] → Report
106+
```
107+
108+
Pros: Maximum flexibility, clear ownership\
109+
Cons: Data transfer overhead, more complex orchestration
110+
111+
**Option C: Two Tesseracts (recommended)**
112+
113+
```
114+
CAD → [Mesh] → [Solve + Post-process + Visualize] → Report
115+
```
116+
117+
Pros: Separates geometry (often CPU-bound, different expertise) from simulation (often GPU-bound, different team), minimal data transfer for tightly coupled steps
118+
119+
The right choice depends on your team structure, hardware constraints, and reuse patterns. When in doubt, start with fewer Tesseracts and split them later if needed — it's easier to break apart than to combine.
120+
121+
## Common anti-patterns
122+
123+
### The "kitchen sink" Tesseract
124+
125+
Don't create a single Tesseract that does everything your project needs. This defeats the purpose of modularity and makes it hard to maintain or reuse.
126+
127+
### Overloading a single Tesseract with mode flags
128+
129+
Tesseracts have one `apply` function. While mode flags can be acceptable in some cases, the default should be to create separate Tesseracts for distinct operations:
130+
131+
```python
132+
# Anti-pattern: mode switching
133+
class InputSchema(BaseModel):
134+
mode: Literal["train", "predict", "evaluate"]
135+
...
136+
137+
# Better: separate Tesseracts
138+
# - model-trainer (for training)
139+
# - model-predictor (for inference)
140+
# - model-evaluator (for evaluation)
141+
```
142+
143+
### Stateful operations
144+
145+
Tesseracts are designed to be stateless and context-free. Each call to `apply` should be independent. If you need state:
146+
147+
- Pass it explicitly in the input schema
148+
- Store it in external storage (files, databases) and reference it by path
149+
- Reconsider whether a Tesseract is the right abstraction

0 commit comments

Comments
 (0)