Browse documentation Introduction
Start hereIntroduction3 min read

Fast iteration. Explicit guards. Readable scripts.

Build AI workflows in minutes, not hours

Turn Markdown into runnable AI workflows — natural-language steps alongside deterministic code, validation, and human approval.

Node.js 20+GitHub CopilotVisible boundaries
On this page
hi.mdmarkdown
---
extension: 'semantic-prompt-language'
version: 1
model: 'gpt-6-luna'
preamble: 'Be concise.'
---

# Hello, SPL

Prose like this is commentary. Only `spl` fences and blockquotes run, top to bottom, in one shared scope.

```spl
let topics = ['recursion', 'closures', 'regular expressions']

hydratable class Card:
    term: str
    definition: str

let cards = object()
for topic in topics:
    cards[topic] = ask(schema=Card, repairs=1) 'A one-sentence flashcard about {topic}'
```

> Pick the key of @cards whose card a beginner would find hardest. Save that exact key to @hardest

```spl
assert hardest in topics "unexpected topic '{hardest}'"
restrict './out':
    write_file('./out/cards.json', json_stringify(object(cards), 2))
success object:
    'cards': len(cards)
    'hardest': hardest
```
  • built-in ask generates LLM response with schema validation and one repair
  • natural language blockquote uses agent mode to follow instructions and uses tools to save variable, the variable is saved to global scope
  • schema=Card decodes each answer and hydrates contructor-free instance of class Card, see Gradual typing
  • assert breaks flow in case of mimatch
  • restrict limits scope's file access to particular location
  • success returns json

Opt-in Types#

Language is dynamic but has full support for opt-in static type checker. There are supported primitive types, structs, classes and dynamic catch-all objects. Functions are first class citizens.

markdownmarkdown

---
extension: 'semantic-prompt-language'
version: 1
model: 'gpt-6-luna'
---

# Types 

This sample shows how to use opt-in types.

```spl
type FloatOrInt = float | int [description = 'Either float or int']

let numbers: list[FloatOrInt] = []

for i in range(4):
    let num = ask(tokens=128, schema=FloatOrInt) 'Sample number'
    numbers.append(num)

for n in numbers:
    if isinstance(n, int):
        log('Integer: {n}')
    else:
        log('Float: {n}')
```

```spl
class Person:
    firstName: str 
    lastName: str 
    age: int 

    def toString():
        return '{self.firstName} {self.lastName} at age {self.age}'

let result = ask(schema=struct(Person)) 'Sample person JSON with firstName, lastName and int age fields'

let person: Person = validate(result, Person)

success person.toString()
```

Why SPL?#

  1. Short loops. One file, run directly. --check validates syntax without a model, and trace/replay reruns your code and guards against recorded model answers.
  2. Exact code where it matters. Control flow, arithmetic and I/O never call a model. ask and think generate values without tools; agent expressions and blockquotes can act.
  3. Guards you can see. fmt, schema and assert check data; restrict checks actions. Prompt guidance (enforce, forbid, len) is kept separate from validation.
  4. Readable. Inputs, prompts, checks and outputs sit in one document, top to bottom.
  5. Optional typed contracts. Enums, structs, classes and reusable aliases describe model output. Annotations, typed functions and persistent mutable contracts enforce consistency; assertions still check whether the content makes sense.

How a run works#

  1. Frontmatter opts the file in. All fences are parsed first, so syntax errors fail before anything runs.
  2. Fences and blockquotes execute in document order in one shared scope.
  3. A blockquote sends its text, visible bindings and runtime tools to an agent. The model calls tools to invoke functions, read environment values, request human input and load/save shared variables. It never submits generated SPL.
  4. ask, think and agent use the official OpenAI, Anthropic and Google clients, one fresh conversation per call.
  5. success prints the result on stdout. Logs go to stderr.

Non-goals#

  • Full Python, inheritance, recursive schemas or general-purpose automatic source conversion.
  • Reproducible model output, transactional actions or an OS sandbox.

Where next#

spl.Short loops. Clear boundaries.GitHub

Find your way