Did you know ... Search Documentation:
Pack le -- api.md

Logical English Web API (Preliminary DRAFT)

The LE API is a JSON-over-HTTP REST endpoint served at `/leapi` (default port 3050).

Authentication

Every request must include a "token" field in the JSON body. The expected token is:

  • The value of the LE_API_TOKEN environment variable, if set when the server started, OR
  • "myToken123" as the historical default fallback when LE_API_TOKEN is unset.

    This behaviour is backwards-compatible: callers using the historical default continue to work unchanged, while deployments needing a real shared secret can set the env var without code changes.

Request / Response format

All requests are HTTP POST with Content-Type: application/json. All responses are JSON objects. On failure the response contains an error key.


Operations

examples — Retrieve a built-in example document

Returns the source text of a named LE example file.

Request

{
  "token": "myToken123",
  "operation": "examples",
  "file": "<example_name>"
}
FieldTypeDescription
filestringName of the example (without extension), e.g. "1_net_asset_value_test_3"

Response

{ "document": "<LE source text>" }

On error: { "answer": "...", "details": "...", "document": "" }

curl example

curl -s -X POST http://localhost:3050/leapi \
  -H 'Content-Type: application/json' \
  -d '{"token":"myToken123","operation":"examples","file":"1_net_asset_value_test_3"}'

answer — Parse a document and answer one query/scenario pair

Loads the LE document, applies the named scenario, and returns an explanation for a single query.

Request

{
  "token": "myToken123",
  "operation": "answer",
  "file": "<program_name>",
  "document": "<LE source text>",
  "theQuery": "<query sentence>",
  "scenario": "<scenario name>"
}
FieldTypeDescription
filestringLogical name / identifier for the program
documentstringFull LE source text
theQuerystringName of the query to run, e.g. "one"
scenariostringName of the scenario to use, e.g. "alice"

Response

{ "answer": <explanation term> }

curl example

curl -s -X POST http://localhost:3050/leapi \
  -H 'Content-Type: application/json' \
  -d '{"token":"myToken123","operation":"answer","file":"testingle",
       "document":"...","theQuery":"one","scenario":"alice"}'

explain — Parse a document and return all answers for a query/scenario

Like answer but collects every answer, not just the first.

Request

{
  "token": "myToken123",
  "operation": "explain",
  "file": "<program_name>",
  "document": "<LE source text>",
  "theQuery": "<query sentence>",
  "scenario": "<scenario name>"
}

Same fields as answer.

Response

{ "results": [ <explanation>, ... ] }

The `<explanation>` is rendered as an HTML string nested-list. For machine consumption of the proof-tree shape (preserving per-node ref, source, and origin annotations), use explain_json instead.


explain_json — Like explain but with a structured JSON proof tree

Identical to explain in inputs and applicability. The difference is in the response shape: each answer's explanation field is a list of structured node dicts rather than a stringified HTML rendering. This preserves the per-node ref (clause reference), source (originating program / module), and origin (the actual Prolog clause that fired) annotations that the HTML rendering strips out via the underscored variables in explanationLEHTML/2.

Designed for agentic / programmatic consumers (e.g. compliance certifiers, structured-output advisory systems) that need to attach proof trees to structured output without having to parse rendered HTML.

Request

{
  "token": "myToken123",
  "operation": "explain_json",
  "file": "<program_name>",
  "document": "<LE source text>",
  "theQuery": "<query sentence>",
  "scenario": "<scenario name>"
}

Same fields as explain / answer.

Response

{
  "results": [
    {
      "answer": "Yes | No | Unknown | Failure",
      "bindings": "<stringified ground goal>",
      "explanation": [
        {
          "type": "proven | failed | unknown",
          "literal": "<stringified goal>",
          "ref": "<clause reference or null>",
          "source": "<source program / module or null>",
          "origin": "<the actual Prolog clause that fired or null>",
          "children": [ <node>, ... ]
        },
        ...
      ]
    },
    ...
  ]
}

Node type values:

  • "proven" — the goal was successfully proved (SLD-resolution succeeded).
  • "failed" — the goal could not be proved.
  • "unknown" — the goal is a hypothesis lacking sufficient facts. For a leaf step that exercised a Prolog built-in (e.g. an arithmetic comparison `160000 is greater or equal to 150000`), ref and origin are null and source carries the LE program name. For a step that exercised a user-defined rule, all four annotation fields are populated.

    curl example

    curl -s -X POST http://localhost:3050/leapi \
      -H 'Content-Type: application/json' \
      -d '{"token":"myToken123",
           "operation":"explain_json",
           "file":"gst",
           "document":"<full LE source>",
           "theQuery":"A",
           "scenario":"A"}'

answer_via_llm — Translate free-form user input via an LLM, then answer

Sends userinput to a configured LLM (Gemini) to generate a new LE scenario/query pair, appends it to document, and then runs the combined program.

Environment variables required on the server:

VariableDescription
LE_LLM_KAPI key for the LLM service
USED_LLMModel identifier, e.g. gemini-2.5-flash

Request

{
  "token": "myToken123",
  "operation": "answer_via_llm",
  "file": "<program_name>",
  "document": "<LE source text>",
  "userinput": "<free-form description / question>"
}
FieldTypeDescription
filestringLogical name for the program
documentstringExisting LE source text used as context
userinputstringNatural-language description of the situation and question

