Project Structure¶
Understanding the layout of a tuvl project.
Directory Layout¶
A typical tuvl project has the following structure:
my-project/
├── pyproject.toml # Project dependencies (uv-managed)
├── .env # Secrets and local config (git-ignored)
├── .env.example # Safe-to-commit template
├── .gitignore
├── models/ # ModelDefinition YAMLs
│ ├── contact.yaml
│ └── order.yaml
├── workflows/ # Workflow YAMLs
│ ├── contact_intake.yaml
│ └── order_processing.yaml
├── datasources/ # DataSource connection YAMLs
│ └── postgres.yaml
├── llms/ # AgentModel / LLM preset YAMLs
│ └── default.yaml
├── nodes/ # Python node implementations
│ ├── contact_nodes.py
│ └── order_nodes.py
├── .tuvl/ # tuvl project config (optional)
│ └── telemetry.yaml # OTel export settings
└── tests/
└── workflows/ # LLM-as-a-Judge test cases (*.yaml)
Generated by tuvl init --sample
Running tuvl init my-project --sample writes the full structure above, including a ready-to-run workflow, nodes, two test cases, and a telemetry config.
Core Directories¶
models/¶
YAML ModelDefinition files. Each one generates a SQLModel class, Pydantic schemas, and CRUD REST endpoints at /api/{model_name}.
kind: "ModelDefinition"
version: "v1"
metadata:
name: "Contact"
spec:
tablename: "contacts"
fields:
- name: "id"
type: "uuid"
primary_key: true
default: "uuid4"
input: false
- name: "email"
type: "string"
unique: true
required: true
- name: "name"
type: "string"
required: true
workflows/¶
YAML Workflow files. Each file's trigger section defines the HTTP endpoint tuvl mounts automatically.
kind: "Workflow"
version: "v1"
metadata:
name: "contact_intake"
spec:
context: "Contact"
trigger:
path: "/api/contacts"
method: "POST"
steps:
- id: "save"
kind: "functional"
runner: "save_contact"
routes:
default: "prioritize"
- id: "prioritize"
kind: "agent"
agent:
model: "default"
prompt: "Classify {{ name }} as high | medium | low priority. Return JSON: {\"priority\": \"...\"}"
output:
format: json
map:
priority: priority
datasources/¶
Database connection configurations. Supports multiple named datasources — reference them from model definitions with datasource: my_ds_name.
kind: "DataSource"
version: "v1"
metadata:
name: "main"
spec:
type: "postgresql"
driver: "asyncpg"
connection:
host: "${POSTGRES_HOST}"
port: ${POSTGRES_PORT:5432}
database: "${POSTGRES_DB}"
username: "${POSTGRES_USER}"
password: "${POSTGRES_PASSWORD}"
llms/¶
AgentModel YAML files — reusable LLM provider presets. Reference them from workflow agent steps by name (e.g. model: "default").
kind: "AgentModel"
version: "v1"
metadata:
name: "default"
spec:
provider: "ollama"
model: "llama3"
api_base: "http://localhost:11434"
temperature: 0.7
max_tokens: 1024
nodes/¶
Python files containing @node()-decorated async functions. tuvl auto-discovers and imports all .py files in this directory at startup — no __init__.py needed.
from tuvl.core.nodes.base import node
@node("save_contact")
async def save_contact(ctx: dict) -> dict:
repo = ctx["_db"]["Contact"]
record = await repo.add({"email": ctx["email"], "name": ctx["name"]})
ctx["id"] = str(record.id)
return ctx
.tuvl/¶
Optional project-level tuvl config. Currently supports:
telemetry.yaml— OTel exporter settings (endpoint, service name, headers)
tests/workflows/¶
YAML test cases for tuvl test. Each file specifies stubs for external calls, an expected execution trace, and optional LLM-as-a-Judge evaluation assertions. See Testing Workflows.
Startup Sequence¶
When tuvl dev or tuvl run starts, the engine:
- Reads
.envand environment variables - Connects to all configured datasources
- Loads
models/*.yaml→ generates SQLModel classes + CRUD routes - Loads
nodes/*.py→ registers@node()functions inNODE_REGISTRY - Loads
workflows/*.yaml→ mounts HTTP trigger routes - Loads
llms/*.yaml→ registersAgentModelpresets - Initialises OTel tracer if
telemetry.yamlis present and enabled - Starts the uvicorn server
Multi-Project Workspaces¶
For larger applications, run multiple tuvl projects side by side:
workspace/
├── services/
│ ├── crm/
│ │ ├── models/
│ │ ├── workflows/
│ │ └── nodes/
│ └── billing/
│ ├── models/
│ ├── workflows/
│ └── nodes/
└── docker-compose.yaml
# Run a specific service
tuvl dev --project-dir services/crm
tuvl dev --project-dir services/billing --port 8001
Next Steps¶
- Architecture — How components interact at runtime
- Models — Full model definition reference
- Workflows — Workflow and step configuration