{
  "schema": "leumas.docs.page/1",
  "id": "skill:leumas-lmx#lmai",
  "slug": "skills/leumas-lmx/lmai",
  "kind": "tools",
  "bucket": "skill",
  "title": "lmai — the prompt-orchestration language",
  "name": "lmai",
  "eyebrow": "the prompt-orchestration language",
  "chip": null,
  "summary": "Drive a language model from a script: declare prompts, force a JSON shape, branch on the answer, and account for what it cost in tokens. Safe mode with no new env flag, and it needs the host's ai...",
  "keywords": [
    "leumas-lmx",
    "force",
    "prompts",
    "branch",
    "language",
    "drive",
    "script",
    "declare"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# `lmai` — the prompt-orchestration language\n\nDrive a language model from a script: declare prompts, force a JSON shape, branch on the answer, and\naccount for what it cost in tokens. **Safe** mode with no new env flag, and it needs the host's `ai`\nbridge — so `POST /api/lmx/run` or Studio's LMX Playground, not the CLI.\n\n```lmai\nMODEL auto\nTEMPERATURE 0.2\nSYSTEM \"You are a terse support classifier.\"\n\nPROMPT classify = \"Label this as billing, bug or other:\\n{{input}}\"\nSHAPE verdict = { \"label\": \"billing\", \"confidence\": 0.9 }\n\nASK classify -> r AS verdict\nPRINT r.label\n\nWHEN r.confidence < 0.7 THEN ASK \"Why is this ambiguous?\\n{{input}}\" -> why\nPRINT why\n\nUSAGE\nRETURN r\n```\n\n---\n\n## [critical] Read this before you trust a run\n\n`providerRegistry.resolve()` **never fails**. `auto` walks `AUTO_PRIORITY` — ollama, leumas-slm,\nopenai, claude, gemini, cursor-agent, codex — and when none is available it falls back to a built-in\n**echo stub** that hands your own message back.\n\nSo on a machine with no API key and no Ollama, a naive prompt script runs green, prints something\nplausible, and means nothing at all.\n\n`lmai` closes that hole three ways:\n\n- every result records **which provider actually answered**;\n- `USAGE` prints it;\n- and because a script author cannot be relied on to write `USAGE`, **the runner prints a warning of\n  its own accord** whenever the stub answered:\n\n  ```\n [warning] the ECHO STUB answered — no real model provider is configured, so this run proves nothing.\n  ```\n\nIf you are automating on top of `lmai`, branch on `usage.stub` before anything else.\n\n**The shaped path makes this worse, not better.** The stub echoes your prompt back — and a shaped\nprompt *contains the `SHAPE` example*, so the JSON extractor finds it and the run succeeds:\n\n```\nresult.value: {\"label\":\"billing\",\"confidence\":0.9}\nresult.usage: {\"calls\":1,\"tokens\":0,\"providers\":[\"stub\"],\"stub\":true,\"retries\":0}\n```\n\nThat is a perfectly plausible classification of the input. It is your own example, handed back.\n`stub: true` and `tokens: 0` are the only things that say so.\n\n---\n\n## Keys\n\n`lmai` invents no policy. It calls `resolveKeyForUser`, already the single decision point behind every\nchatbot and coding-agent run:\n\n| Caller | Key used |\n|---|---|\n| active member, or admin | the env-mounted key for that provider |\n| free user | their own stored BYO key |\n| neither | a `402` — *\"Add your own key in Settings, or upgrade your membership\"* |\n\nThat message is surfaced to the script verbatim, because it is already written for a human.\n\n**Local providers skip this entirely.** `ollama`, `leumas-slm` and `codex` are configured with an\nendpoint or a binary path, not a credential, so `lmai` never demands a key for a model running on your\nown machine.\n\n---\n\n## Directives\n\nConfig and declarations are hoisted, so a `PROMPT` may be declared after the `ASK` that uses it.\nStatements run in source order. `#` starts a comment.\n\n### Config\n\n| Directive | Notes |\n|---|---|\n| `MODEL <provider>[:<model>]` | `MODEL auto`, `MODEL claude`, `MODEL claude:claude-sonnet-5` |\n| `TEMPERATURE <n>` | **`0` is a setting, not \"unset\"** — an omitted temperature is never defaulted |\n| `MAXTOKENS <n>` | same |\n| `SYSTEM \"…\"` or `SYSTEM do … end` | the system prompt |\n\n### Declarations\n\n| Directive | Notes |\n|---|---|\n| `PROMPT <name> = \"…\"` | a template; `\\n` escapes work |\n| `PROMPT <name> do … end` | multi-line, taken verbatim |\n| `SHAPE <name> = { … }` | an **example** JSON document — a shape, not a schema |\n| `SHAPE <name> do … end` | multi-line JSON |\n| `SET <name> = <value>` | a variable; JSON-coerced, so `SET n = 3` is the number 3 |\n\n### Statements\n\n| Directive | Notes |\n|---|---|\n| `ASK <name\\|\"literal\"> -> <var>` | one model turn; the reply text lands in `<var>` |\n| `ASK … -> <var> AS <shape>` | forces JSON; `<var>` is the **parsed document** |\n| `WHEN <cond> THEN <statement>` | one guarded statement |\n| `PRINT <path>` or `PRINT \"… {{slot}} …\"` | |\n| `USAGE` | the accounting line |\n| `RETURN <path>` | the script's value; **stops the script** |\n\n`WHEN` conditions are `path op value` (`=` `!=` `>` `>=` `<` `<=`) or a bare path for truthiness.\nA missing path is falsy, never an error.\n\n---\n\n## `{{slots}}`\n\nTemplates interpolate from three sources, all in one namespace:\n\n1. the host's named `inputs` (`{\"input\": \"…\"}` from the API, or `# inputs: input=…` in a bundled demo);\n2. `SET` variables;\n3. any prior `ASK` result — including into a document: `{{r.label}}`.\n\n**An unresolved slot is an error, not an empty string.** A prompt that silently loses its input still\nruns, still costs tokens, and answers a question nobody asked — the single most expensive way for a\nscripted model call to fail.\n\nNon-string values are JSON-stringified rather than coerced, so `{{r}}` on an object gives `{\"a\":1}`\nand never `[object Object]`.\n\n---\n\n## The shaped path (`AS`)\n\n`SHAPE` gives the model an *example* of the document you want. `lmai` appends it plus\n`Respond with ONLY the JSON document — no commentary, no code fences.`, then parses the reply with the\nsame fence-stripping, balanced-bracket extractor `@leumas/generation` uses.\n\nIf that fails it retries **exactly once** with a stricter nudge, then gives up with the first 200\ncharacters of the reply. It never loops.\n\nBoth calls count toward tokens and toward the ASK budget. `usage.retries` tells you it happened.\n\n---\n\n## `USAGE`\n\n```\n--- usage --- calls=1 tokens=12 retries=0 provider=claude (token counts only — the provider layer reports no cost)\n```\n\n**Tokens, never money.** `cost` is hardcoded `0` throughout the provider layer and there is no price\ntable anywhere in the tree. `USAGE` says so in as many words rather than implying a figure it cannot\ncompute.\n\n---\n\n## Limits\n\n| Limit | Value | Why |\n|---|---|---|\n| ASK budget | 20 per script (retries count) | a loop cannot spend a key |\n| Run timeout | 60s default | the global LMX default is 5s, which kills any real model call |\n\n---\n\n## What it deliberately cannot do\n\n- **Loop, or define functions.** A prompt script that needs those wants `lmx` with the `adapter()`\n  bridge, not a second general-purpose language.\n- **Call tools.** `toolCalls` come back from the provider and are not surfaced; a tool loop is what\n  `@leumas/chatbots` and the coding agent are for.\n- **Report a cost.** See above.\n- **Run from the CLI.** No `ai` bridge there.\n\n---\n\n## Running it\n\n```bash\ncurl -s -X POST localhost:3000/api/lmx/run \\\n  -H 'content-type: application/json' \\\n  -d '{\"mode\":\"lmai\",\"source\":\"PROMPT p = \\\"hi {{input}}\\\"\\nASK p -> r\\nPRINT r\\nUSAGE\",\"inputs\":{\"input\":\"there\"}}'\n```\n\nBundled examples: `shared/engines/lmx/src/scripts/demo.lmai`, `shared/engines/lmx/src/scripts/triage.lmai`.\nTests: `shared/engines/lmx/src/test/lmai.test.js`. Guard: `pnpm smoke:lmx`.\n",
  "source": {
    "path": ".claude/skills/leumas-lmx/reference/lmai.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 7025,
    "hash": "3a9d90248608bc86d59d0b63cba2297365a21731"
  },
  "urls": {
    "html": "/p/skills/leumas-lmx/lmai",
    "json": "/docs/skills/leumas-lmx/lmai.json",
    "md": "/docs/skills/leumas-lmx/lmai.md"
  },
  "links": {
    "composes": [],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": []
  }
}
