LSG-2: .lm File Style Guide¶
This guide defines conventions for writing .lm (Lamia script) files.
.lm files are the orchestration layer -- they combine Python code
with Lamia syntax to wire prompts, files, web actions, and validation
into workflows. Violations are enforced by lamia inspect -- see the full rule reference.
Core principle¶
.lm files are Python + Lamia. Follow PEP 8 for the Python parts.
but keep usage of Python code to a minimum only when Lamia syntax is not sufficient for the task.
Indentation and formatting¶
Use 4 spaces for indentation (PEP 8). Never tabs.
def analyze_code(language="python"):
"""
Review the code in {@source.py} for bugs and style issues.
Focus on {language}-specific patterns.
"""
for ticker in ["AAPL", "NVDA", "GOOG"]:
data = get_stock(ticker=ticker) -> JSON[StockQuote]
print(f"{ticker}: {data.price}")
Return types¶
Always declare return types when you need validated output.
Lamia supports: JSON, HTML, YAML, XML, CSV, Markdown, TEXT.
Format validation (type only)¶
def load_config() -> JSON:
"./config.json"
def generate_page() -> HTML[LandingPage]:
"Create a login form"
Strict vs permissive¶
Skip permissive Boolean flag and use HTML[Model] instead.
-> HTML[Model] # permissive (default) - filler content arround the content you want to extract is allowed and model matched rules are relaxed a bit. Check Lamia documentation for more details.
-> HTML[Model, True] # strict - response must be pure HTML, etc. and no drifts are allowed in the document conten
-> HTML[Model, False] # Use HTML[Model] instead of this
Use cases: - Use strict mode for content generation (you control the output). - Use permissive mode for parsing external content (HTML pages, API responses).
Don't embed format instructions¶
Never put output schema in comments, docstrings, or prompt text:
# Bad -- the schema belongs in a Pydantic model
def analyze():
"""
Analyze the data.
Output JSON:
{"score": 0, "summary": "..."}
"""
# Good -- Pydantic model + return type
class Analysis(BaseModel):
score: int
summary: str
def analyze() -> JSON[Analysis]:
"Analyze the data"
Calling .hu functions¶
.hu files are auto-discovered as callable functions — the filename (without .hu) becomes the function name. Call them directly; do not wrap them in a def:
# Good — call the .hu file directly
result = summarize(aspect="key findings", max_words=200) -> HTML
# Bad — wrapping a .hu file in a def (flagged by LMW022)
def summarize_text(aspect, max_words) -> HTML:
"summarize.hu"
# Bad — positional arguments (flagged by LMW006)
result = summarize("key findings", 200) -> HTML
All arguments must be keyword arguments.
Return type on the call site¶
The -> Type on .hu calls is where you specify what format
you expect back. The .hu file itself stays format-agnostic:
# Same .hu prompt, different output formats
html_result = summarize(topic="AI") -> HTML
json_result = summarize(topic="AI") -> JSON[Summary]
text_result = summarize(topic="AI") -> TEXT
Simplified syntax¶
For one-off commands that don't need a function name, use the simplified arrow syntax:
File operations¶
Reading Files¶
Read files with Lamia syntax. Avoid using Python code to read files. Validation will be done automatically if needed.
def read_data():
"./data/input.txt"
def load_settings() -> JSON:
"../config/settings.json"
content = read_data() -> TEXT # We can select the return type during the runtime.
settings = load_settings() # load_settings will always return a JSON object.
Writing Files¶
Write files with Lamia syntax. Avoid using Python code to write files. Validation will be done automatically if needed.
Assign to a variable when you need the result. Use -> File(...)
when you want to write directly to disk: do
"Create a landing page" -> File(HTML, "index.html"):
"Generate test data" -> File(CSV, "fixtures.csv"):
If the result is needed in memory and written to a file, use a single operation:
File context¶
Instead of using many paths to a directory in your code, use with files("~/Documents/") to scope file search directories and to avoid writing many paths to a directory in your code:
with files("~/Documents/"):
def answer_question(question):
"""
Answer: {question}
Use information from {@resume.pdf} and {@cover_letter.txt}
"""
Variable naming¶
Follow PEP 8:
# Good
review_result = review_code(language="python") -> JSON[Review]
stock_data = get_stock(ticker="AAPL") -> CSV[StockQuote]
# Bad
ReviewResult = review_code(language="python") -> JSON[Review]
x = get_stock(ticker="AAPL") -> CSV[StockQuote]
Pydantic models use PascalCase (standard Python class naming):
Model selection¶
Use a singular parameter for models when a function needs a specific LLM:
Use an array of models for models when providing fallback models:
Imports¶
Lamia auto-injects the following into .lm execution scope — do not import them explicitly:
- Lamia: All .hu files as functions in the project and all functions in the .lm files
- Lamia All Lamia types
- Pydantic: All Pydantic types it uses
- Typing:
List,Dict,Optional,Any
Only import Python standard library modules (import json, import re, etc.) and third-party packages. The linter flags redundant imports with LMW021.
Pydantic models defined in .py files within the project are also auto-discovered — no import needed.