# Using LLMs to Create Tesseracts Large Language Models (LLMs) like Claude or GPT-4 can help you create Tesseracts more quickly by generating schemas and boilerplate code. This page provides guidance on how to use LLMs effectively for Tesseract development. ```{note} LLM capabilities are evolving rapidly. The prompting strategies and example outputs on this page reflect our experience as of early 2026. Newer models may require less hand-holding or produce better results with simpler prompts. ``` ## What LLMs are good at LLMs excel at: - **Generating Pydantic schemas** from natural language descriptions or existing code - **Converting existing functions** into Tesseract `apply` endpoints - **Writing boilerplate** for `tesseract_api.py` files - **Suggesting field descriptions** and validation rules - **Explaining Tesseract concepts** and debugging issues ## Effective prompting strategies ### Include conventions and an example pattern LLMs produce the best results when you provide both conventions and a minimal example in the prompt. This works regardless of whether the LLM has access to the Tesseract Core codebase — by providing conventions inline, you don't need to rely on the model knowing or being able to read Tesseract Core source code. ```` Tesseract conventions: - InputSchema and OutputSchema are Pydantic BaseModel classes - Use `from tesseract_core.runtime import Array, Float32` - Array syntax: `Array[(None,), Float32]` for 1D dynamic arrays, `Float32` for scalars - The `apply` function takes InputSchema and returns OutputSchema Example pattern: ```python from pydantic import BaseModel, Field from tesseract_core.runtime import Array, Float32 class InputSchema(BaseModel): x: Array[(None,), Float32] = Field(description="...") class OutputSchema(BaseModel): y: Float32 = Field(description="...") def apply(inputs: InputSchema) -> OutputSchema: ... ``` ```` ### Be specific about what you want Describe your function clearly and explicitly ask for minimal output: ``` I have a function that performs CFD mesh generation: - Input: A CAD geometry file path (string) and mesh resolution (float, 0.01 to 1.0) - Output: Node coordinates (Nx3 float array) and element connectivity (Mx4 int array) Generate just the InputSchema, OutputSchema, and apply function. ``` The phrase "generate just" helps prevent over-engineering (like adding autodiff endpoints you didn't ask for). ### Ask for incremental improvements Start simple and iterate: 1. First, ask for basic schemas and apply function 2. Then, add field descriptions and validation 3. Finally, add autodiff endpoints if needed ## Example: Generating a basic Tesseract This example shows a minimal Tesseract without autodiff endpoints. The output below was generated by Claude 3.5 Haiku. **Prompt:** ```` Create a Tesseract that wraps a function for computing the stress in a beam. Tesseract conventions: - InputSchema and OutputSchema are Pydantic BaseModel classes - Use `from tesseract_core.runtime import Float64` - `Float64` for scalar floats - The `apply` function takes InputSchema and returns OutputSchema Example pattern: ```python from pydantic import BaseModel, Field from tesseract_core.runtime import Float64 class InputSchema(BaseModel): x: Float64 = Field(description="...") class OutputSchema(BaseModel): y: Float64 = Field(description="...") def apply(inputs: InputSchema) -> OutputSchema: ... ``` Inputs: - length: beam length in meters (positive float) - width: beam width in meters (positive float) - height: beam height in meters (positive float) - force: applied force in Newtons (can be negative) - material_E: Young's modulus in Pascals (positive float) Output: - max_stress: maximum stress in Pascals (float) - deflection: maximum deflection in meters (float) Generate just the InputSchema, OutputSchema, and apply function. Use Pydantic Field() with descriptions. ```` **Actual code the LLM produced (Claude 3.5 Haiku):** ```python from pydantic import BaseModel, Field from tesseract_core.runtime import Float64 class InputSchema(BaseModel): length: Float64 = Field(description="Beam length in meters (positive float)") width: Float64 = Field(description="Beam width in meters (positive float)") height: Float64 = Field(description="Beam height in meters (positive float)") force: Float64 = Field(description="Applied force in Newtons (can be negative)") material_E: Float64 = Field(description="Young's modulus in Pascals (positive float)") class OutputSchema(BaseModel): max_stress: Float64 = Field(description="Maximum stress in Pascals (float)") deflection: Float64 = Field(description="Maximum deflection in meters (float)") def apply(inputs: InputSchema) -> OutputSchema: """Compute the maximum stress and deflection in a beam under load.""" # Second moment of inertia for rectangular cross-section: I = (width * height^3) / 12 I = (inputs.width * (inputs.height ** 3)) / 12.0 # Distance from neutral axis to outer fiber c = inputs.height / 2.0 # Maximum bending moment for simply supported beam with center load max_moment = (inputs.force * inputs.length) / 4.0 max_stress = (max_moment * c) / I # Maximum deflection: delta = (F * L^3) / (48 * E * I) max_deflection = (inputs.force * (inputs.length ** 3)) / (48.0 * inputs.material_E * I) return OutputSchema( max_stress=abs(max_stress), deflection=abs(max_deflection) ) ``` ```{note} This basic example does not implement autodiff endpoints. To make a Tesseract fully differentiable, you need to: 1. Wrap types with `Differentiable[...]` 2. Implement `jacobian`, `jacobian_vector_product`, and/or `vector_jacobian_product` functions See the [differentiable programming guide](../introduction/differentiable-programming.md) and [Tesseract-JAX](https://github.com/pasteurlabs/tesseract-jax) for how to add autodiff support. ``` ## Example: Converting existing code When converting existing code, be explicit about array shapes and which parameters should be differentiable. **Prompt:** ``` Convert this existing Python function into a Tesseract: def simulate_heat_transfer(T_initial, T_boundary, diffusivity, dx, dt, n_steps): """ 1D heat equation solver using finite differences. Args: T_initial: Initial temperature distribution (1D array) T_boundary: Boundary temperature (scalar) diffusivity: Thermal diffusivity (scalar) dx: Grid spacing (scalar) dt: Time step (scalar) n_steps: Number of time steps (integer) Returns: Final temperature distribution (1D array, same shape as T_initial) """ T = T_initial.copy() r = diffusivity * dt / dx**2 for _ in range(n_steps): T_new = T.copy() T_new[1:-1] = T[1:-1] + r * (T[2:] - 2*T[1:-1] + T[:-2]) T_new[0] = T_boundary T_new[-1] = T_boundary T = T_new return T Generate just the InputSchema, OutputSchema, and apply function. n_steps is an integer (not differentiable). Use Array[(None,), Float32] for variable-length arrays. ``` **Expected LLM output:** ```python from pydantic import BaseModel, Field from tesseract_core.runtime import Array, Float32 class InputSchema(BaseModel): T_initial: Array[(None,), Float32] = Field( description="Initial temperature distribution (1D array)" ) T_boundary: Float32 = Field(description="Boundary temperature (scalar)") diffusivity: Float32 = Field(description="Thermal diffusivity (scalar)") dx: Float32 = Field(description="Grid spacing (scalar)") dt: Float32 = Field(description="Time step (scalar)") n_steps: int = Field(description="Number of time steps (integer)") class OutputSchema(BaseModel): T_final: Array[(None,), Float32] = Field( description="Final temperature distribution (1D array)" ) def apply(inputs: InputSchema) -> OutputSchema: """1D heat equation solver using finite differences.""" import numpy as np T = np.array(inputs.T_initial, dtype=np.float32) r = inputs.diffusivity * inputs.dt / inputs.dx**2 for _ in range(inputs.n_steps): T_new = T.copy() T_new[1:-1] = T[1:-1] + r * (T[2:] - 2 * T[1:-1] + T[:-2]) T_new[0] = inputs.T_boundary T_new[-1] = inputs.T_boundary T = T_new return OutputSchema(T_final=T) ``` ## Tips for better results ### Validate the output Always review and test LLM-generated code: 1. Check that schemas match your actual data types 2. Verify that array shapes are correct (use `None` for dynamic dimensions) 3. Test with real inputs before building the Tesseract 4. Run `tesseract build` to catch any issues early ### Iterate on the design Use the LLM as a pair programming partner: ``` The schema you generated works, but I'd like to: 1. Add validation that dx and dt satisfy the CFL condition 2. Make the boundary conditions more flexible (fixed, periodic, or insulating) 3. Add an optional output for the full time history, not just final state Update the Tesseract accordingly. ``` ## Using LLMs with autodiff frameworks If you're using JAX and want automatic differentiation, mention this explicitly: ``` I'm using JAX for automatic differentiation. Generate a Tesseract that: 1. Uses Tesseract-JAX utilities if applicable 2. Implements jacobian using jax.jacobian 3. Implements jvp using jax.jvp 4. Implements vjp using jax.vjp Here's my JAX function: ... ``` ```{seealso} [Tesseract-JAX](https://github.com/pasteurlabs/tesseract-jax) allows you to embed Tesseracts as JAX primitives into end-to-end differentiable programs, and provides helpers for automatically generating autodiff endpoints from JAX functions. ```