Response

{
  "results": [ <explanation>, ... ],
  "translation": "<LLM-generated LE text>"
}

On failure: "results" contains an error dict and "translation" is "I did not understand".

curl example

curl -s -X POST http://localhost:3050/leapi \
  -H 'Content-Type: application/json' \
  -d '{"token":"myToken123","operation":"answer_via_llm",
       "file":"testllm","document":"...","userinput":"Is Alice eligible?"}'

load — Load a LE or Prolog program into a fresh session module

Parses and asserts a LE (or plain Prolog) program, returning its metadata.

Request — inline LE source

{
  "token": "myToken123",
  "operation": "load",
  "le": "<LE source text>"
}

Request — load from file

{
  "token": "myToken123",
  "operation": "load",
  "file": "<path under /moreExamples/>"
}

The file path must reside under `/moreExamples/`. Files ending in `.le` are parsed as Logical English; all others are loaded as plain Prolog.

Response

{
  "sessionModule": "<generated module name>",
  "kb": "<kb name or null>",
  "predicates": [ "<predicate/arity>", ... ],
  "examples": [ { "name": "...", "scenarios": [ { "assertion": "...", "clauses": "..." } ] } ],
  "queries": [ "<query term>", ... ],
  "language": "le | prolog",
  "target": "taxlog | prolog"
}

The sessionModule value must be passed to subsequent answeringQuery and loadFactsAndQuery calls.


le2prolog — Translate a LE program to Prolog source

Returns the Prolog text equivalent of a LE program without asserting anything.

Request

{
  "token": "myToken123",
  "operation": "le2prolog",
  "le": "<LE source text>"
}

Response

{
  "prolog": "<Prolog source text>",
  "kb": "<kb name>",
  "predicates": [ "<predicate string>", ... ],
  "examples": [ ... ],
  "queries": "<Prolog query clauses>",
  "target": "taxlog | prolog"
}

answeringQuery — Run an English query against a loaded session module

Requires a prior load call to obtain sessionModule.

Request

{
  "token": "myToken123",
  "operation": "answeringQuery",
  "query": "<English query string>",
  "scenario": "<Prolog scenario term>",
  "sessionModule": "<module name from load>"
}

Response

{ "answer": "<answer term as string>", "result": "ok" }

loadFactsAndQuery — Assert facts into a session module and run a goal

Asserts a list of ground facts into an existing session module, then optionally evaluates a goal against them.

Request

{
  "token": "myToken123",
  "operation": "loadFactsAndQuery",
  "sessionModule": "<module name>",
  "facts": [ "<fact term string>", ... ],
  "goal": "<Prolog goal string>",
  "vars": [ "<var name>", ... ]
}

goal and vars are optional. Only ground facts (no :- heads) are accepted.

Response

{
  "facts": [ ... ],
  "goal": "<goal>-(vars)",
  "answers": [ { "bindings": { "<var>": <value> }, "explanation": <tree> } ],
  "result": "true | false | unknown"
}

query — Low-level Prolog/taxlog query (legacy taxkbapi)

Evaluates a Prolog term against a named module's knowledge base, optionally with hypothetical facts.

Request

{
  "token": "myToken123",
  "operation": "query",
  "theQuery": "<Prolog term string>",
  "module": "<module URL or name>",
  "facts": [ "<fact term string>", ... ]
}

facts is optional.

Response

{
  "results": [
    {
      "result": "true | false | unknown",
      "bindings": { "<VarName>": <value>, ... },
      "unknowns": [ { "goal": <term>, "module": "<module>" }, ... ],
      "why": <explanation tree>
    }
  ]
}

curl examples

# Simple query
curl -X POST http://localhost:3050/leapi \
  -H 'Content-Type: application/json' \
  -d '{"token":"myToken123","operation":"query","theQuery":"a(1,Y)","module":"http://tests.com"}'

# With hypothetical facts
curl -X POST http://localhost:3050/leapi \
  -H 'Content-Type: application/json' \
  -d '{"token":"myToken123","operation":"query","theQuery":"a(13,Y)","facts":["d(13)"],"module":"http://tests.com"}'

draft — Draft a Prolog file from web-page content (legacy)

Accepts structured page content and returns a drafted Prolog text.

Request

{
  "token": "myToken123",
  "operation": "draft",
  "pageURL": "http://mysite/page1#section2",
  "content": [
    { "url": "http://mysite/page1#section2!chunk1", "text": "john flies by instruments" }
  ]
}

Response

{ "pageURL": "http://mysite/page1#section2", "draft": "<Prolog text>" }

Explanation tree nodes

The why / explanation fields returned by query and loadFactsAndQuery are arrays of node objects:

{
  "type": "<node type>",
  "literal": "<goal as string>",
  "module": "<module>",
  "source": "<source reference>",
  "textOrigin": "<origin>",
  "children": [ <node>, ... ]
}

Starting the server

?- use_module(api), start_api_server.        % listens on port 3050
?- use_module(api), start_api_server(8080).  % custom port

When loaded inside SWISH the server start is a no-op (SWISH already handles HTTP).