Skip to main content

A declarative language for LLM-driven state machines (reference interpreter).

Project description

mklang

CI Docs License

A declarative language for LLM-driven state machines. A .mk file (mk = machine) describes an agent as a set of states; an LLM is the runtime that executes generative steps. The document is the program; the host supplies the interpreter, optional tools, and optional code-hook gates.

mklang : LangGraph  ::  a declarative spec : Python code

The idea

Each state has four faces:

Face Answers Example Ref. interpreter
structure what shape? "The output is an email reply, max 150 words" system
execution how to act? "Do not invent policies not in the KB facts" system
prompt what to think? "Write a reply to {{ticket.body}}…" user (+ {{…}})
gates when to exit? see below separate judge call

Sticky policy goes in execution; turn data and {{context}} go in prompt. There is no system: keyword — see Best practices §3.

Real side effects (search, send, calc) are tool: states — host callables, not prose in execution. See examples/react.mk and examples/triage.mk.

The output of a state is stored in the shared context under its output: key, so later states read it via {{key}}. Four optional faces unlock richer reasoning: reason (traced chain-of-thought), accumulate (append to a list), fan-out (sample: N / over: {{list}}), and call (run another machine) — see Reasoning architectures.

Gates are the transitions. A state's gates list is its transition table: each gate is a natural-language condition the LLM judges, plus what happens next.

gates:
  - when: the reply resolves the request and is in the required tone
    then: ok
    to: send
  - when: information from the KB is missing
    repair: 2 # re-run this state with feedback, up to 2 times
    to: gather
  - when: the request needs a human
    escalate: true
    to: human_review

Policies: ok (advance), repair(N) (self-correct with feedback), escalate (route to a handler), fail (abort). A global step budget prevents runaway loops.

Design commitments

  • Document-first — readable without the interpreter; prose-first for the common path. Production machines still need developer judgment for tools, hooks, and untrusted inputs (see SPEC threat model).
  • LLM-as-runtime — non-deterministic by design; gates (prose + optional code hooks + budgets + trace) are the reliability mechanism. Prose-gate accuracy is an empirical claim, not a free lunch.
  • Prose, not typesstructure and gate conditions are natural language, judged by the LLM at runtime; optional hook: gates add host bool checks.
  • Provider-agnostic — a .mk never names a provider or model. States route by capability tier (fast / balanced / reasoning); the runtime maps each tier to a concrete model. Portability of the document is syntactic; whether different providers fire the same gates on the same run is measurable (see scripts/gate_divergence.py).
  • Spec + conformance — an implementation-neutral conformance suite pins interpreter semantics so a second runtime can match the language contract.
  • Language-agnostic runtime — the spec assumes only "some host with an LLM".

Files

Runtime configuration

The .mk picks a tier; a host-side config picks the model. This is the whole of "make it multi-provider":

active: deepseek # deepseek | anthropic | openai | google | openrouter | xai | mistral | local
providers:
  deepseek:
    base_url: https://api.deepseek.com
    tiers:
      {
        fast: deepseek-chat,
        balanced: deepseek-chat,
        reasoning: deepseek-reasoner,
      }
  anthropic:
    tiers:
      {
        fast: claude-haiku-4-5,
        balanced: claude-sonnet-5,
        reasoning: claude-opus-4-8,
      }
  local:
    base_url: http://localhost:11434/v1
    tiers: { fast: qwen3:8b, balanced: qwen3:32b, reasoning: deepseek-r1:70b }

The example config defaults to DeepSeek (the path we live-test against). Flip active: anthropic (or openai / local / …) and every example runs unchanged. Blocks ship for Anthropic, OpenAI, Google, DeepSeek, OpenRouter, xAI (Grok), Mistral, and local (Ollama/vLLM) — every non-Anthropic one is OpenAI-compatible, so a single adapter serves them all. OpenRouter is a meta-provider: its vendor/model ids let each tier target a different vendor through one endpoint. Per-tier params (Anthropic adaptive-thinking + effort, OpenAI/xAI reasoning_effort, …) live under params. Full map: config/runtime.example.yaml.

Reasoning architectures

Every modern reasoning/agentic pattern maps onto the core (states + gates + prose + tiers + the optional faces). Full skeletons in SPEC.md §10; operating guidance in docs/patterns.md.

Eight of these ship as ready, general-purpose std_* machines — parameterized by context, callable from your machines (call: std_refine), runnable by name:

mklang run std_self_consistency --set task="Estimate the risk of X"

See the stdlib catalog (ADR 0012). The patterns that need host tools/hooks or static call: targets (ReAct, router, exact policy) stay as authored examples.

Architecture mklang constructs
Chain-of-Thought reason: true
ReAct think → tool state (host callable) → observation accumulated
Reflexion / self-refine produce → self-judge gate → repair
Self-consistency sample: N → reducer state (majority)
Tree-of-Thought sample: k → score/select reducer → loop (depth via budget)
Plan-and-Execute planner parse: list (0.3) → over: {{steps}} → reducer
Debate / ensemble over: {{personas}} → synthesizer
Map-Reduce over: {{chunks}} → reducer
Router-of-experts classify → call specialists
Speculative cascade tier: fast draft → escalatetier: reasoning
Exact policy checks gate hook: host (ctx, output) -> bool (no LLM)

Install

pip install mklang

Editor validation for .mk files works out of the box via the JSON Schema — point yaml-language-server at https://raw.githubusercontent.com/gianlucamazza/mklang/main/schema/mklang.schema.json.

Quickstart (reference interpreter)

cp .env.example .env            # set DEEPSEEK_API_KEY=… (or another provider key)
uv run mklang check examples/self_consistency.mk
uv run mklang lint examples/self_consistency.mk   # + static analysis
uv run mklang run examples/self_consistency.mk \
  --set question.text="What is the capital of Australia?"
