CLI ==== The ``l2p`` CLI provides terminal-based access to PDDL generation, validation, planning, and LLM agent tooling. It supports both **interactive** workflows for human users and **stateless** command pipelines for automation and LLM agents. Configuration is stored at ``~/.l2p/config.yaml`` and managed via the :ref:`cli_init` and :ref:`cli_config` commands. .. tip:: Non-interactive commands (``l2p set``, ``l2p build``, ``l2p validate``, ``l2p plan``, ``l2p schema``) accept structured JSON input and produce machine-readable output - ideal for LLM tool-calling agents. See :ref:`cli_agentic` below. .. _cli_quickref: Quick Reference --------------- +----------------------------------+----------------------------------------------------+-----------------+ | Command | Description | Audience | +==================================+====================================================+=================+ | :ref:`cli_init` | Configure LLM provider & model | Human + Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_set` | Inject a PDDL component from JSON | Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_build` | Assemble & render full PDDL domain or problem | Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_validate` | Validate JSON components or ``.pddl`` files | Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_plan` | Run a planner on domain/problem strings | Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_schema` | Output Pydantic JSON Schema for LLM reference | Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_generate` | Interactive domain & problem generation | Human | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_models` | List, switch, and test configured models | Human + Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_config` | Show, edit, validate, or reset configuration | Human + Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_templates` | List, show, and find prompt templates | Human + Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_new` | Create blank PDDL domain or problem file | Human + Agent | +----------------------------------+----------------------------------------------------+-----------------+ | :ref:`cli_chat` | Interactive LLM chat with PDDL editing | Human | +----------------------------------+----------------------------------------------------+-----------------+ .. _cli_interactive: Interactive Commands (for humans) --------------------------------- These commands provide step-by-step prompts and are designed for direct terminal use. .. _cli_init: ``l2p init`` - Initial Setup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Walks through LLM backend selection, provider, model name, and API key configuration. The resulting config is saved to ``~/.l2p/config.yaml``. .. code-block:: bash # Interactive setup l2p init # Non-interactive (for scripts) l2p init --backend openai --provider openai --model gpt-4o-mini Supports two backends: * **unified** - uses the `simonw/llm `_ library * **openai** - uses the OpenAI SDK directly Providers include ``openai``, ``google``, ``anthropic``, ``deepseek``, ``mistral``, ``ollama``, and ``ollama-cloud``. .. _cli_generate: ``l2p generate`` - Interactive PDDL Generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Step-by-step pipelines for building PDDL domains and problems with LLM assistance. .. code-block:: bash # Interactive domain generation l2p generate domain # Interactive problem generation l2p generate problem # Increase LLM retries on parse failure l2p generate domain --max-retries 5 ``l2p generate domain`` guides you through: domain name, description, types, constants, predicates, functions, and actions - each generated by the LLM, entered manually, or reviewed before proceeding. ``l2p generate problem`` prompts for: domain source (NL description or existing ``.pddl`` file), problem name, description, objects, initial state, and goal state. .. tip:: At each generation step you can accept the LLM output, provide fix feedback for a regeneration, or enter the data manually. .. _cli_chat: ``l2p chat`` - Interactive LLM Session ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A REPL-style chat session with the configured LLM, featuring PDDL-specific commands: .. code-block:: bash l2p chat # Inside the session: # /exit - quit # /edit - load & edit a PDDL file # /validate - validate a PDDL file # - send to LLM with L2P context The ``/edit`` command loads a PDDL file, takes an edit description, sends it to the LLM with editing instructions, shows a diff, and prompts to overwrite. .. _cli_models: ``l2p models`` - Model Management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ List available models, switch between them, and test LLM connections. .. code-block:: bash # List configured models l2p models list l2p models list --details # show context length, cost, params l2p models list --provider openai # filter by provider # Switch model interactively l2p models switch # Test connection l2p models test l2p models test --simple # config validation only .. _cli_config: ``l2p config`` - Configuration Management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ View, edit, validate, and reset the L2P configuration. .. code-block:: bash # Show configuration l2p config show l2p config show --raw # raw YAML output # Edit in $EDITOR l2p config edit # Validate settings l2p config validate # Reset to defaults l2p config reset --force The ``edit`` subcommand auto-detects your editor from the ``$EDITOR`` environment variable and re-validates the config after saving. .. _cli_new: ``l2p new`` - Blank PDDL Files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create minimal PDDL domain or problem skeleton files. .. code-block:: bash # Create a domain file l2p new blocksworld.pddl # Create a problem file l2p new pb1.pddl --type problem --domain-name blocksworld .. _cli_templates: ``l2p templates`` - Prompt Template Management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ List, show, and find built-in prompt templates used by the generation commands. .. code-block:: bash # List all templates l2p templates list l2p templates list --category domain # filter by category l2p templates list --details # show source & path # Show template content l2p templates show --name types # Find template file path l2p templates find --name actions Three categories are available: ``domain``, ``task``, and ``feedback``. .. _cli_agentic: Agentic Workflow (for automation & LLM agents) ---------------------------------------------- These commands accept structured JSON input, perform a single operation, and produce machine-readable output. They are designed for non-interactive use by LLM tool-calling agents and shell scripts. .. tip:: Every agentic command is **stateless** - pass full JSON via ``--data`` or compose from individual flags. Pipe validated JSON between commands:: l2p set types --data '[...]' --json | l2p set predicates --stdin --json .. _cli_set: ``l2p set`` - Inject & Validate a Component ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Inject individual PDDL components from JSON. Each call validates the data against L2P's semantic rules and optionally outputs the formatted result. .. code-block:: bash # Inject types l2p set types --data '[{"name":"block","parent":"object"}]' # Inject predicates, output PDDL l2p set predicates --data '[ {"name":"clear","params":[{"variable":"?x","type":"block"}]} ]' --pddl # Inject from file l2p set actions --file actions.json --json # Pipe between commands l2p set types --data '[...]' --json | l2p set predicates --stdin # Show JSON Schema for LLM l2p set types --schema Available components: * Domain: ``requirements``, ``types``, ``constants``, ``predicates``, ``functions``, ``derived-predicates``, ``actions``, ``durative-actions``, ``events``, ``processes``, ``constraints`` * Problem: ``objects``, ``initial-state``, ``goal-state``, ``metric`` .. _cli_build: ``l2p build`` - Assemble & Render PDDL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Build the final PDDL string from a complete ``DomainDetails`` or ``ProblemDetails`` JSON, or from individual component files. .. code-block:: bash # Full JSON - one-shot domain (recommended for agents) l2p build domain --data '{ "name":"blocksworld", "types":[{"name":"block","parent":"object"}], "predicates":[...], "actions":[...] }' -o domain.pddl # Full JSON - one-shot problem l2p build problem --data '{ "name":"pb1","domain_name":"blocksworld", "objects":[...], "initial_state":{...}, "goal_state":{...} }' -o problem.pddl # Individual component files l2p build domain --name blocksworld \ --types types.json --predicates preds.json \ --actions actions.json -o domain.pddl # Output JSON instead of PDDL l2p build domain --data '{...}' --json File references use the ``@`` prefix (e.g. ``--types @types.json``). .. _cli_validate: ``l2p validate`` - Semantic & Syntax Checking ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Validate individual JSON components or entire ``.pddl`` files against L2P's rule engine. Rules cover naming conventions, type inheritance, variable scope, arity matching, undeclared symbols, and uppercase warnings. .. code-block:: bash # Validate a component JSON snippet l2p validate types --data '[{"name":"block","parent":"object"}]' # Validate a .pddl domain file l2p validate domain domain.pddl # Validate a .pddl problem file (cross-checked against domain) l2p validate problem problem.pddl --domain domain.pddl # Validate full DomainDetails JSON l2p validate domain --data '{"name":"bw","types":[],"predicates":[],"actions":[]}' Three input modes: raw JSON string, JSON file, or ``.pddl`` file. Exits with code 1 on validation failure. .. _cli_plan: ``l2p plan`` - Run a Planner ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Execute a classical planner on PDDL domain and problem strings directly - no temporary files needed (they are managed automatically). .. code-block:: bash # Run Fast Downward with raw PDDL strings l2p plan \ --domain '(define (domain test) ...)' \ --problem '(define (problem p) ...)' \ --planner fast-downward --alias lama-first # Read from files l2p plan --domain @domain.pddl --problem @problem.pddl --json # Use Unified Planning backend l2p plan --domain @d.pddl --problem @p.pddl \ --planner unified --engine aries --json Output with ``--json`` returns a structured :class:`~l2p.planner_builder.PlanningResult`:: { "is_successful": true, "plan": ["(pickup b1)", "(stack b1 b2)"], "error_message": null, "raw_output": "...", "metrics": {} } Two planner backends: * **Fast Downward** - submodule-based, requires the executable path * **Unified Planning** - Python API, requires ``pip install unified-planning[engine]`` .. _cli_schema: ``l2p schema`` - JSON Schema Reference for LLMs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Output the Pydantic JSON Schema for any PDDL component. LLMs can read this to know the exact JSON structure expected by ``l2p set`` and ``l2p build``. .. code-block:: bash # Schema for a single component l2p schema types l2p schema predicates l2p schema actions # Include example JSON l2p schema domain --examples # List all available schemas l2p schema list Available schemas: ``types``, ``constants``, ``predicates``, ``functions``, ``derived-predicates``, ``actions``, ``durative-actions``, ``events``, ``processes``, ``constraints``, ``parameters``, ``objects``, ``initial-state``, ``goal-state``, ``metric``, ``domain``, ``problem``, ``requirements``. End-to-End Agent Workflow ~~~~~~~~~~~~~~~~~~~~~~~~~ Here is how an LLM agent can chain CLI commands to produce a validated PDDL domain and plan: .. code-block:: bash # 1. Get the JSON schema the LLM should follow l2p schema domain --examples # 2. Build domain from JSON l2p build domain --data '{ "name":"blocksworld","types":[{"name":"block","parent":"object"}], "predicates":[...],"actions":[...] }' -o domain.pddl # 3. Validate l2p validate domain domain.pddl # 4. Build problem l2p build problem --data '{ "name":"pb1","domain_name":"blocksworld", "objects":[{"name":"b1","type":"block"}], "initial_state":{"facts":["(on-table b1)"]}, "goal_state":{"conditions":["(holding b1)"]} }' -o problem.pddl # 5. Plan l2p plan --domain @domain.pddl --problem @problem.pddl --json .. _cli_agents_md: AGENTS.md for LLM Agent Tooling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LLM-powered coding assistants (such as `OpenCode `_) use an ``AGENTS.md`` file placed at the project root to understand a codebase - covering setup, architecture, CLI commands, conventions, and testing. Copy the following ``AGENTS.md`` into the root of your L2P checkout so your agent has all the context it needs to work effectively: .. code-block:: text :caption: AGENTS.md - place at project root # Agent Instructions for L2P L2P is a Python library that generates, validates, and plans PDDL models from natural language via LLMs. ## Setup & Dependencies - Python >=3.10 (CI uses 3.10) - Install: `pip install -r requirements.txt` then `pip install -e .` - Extra install groups (use as needed): `cli`, `openai`, `mistral`, `huggingface`, `planner`, `all` - `cli` (`llm` + `rich`) is required for the `l2p` CLI - `planner` (`unified-planning`) required for `UnifiedPlanning` planner backend - Tests: `pytest` (unittest-based, no coverage/typechecking configured) - Lint (errors only): `flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics` - Lint (all warnings): `flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics` ## Environment - Required: `OPENAI_API_KEY` for OpenAI models - Optional: `CLAUDE_API_KEY`, `DEEPSEEK_API_KEY`, `OLLAMA_API_KEY` - LLM config YAMLs: `l2p/llm/utils/llm.yaml` (UnifiedLLM) or `l2p/llm/utils/openaiSDK.yaml` (OPENAI SDK) ## FastDownward Submodule (planner) - Path: `downward/` - must initialize with `git submodule update --init --recursive` - Planner executable: `downward/fast-downward.py` (note: may need building; see FastDownward docs) - Default alias: `lama-first` ## Architecture Two usage modes: **Python API** (programmatic) and **CLI** (stateless, agent-friendly). ### Core classes (`from l2p import ...`) | Class | File | Purpose | |-------|------|---------| | `DomainBuilder` | `l2p/domain_builder.py` | Construct PDDL domains; has `formalize_component()` (LLM extraction), `set_*()` methods, `generate_domain()` | | `ProblemBuilder` | `l2p/problem_builder.py` | Construct PDDL problems; same pattern as DomainBuilder | | `FeedbackBuilder` | `l2p/feedback_builder.py` | LLM-driven feedback loops: diagnose, revise, evaluate, reflect, select | | `PromptBuilder` | `l2p/prompt_builder.py` | Assemble structured prompts with Role/Format/Rules/Examples/Task sections | | `FastDownward`, `UnifiedPlanning` | `l2p/planner_builder.py` | Run external planners; returns `PlanningResult` dataclass | | `UnifiedLLM`, `OPENAI`, `HUGGING_FACE` | `l2p/llm/` | LLM backends | ### PDDL Type Models (`l2p/utils/pddl_types.py`) All PDDL components are Pydantic v2 models: `PDDLType`, `Predicate`, `Action`, `DurativeAction`, `Function`, `DerivedPredicate`, `Constant`, `Requirement`, `Constraint`, `Event`, `Process`, `PDDLObject`, `InitialState`, `GoalState`, `Metric`, `DomainDetails`, `ProblemDetails`. Domain components are set via `DomainBuilder.set_*(value, append=False)`. Problem components via `ProblemBuilder.set_*`. Setting `None` clears the list; `append=True` adds to existing list. ### LLM Extraction Pattern (`formalize_component`) `DomainBuilder.formalize_component()` and `ProblemBuilder.formalize_component()` take: - `model`: LLM instance - `component_class`: single class or list of classes (e.g., `Predicate`, `[PDDLType, Predicate]`) - `description`: natural language description - `**ctx_kwargs`: context like `types=`, `predicates=` - Returns `(dict[Type[BaseModel], List[BaseModel]], raw_llm_output)` The LLM must output JSON wrapped in XML tags like `...`. Default prompts are auto-selected per component class. Using a list of multiple component classes requires a custom `prompt_template`. Requirements are **auto-generated** by `DomainBuilder.generate_requirements()` based on which components are present. ## CLI Commands Entry point: `l2p` (via `l2p.cli.main:main`). All commands are stateless (pass full data each call). | Command | Purpose | Key Flags | |---------|---------|-----------| | `l2p init` | Configure LLM (interactive or `--backend/--provider/--model`) | `--backend unified|openai` | | `l2p config show/edit/reset/validate` | Manage config at `~/.l2p/config.yaml` | | | `l2p models test` | Test LLM connection | | | `l2p schema ` | Show Pydantic JSON Schema for LLM reference | `--examples` | | `l2p set ` | Validate and inject a single PDDL component from JSON | `--data`, `--file`, `--stdin`, `--json`, `--pddl`, `--schema` | | `l2p build domain|problem` | Assemble full PDDL from JSON or component files | `--data`, `-o`, `--json` | | `l2p validate ` | Validate components or .pddl files | `--data`, `--file`, path argument for .pddl | | `l2p plan` | Run planner on PDDL (raw strings or `@file`) | `--domain`, `--problem`, `--planner`, `--alias`, `--json` | | `l2p generate domain|problem` | Interactive LLM-driven generation (requires `l2p init` first) | `--max-retries` | | `l2p templates show/list/set` | Manage prompt templates | | ### CLI Data Input - `--data '...'` - raw JSON string - `--file path.json` - read from file - `--stdin` - pipe JSON from stdin - `@file.pddl` - prefix with `@` to read from file (used in `build` and `plan`) ### CLI Pipeline Pattern (agent-friendly) ```bash l2p schema types --examples # learn JSON shape l2p set types --data '[...]' --json | l2p set predicates --stdin --json l2p build domain --data '{"name":"bw","types":[...]}' -o domain.pddl l2p validate domain domain.pddl l2p plan --domain @domain.pddl --problem @problem.pddl --json ``` ### Recommendations for Agents - **Use the CLI** (`set`/`build`/`validate`/`plan`) for stateless, scriptable workflows. The Python API is better for custom loops. - Before generating any PDDL with an LLM, always run `l2p schema --examples` to get the exact JSON schema an LLM should output. - Always validate generated PDDL before planning: `l2p validate domain domain.pddl`. - For FastDownward, default alias is `lama-first`. Other options: `seq-opt-fdss-1`, `seq-opt-bjolp`. - For UnifiedPlanning backend: `pip install unified-planning unified-planning[engines]` then `--engine aries`. ## Testing - `pytest` runs all tests. No special flags needed. - Mock LLM at `tests/mock_llm.py` - set `mock.output` to a string to simulate LLM responses. - Test data (prompts, PDDL files) under `tests/pddl/`. - Tests use `unittest.TestCase` - runnable with `pytest` or `python -m unittest`. ## Key Conventions - PDDL keywords output by LLMs (AND/OR) are **lowercased** automatically by `generate_domain()` and `generate_problem()`. - Use `format_*` functions from `l2p/utils/pddl_format.py` for standalone PDDL formatting. - Validators in `l2p/validators/` check naming, type hierarchy, param types, variable scope, symbol references, arity. - Backend (`unified` vs `openai`) is automatically inferred from config_path (`llm.yaml` -> unified, `openaiSDK.yaml` -> openai). - The `@require_llm` decorator on `formalize_component()` validates that the model parameter is a `BaseLLM` instance. This file is designed to be consumed by LLM agent tools like `OpenCode `_ that use ``AGENTS.md`` (or ``.opencode/`` skills) to bootstrap context for the coding assistant - see the `OpenCode documentation `_ for more details.