# default provider is deepseek; override with --provider anthropic|openai|…

# pause on budget, resume later (exit code 3 = suspended):
uv run mklang run examples/self_consistency.mk --max-tokens 300 --checkpoint ck.json
uv run mklang resume ck.json --max-tokens 5000

# human-in-the-loop: escalate gates suspend; resume with the human decision:
uv run mklang run examples/expense_approval.mk --checkpoint ck.json --hitl
uv run mklang resume ck.json --set human.reply="approved, cost center 42"

The .mk picks tiers; config/runtime.example.yaml maps them to models (active: deepseek by default); the key comes from .env. Same machine, any provider.

Test your machine without API keys

mklang test runs your machine against a script of named scenarios with a scripted LLM (produce texts, judge picks) and scripted tools/hooks — fully deterministic, no provider or key. It pins the paths you care about before you spend a token on a live run.

uv run mklang test examples/triage.mk --script examples/triage.test.yaml
# PASS happy-path
# PASS kb-empty-escalates

Each scenario declares a scripted llm:/tools:/hooks: and an expect: (status, error, result, at, trace skeleton, context keys) — the same case format the conformance suite uses. A mismatch prints a minimal diff (the first differing key, expected vs actual) and exits 1. See examples/triage.test.yaml.

MCP server (agentic hosts)

Agent hosts that speak MCP (Claude Code and other clients) can commission a machine instead of embedding the library (ADR 0011): the host requests a run and gets back the result with full provenance (trace + usage).

pip install 'mklang[mcp]'
claude mcp add mklang -- mklang-mcp --config /abs/path/to/runtime.yaml

The server exposes commissioning tools (run / resume), discovery (list_machines / describe_machine), and check (ADR 0011 + 0013). run accepts inline .mk source or a path, with inputs merged into the context; resume takes an opaque handle or checkpoint file (e.g. {"human.reply": "…"} for HITL). Live engine events stream as mklang.event logging notifications (ADR 0019). In-memory sessions hold suspensions unless you pass checkpoint_path. Provider keys resolve server-side from the environment, never over the wire.

Console (interactive)

mklang console (extra mklang[console]) is the agent-first front door: type what you want, the console's agent authors or picks a machine, commissions it, and streams the run state-by-state — escalations and tool consent come back to you inline. The agent itself is a machine (agent.mk) — read it, lint it, swap it with --agent your_brain.mk (ADR 0015). Agent replies render as Markdown; the brain declares host clocks today / now for wall-clock questions. Details: docs/console.md.

pip install 'mklang[console]'
mklang console

Status

Language v0.3 / package 0.8.1 — core complete: states + gates + prose, tiers, reason / accumulate / fan-out / call / tool / parse: list / code-hook gates; multi-provider interpreter with entry-point plugins (tools, hooks, providers, machines); resumable checkpoints + HITL; mklang check / lint (--llm optional) / test; conformance suite; machine stdlib (std_*); MCP host; console TUI (M1–M3); structured web search (offline stub by default); host tool stub architecture for search / search_kb / send_reply (ADR 0020); host clock conventions context.today / context.now; sectioned produce system prompts from structure+execution; output anti-cutoff + context budgets (ADR 0016–0019); best practices. Gate judging follows the state tier by default.

  • Live: DeepSeek (default) and OpenAI green (release matrix 0.7.0; re-run on 0.8.0 publish), including gate-divergence agreement 1.0 on the synthetic harness — see docs/experiments/gate-divergence.md. Anthropic unit-tested; live may be billing-blocked.
  • Release policy: DeepSeek + OpenAI smoke and three-run gate agreement are blocking; other configured providers are reported without blocking. PyPI publication uses GitHub OIDC Trusted Publishing from the release workflow.
  • Open / later: Anthropic live when the account has credit; on_truncate=continue stitching; std_research / language-level context zones (ROADMAP).
  • Roadmap and full release notes: ROADMAP.md, CHANGELOG.md.

License

Apache-2.0. Contributions welcome — see CONTRIBUTING.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mklang-0.8.2.tar.gz (295.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mklang-0.8.2-py3-none-any.whl (114.9 kB view details)

Uploaded Python 3

File details

Details for the file mklang-0.8.2.tar.gz.

File metadata

  • Download URL: mklang-0.8.2.tar.gz
  • Upload date:
  • Size: 295.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mklang-0.8.2.tar.gz
Algorithm Hash digest
SHA256 5243c88047cef8a2de035db34295f34eaab5a9865fb47ac791995c4885d0bdbe
MD5 8d69f2337100f947d23b58f85a2859b7
BLAKE2b-256 d3735167a2c7f19013ccf7aeb90807d0ad9a2ce02965b15421df3aacdc28e14f

See more details on using hashes here.

Provenance

The following attestation bundles were made for mklang-0.8.2.tar.gz:

Publisher: release.yml on gianlucamazza/mklang

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mklang-0.8.2-py3-none-any.whl.

File metadata

  • Download URL: mklang-0.8.2-py3-none-any.whl
  • Upload date:
  • Size: 114.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mklang-0.8.2-py3-none-any.whl
Algorithm Hash digest
SHA256 35b5796626b8a28760730bb8fef54e2b861cdfeff99d57bae63a3b6fa2c8c35c
MD5 862c33216e572a71eae5128f30d9cd44
BLAKE2b-256 dfd746ba2c2390b20f3896a201c831fba93615850856e1c4ebdab02bbd5b3b26

See more details on using hashes here.

Provenance

The following attestation bundles were made for mklang-0.8.2-py3-none-any.whl:

Publisher: release.yml on gianlucamazza/mklang

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page