LIBRARY GUIDE · 0.8.5
Reason from facts.
Keep the explanation.
Ting is a deterministic symbolic reasoning library. Write readable .ting modules or Python declarations, run inference, and inspect the evidence behind every conclusion.
Typed schemas and explicit conditions make rules readable and testable.
Control work, inspect outcomes and change facts through transactions.
Follow conclusions to their rules, bindings and supporting facts.
Choose a reading path
- New to Python or Ting: install Ting, read the Python notation, then run Your first reasoner.
- Understand a session: continue through facts, rules, running and changing inputs, and work limits.
- Keep rules in editable files: follow Compile readable source, then the source language. Several source files explains imports.
- Choose a task: evaluate options, explain a result, reuse selected knowledge, or choose how to run cases.
Complete examples include their imports and setup. Short continuations identify the example or saved file they require. Read a code block’s label before copying: python is application code, ting is rule source, and console is a terminal command. Use the API reference to look up signatures, and Availability and limits to check supported uses.
Install and verify
Install Python 3.11 or later first. A terminal is the Command Prompt or PowerShell on Windows, or Terminal on Linux/macOS. Run the commands below in a folder containing the downloaded wheel. A virtual environment keeps this project’s Python packages separate. The commands use its Python directly, so activation is not required.
Windows — Command Prompt or PowerShell
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install --no-deps .\ting_reasoner-0.8.5-py3-none-any.whl
.\.venv\Scripts\python.exe -m ting_reasoner.selftestLinux or macOS — Terminal
python3 -m venv .venv
./.venv/bin/python -m pip install --no-deps ./ting_reasoner-0.8.5-py3-none-any.whl
./.venv/bin/python -m ting_reasoner.selftestThe selftest prints a JSON report. Look for "ok": true. If it fails, keep the complete error and use Troubleshooting. Run this check from a terminal because it also starts worker processes. Ting needs no C compiler or additional runtime packages.
Throughout this manual, python example.py means the Python where you installed Ting. With the environment above, use .\.venv\Scripts\python.exe example.py on Windows or ./.venv/bin/python example.py on Linux/macOS, from the same folder. Save Python examples as .py files; commands labelled console belong in the terminal, not in a Python file.
Install from the source archive instead
Extract the archive, open a terminal in the folder containing pyproject.toml, and create the virtual environment there. Replace the wheel filename in the install command with . (the current folder). Pip may need to download build tools. Then run the same selftest.
Understand the Python calls
You can use Ting from an ordinary Python script. Save Python code in a file such as my_reasoner.py and run python my_reasoner.py from its folder after installing Ting. Readable rules belong in a separate .ting file. The next sections provide complete examples of both approaches.
Names, objects and function calls
A variable is a name for a value or object. In engine = Engine(program), Python calls Engine, passes program as its argument, and assigns the returned session object to the name engine. You choose that variable name; Ting does not require it.
| Python notation | How to read it |
|---|---|
from ting_reasoner import Engine | Make Ting’s Engine class available in this script. Import each name that your code uses. |
engine = Engine(program) | Create a reasoning session. The argument is an already compiled program in this example. |
engine.run() | Call a method: a function belonging to this object. Empty parentheses mean that this call uses its default options. |
result = engine.run() | Save the returned RunResult, which describes how the run stopped. The conclusions are available separately as engine.conclusions. |
engine.conclusions | Read a property. No parentheses are needed. This returns a tuple of conclusion objects, possibly empty. |
compile_file(source_path, overwrite=True) | Pass the source path by position and the optional overwrite argument by name. Set it only when replacing an existing compiled output is intended. |
None, True, False | Python’s no-value marker and Boolean values. Capitalization matters. Ting source uses lowercase null, true, and false. |
(rule,) | A tuple containing one item. The comma is necessary: (rule) is just rule. A tuple is an ordered collection whose items cannot be reassigned. |
{"name": "apple"} | A dictionary: the key "name" maps to the value "apple". Use conclusion.as_dict() to obtain a dictionary of output fields. |
conclusion["name"] | Read one field by name. A missing field raises KeyError. |
Indented lines belong to a block such as a loop, try, or with. Copy the indentation along with an example. Lines beginning with # are comments for the reader. An assert in an example checks an expected outcome and raises AssertionError if it is false; your application should use ordinary conditional checks to handle expected failures.
Read a result safely
The following fragment continues Your first reasoner after its input declarations, using that example’s engine. A for loop visits each conclusion and also works when there are none. Indexing engine.conclusions[0] requires at least one result.
result = engine.run()
if result.status.value == "quiescent":
for conclusion in engine.conclusions:
print(conclusion.name, conclusion.as_dict())
else:
print("Reasoning stopped with:", result.status.value)quiescent means the current rules have no remaining eligible work. A work limit can stop reasoning earlier. An empty result does not by itself mean “rejected” or “impossible”; your rules must explicitly define those decisions. See Work limits for controlled stopping.
Read a signature in the API reference
In compile_file(path, output=None, *, overwrite=False, ...), path is required, output has a default, and arguments after * must be named. An annotation such as str | Path lists accepted types. An arrow such as -> SourceBuild names the returned type. You call the function with values, without copying these annotations or the ... placeholder.
When a call fails, Python raises an exception and skips the remaining statements in its try block. An except SourceError as error block receives Ting’s source error object. An else block runs only when the call succeeded. The build error examples explain what to inspect and how to correct the input.
Your first reasoner
A schema describes data. A rule matches that data and derives facts or emits conclusions. A knowledge base combines schemas and rules; an Engine owns the working session.
from ting_reasoner import Engine, FactSchema, KnowledgeBase, Rule, bind, emit, use
Fruit = FactSchema("fruit", name=str, sweet=bool)
rule = Rule(
"recognize_sweet_fruit",
when=(Fruit.pattern(name=bind("name"), sweet=True),),
then=(emit("sweet_fruit", name=use("name")),),
)
knowledge = KnowledgeBase((Fruit,), (rule,))
engine = Engine(knowledge)
engine.declare(Fruit(name="apple", sweet=True), Fruit(name="lemon", sweet=False))
result = engine.run()
assert result.firings == 1
assert engine.conclusions[0]["name"] == "apple"
print(engine.conclusions[0].as_dict())bind("name") captures a fact value. use("name") refers to that earlier binding. The output is {"name": "apple"}. A conclusion is immutable; as_dict() gives you an ordinary mapping for integration.
What each call contributes
| Call or assignment | Meaning and returned value |
|---|---|
Fruit = FactSchema(...) | Define the allowed fields and exact Python types. Fruit is a schema object, not an individual fruit. |
Fruit(name="apple", sweet=True) | Create one immutable Fact. This checks values, but does not add the fact to a session. |
Fruit.pattern(...) | Create a pattern describing which fruit facts a rule should match. |
bind("name") and use("name") | Capture a matching value, then refer to it in the same rule. This Ting binding is separate from Python’s variables. |
emit("sweet_fruit", ...) | Create an action definition. It emits a structured conclusion when the rule fires; defining it does not execute inference. |
Rule(...) / KnowledgeBase(...) | Create a rule definition, then group schemas and rules into a knowledge base. |
Engine(knowledge) | Compile the knowledge base and create a session. For repeated sessions, compile once and reuse a program. |
engine.declare(fact) | Add one fact and return its FactHandle, which can identify it for later operations. Passing several facts returns a tuple of handles. |
engine.run() | Run the rules and return RunResult. result.firings counts rule firings in this call. |
conclusion.as_dict() | Return a separate dictionary of the conclusion’s values. Editing that dictionary does not change the conclusion. |
Facts and types
A fact is one record created from a schema, such as Fruit(name="apple", sweet=True). Its values cannot be edited in place. Pass several facts to one declare() call to add them together; declaring an identical input fact reuses its handle. Wrong field names or types are rejected before inputs change. A derived fact is produced by a rule and may match other rules. An emitted conclusion is an application result in engine.conclusions; it is not an input to further rule matching.
| Type | Contract |
|---|---|
None, bool, int, float, str, bytes | Portable scalar values. Floats must be finite. Scalar integers retain Python precision. |
| Union fields | Declare a tuple of accepted exact types, such as value=(int, str). |
| Identity | True, 1 and 1.0 are distinct. Positive and negative floating-point zero have distinct exact identities. |
FactHandle | Session-local identity. Base handles are positive; derived handles are negative. Retraction or merging replacement can invalidate a handle. |
NDField / NDValue | Immutable fixed-shape multidimensional values; see Arrays. |
engine.facts returns a tuple of current facts. engine.fact_items returns (handle, fact) pairs. A handle identifies a fact within this session; do not transfer it to another Engine. Read Run and inspect before removing, replacing or resetting inputs.
Rules and conditions
Rules have an explicit name, conditions and consequences. Bindings are established left to right. Reusing a bound name compares exact values. Use Rule(..., salience=10) to request a higher priority within the same dependency level; the default is 0. In readable source the corresponding entry is priority: 10. Dependencies needed for negation take precedence over priority.
| Authoring function | Meaning |
|---|---|
eq, ne | Exact typed identity comparisons. |
lt, le, gt, ge, between | Ordering comparisons require matching operand types. between includes both endpoints. Non-orderable values do not match. |
one_of | Match any listed exact value. |
all_of, any_of, not_ | Combine declarative conditions; alternatives must expose consistent bindings. |
exists | Require a witness, with deterministic first-witness selection; local bindings do not escape. |
for_all | Require every row of the selected schema to satisfy its condition. An empty relation satisfies this condition. |
test | Compare constants or established bindings without arbitrary callbacks. |
derive | Add a typed derived fact. |
emit | Publish a deduplicated conclusion and its provenance. |
from ting_reasoner import Engine, FactSchema, KnowledgeBase, Rule, bind, emit, eq, use
Request = FactSchema("request", orchard=int, variety=str)
Stock = FactSchema("stock", orchard=int, variety=str, boxes=int)
rule = Rule("find_stock", (
Request.pattern(orchard=bind("orchard"), variety=bind("variety")),
Stock.pattern(orchard=eq(use("orchard")), variety=eq(use("variety")), boxes=bind("boxes")),
), (emit("available", variety=use("variety"), boxes=use("boxes")),))
engine = Engine(KnowledgeBase((Request, Stock), (rule,)))
engine.declare(Request(orchard=3, variety="apple"), Stock(orchard=3, variety="apple", boxes=12))
engine.run()
assert engine.conclusions[0]["boxes"] == 12This example joins Request and Stock by requiring both orchard and variety to agree. The expected number of boxes is 12. Rules define comparisons and outputs; your Python application acts on the returned conclusions. A rule cannot use the absence of a fact to derive the very fact whose absence it tests, directly or through a dependency cycle. Such recursion through negation fails compilation. See Write Ting rules for the equivalent source notation.
Run and inspect
engine.run() applies eligible rules until no work remains or a limit stops it. A rule application to a particular combination of facts is called an activation; executing its actions is a firing. Running again without changing inputs continues remaining work and does not repeat already completed firings.
What happens when you change inputs?
By default, an input change immediately clears derived facts, conclusions, proofs and trace. Call run() afterward to rebuild results. An unchanged replacement, duplicate input declaration or missing-handle retraction makes no change. Optional support maintenance can retain still-supported results; it also requires run() to find new consequences.
| Operation | Result |
|---|---|
declare(*facts) | One fact returns one handle; multiple facts return handles in argument order. Publication is atomic. |
replace(handle, fact) | Replace a base fact; an exact duplicate may merge into an existing handle. |
retract(handle) | Remove an input fact. The update behavior described above applies to current inferred results. |
reset(*facts) | Start a new case with these inputs and any fixed seeds. Discard old handles; their numeric IDs may be reused. |
run(max_firings=...) | Bound successful firings. Zero permits matching without firing. |
conclusions / trace / provenance | Immutable views of conclusions, ordered events and proof edges. |
snapshot() | Save the current facts, results and proofs. Its epoch is the session’s change counter; later edits do not change this snapshot. |
Read result.status.value: "quiescent" means no eligible work remains; "firing_limit" or "budget_exhausted" means the run stopped at a limit. Results can be incomplete in those two cases. Counts such as result.firings describe this call. See Work limits for resuming a stopped run. Each firing either commits all its actions or none; an exception later in the run does not undo previous successful firings.
Work limits
Use logical work budgets when predictable exhaustion boundaries matter. Candidate rows, constraint evaluations, activations and firings are charged before the corresponding work. A call accepts either max_firings or budget.
from ting_reasoner import Engine, ExecutionBudget, FactSchema, KnowledgeBase, Rule, bind, emit, use
Item = FactSchema("item", value=int)
rule = Rule("record", (Item.pattern(value=bind("v")),), (emit("value", value=use("v")),))
engine = Engine(KnowledgeBase((Item,), (rule,)))
engine.declare(*(Item(value=i) for i in range(5)))
result = engine.run(budget=ExecutionBudget(max_candidate_rows=2, max_firings=10))
assert result.status.value == "budget_exhausted"
assert result.exhausted_budget == "candidate_rows"
assert result.budget_usage.candidate_rows == 2Inspect exhausted_budget and budget_usage before deciding how to resume. Repeating an insufficient matching budget can repeat the same scan; matching exhaustion is not a saved cursor through arbitrary partial work. Firing exhaustion is resumable. A budget bounds counted work, not elapsed time or total memory. Supplying one can affect speed; compare the same budget settings when measuring performance.
Transactions
A transaction stages base-fact declarations, retractions and replacements. It commits all edits together. The expected epoch is the session’s change counter, used to detect an intervening update. A stale transaction raises StaleTransactionError; an exception inside the context rolls it back.
from ting_reasoner import Engine, FactSchema, KnowledgeBase
Reading = FactSchema("reading", sensor=str, value=int)
engine = Engine(KnowledgeBase((Reading,)))
with engine.transaction(expected_epoch=engine.epoch) as tx:
staged = tx.declare(Reading(sensor="north", value=21))
handle = tx.receipt.resolve(staged)
assert handle is not None
assert engine.facts == (Reading(sensor="north", value=21),)
snapshot = engine.snapshot()
assert snapshot.epoch == engine.epochA staged handle belongs to its creating transaction. Resolve it through the commit receipt after success. Run inference after the transaction closes. Snapshots are immutable views, not mutable clones of an Engine.
Compile readable source
Before this section: Understand the Python calls and Your first reasoner.
You choose where your files live. Save editable knowledge as a UTF-8 .ting file in any folder you can write to. Pass its path to compile_file(), which validates the source, runs its embedded tests, and writes a portable .tingir program. You can choose a different folder for that compiled file.
1. Choose the source folder and filename
Run this Python code to create an example folder under your home directory and print the full source path. orchard.ting is an example filename; you can use your own name. Creating the folder does not yet create the source file.
from pathlib import Path
project_dir = Path.home() / "ting_examples"
project_dir.mkdir(parents=True, exist_ok=True)
source_path = project_dir / "orchard.ting"
print("Save the Ting source here:", source_path)Path.home() supplies your actual home directory on Windows, Linux or macOS. To choose another folder, replace the project_dir assignment above with a path for your system, then create the folder in the same way:
| System | Example folder assignment |
|---|---|
| Windows | project_dir = Path(r"D:\My Ting Rules") |
| Linux | project_dir = Path("/home/alex/my_ting_rules") |
| macOS | project_dir = Path("/Users/alex/my_ting_rules") |
These are illustrative paths: replace the drive, username and folder with your own. The / operator joins folders and filenames for the operating system running Python; spaces and Unicode names are supported. The r before a Windows string preserves its backslashes. Windows drive paths do not become Linux paths. If a user supplies a path beginning with ~, call Path(value).expanduser() to expand the home directory.
2. Save the readable source at that path
In your text editor, use Save As and save the following text to the full path printed as source_path. Choose UTF-8 encoding and the exact filename orchard.ting, without an added .txt extension. The schema describes input facts, the conclusion describes an output, and the test checks that an apple is reported while a lemon is not.
# Edit this file, then call compile_file("orchard.ting").
ting source 1
requires semantics 1
module orchard version "1.0.0" role program
schemas:
schema Fruit:
name: text required
sweet: boolean required
conclusions:
conclusion SweetFruit:
name: text required
rules:
rule recognize_sweet_fruit:
when:
match Fruit:
name: bind name
sweet: true
then:
conclude SweetFruit:
name: use name
tests:
test apple_is_sweet:
given:
fact Fruit:
name: "apple"
sweet: true
fact Fruit:
name: "lemon"
sweet: false
expect:
conclusion SweetFruit:
name: "apple"
firings: 1If your application already holds that text in a Python string called source_text, save it through the same path:
with source_path.open("x", encoding="utf-8") as stream:
stream.write(source_text)Use the editor or the Python save operation to create the source file. Mode "x" raises FileExistsError if the file already exists; use "w" only when you intend to replace its contents. Folder creation and source writing use ordinary Python filesystem exceptions such as PermissionError. Ting's compile_file() reads an existing source file; it does not create that editable source for you.
3. Compile the saved file, then load and run it
This code uses source_path from step 1. Keep it in the same Python session or script. If you run it separately, repeat the folder and source-path assignments first.
from ting_reasoner import Engine, compile_file, load_tingir
built = compile_file(source_path)
print("Compiled program:", built.output)
# Default: orchard.tingir in the same folder as orchard.ting.
engine = Engine(load_tingir(built.output))
Fruit = engine.schema("orchard.Fruit")
engine.declare(Fruit(name="pear", sweet=True))
engine.run()
print(engine.conclusions[0].as_dict()) # {"name": "pear"}built contains the verified program, test report, source analysis, output path and digest. Use Engine(built.program) to run immediately without reloading the file. built.schema("Fruit") accepts a name visible in the root module; after loading an artifact alone, engine.schema("orchard.Fruit") uses its exact qualified name.
4. Choose a different compiled-output folder
Pass a second argument to select the full compiled filename. Create its parent directory first. This example keeps compiled programs in a compiled subfolder; output_dir can instead be any other writable absolute folder path.
output_dir = project_dir / "compiled"
output_dir.mkdir(parents=True, exist_ok=True)
output_path = output_dir / "orchard.tingir"
built = compile_file(source_path, output_path)
print(built.output) # The full path to compiled/orchard.tingir.
engine = Engine(load_tingir(built.output))| Call | Where the compiled file is written |
|---|---|
compile_file(source_path) | Beside the source, with .ting replaced by .tingir. |
compile_file(source_path, output_path) | At the full .tingir filename you supply. |
compile_files(source_paths, output_dir) | One .tingir file per source, inside your chosen output directory; see Several source files. |
The compiler requires existing parent directories and protects existing outputs. To deliberately rebuild the same compiled file, call compile_file(source_path, output_path, overwrite=True). Compilation and embedded tests must succeed before the previous compiled file is replaced. Catch SourceError for compilation or publication failures; see Handle build errors.
What does a filename without a folder mean?
compile_file("orchard.ting") looks in the Python process's current working directory. Print Path.cwd() to see that directory. Relative paths for explicit outputs and imported module files also start there. Use the explicit source_path and output_path variables above when you want the same locations regardless of where your application is launched.
Complete Python example: save, compile and run in a chosen folder
This runnable example uses a temporary folder so it can be repeated safely. Its last comment shows how to retain the files under your home directory instead. It demonstrates both the default output location and an explicitly chosen compiled folder.
from pathlib import Path
from tempfile import TemporaryDirectory
from ting_reasoner import Engine, SourceError, compile_file, load_tingir
source_text = """# Edit this file, then call compile_file("orchard.ting").
ting source 1
requires semantics 1
module orchard version "1.0.0" role program
schemas:
schema Fruit:
name: text required
sweet: boolean required
conclusions:
conclusion SweetFruit:
name: text required
rules:
rule recognize_sweet_fruit:
when:
match Fruit:
name: bind name
sweet: true
then:
conclude SweetFruit:
name: use name
tests:
test apple_is_sweet:
given:
fact Fruit:
name: "apple"
sweet: true
fact Fruit:
name: "lemon"
sweet: false
expect:
conclusion SweetFruit:
name: "apple"
firings: 1
"""
def save_and_run(project_dir):
project_dir = Path(project_dir).expanduser().absolute()
project_dir.mkdir(parents=True, exist_ok=True)
source_path = project_dir / "orchard.ting"
# "x" creates a new source file and protects an existing one.
with source_path.open("x", encoding="utf-8") as stream:
stream.write(source_text)
beside_source = compile_file(source_path)
assert beside_source.output == source_path.with_suffix(".tingir")
output_dir = project_dir / "compiled"
output_dir.mkdir(parents=True, exist_ok=True)
output_path = output_dir / "orchard.tingir"
built = compile_file(source_path, output_path)
assert built.output == output_path and output_path.is_file()
assert source_path.read_text(encoding="utf-8") == source_text
# An existing compiled file also requires an explicit overwrite choice.
try:
compile_file(source_path, output_path)
except SourceError as error:
assert error.diagnostic.code == "TING-IO-003"
else:
raise AssertionError("Expected existing output to be protected")
built = compile_file(source_path, output_path, overwrite=True)
engine = Engine(load_tingir(built.output))
Fruit = engine.schema("orchard.Fruit")
engine.declare(Fruit(name="pear", sweet=True))
engine.run()
assert engine.conclusions[0]["name"] == "pear"
print("Source:", source_path)
print("Compiled:", built.output)
print(engine.conclusions[0].as_dict())
# A disposable folder keeps this complete demonstration repeatable.
with TemporaryDirectory() as directory:
save_and_run(Path(directory) / "My Ting files café")
# For files you want to keep, replace the two lines above with:
# save_and_run(Path.home() / "ting_examples")Compile when rules or schemas change. Reuse the program for new facts and independent sessions. Facts under tests: are used only to check the rules; they are not the inputs of your application. An Engine begins with any fixed seeds in its program. Supply observations for the current case with declare().
Source held in memory
compile_source(text) performs the same compilation and tests without writing files. This complete example can run in a script or notebook.
from ting_reasoner import Engine, compile_source
source = """# Edit this file, then call compile_file("orchard.ting").
ting source 1
requires semantics 1
module orchard version "1.0.0" role program
schemas:
schema Fruit:
name: text required
sweet: boolean required
conclusions:
conclusion SweetFruit:
name: text required
rules:
rule recognize_sweet_fruit:
when:
match Fruit:
name: bind name
sweet: true
then:
conclude SweetFruit:
name: use name
tests:
test apple_is_sweet:
given:
fact Fruit:
name: "apple"
sweet: true
fact Fruit:
name: "lemon"
sweet: false
expect:
conclusion SweetFruit:
name: "apple"
firings: 1
"""
built = compile_source(source)
assert built.tests.passed and built.tests.total == 1
engine = Engine(built.program)
Fruit = built.schema("Fruit")
engine.declare(Fruit(name="pear", sweet=True))
engine.run()
assert engine.conclusions[0]["name"] == "pear"
print(engine.conclusions[0].as_dict())Write Ting rules
Each readable .ting file defines a module: a named collection of declarations. Begin with the three header lines shown in Compile readable source. The ting source 1 and requires semantics 1 lines select the language; the module line supplies your own name, three-part knowledge version and role. That module version describes your knowledge and is independent of the installed Ting version. Use program for a runnable program, library for reusable declarations with explicit exports, and test for validation. The role determines which object compilation returns; see Compiled libraries before compiling a library. A test module is not deployable.
Use two spaces per indentation level, UTF-8 text, and # comments outside quoted strings. Strings use double quotes and JSON escapes. Top-level sections such as schemas: and rules: may be reordered, but each occurs at most once. Names are ASCII identifiers; quoted values can contain Unicode. No expressions execute Python.
| Declaration | Meaning and example |
|---|---|
schemas: | Required typed input/derived fields: name: text required. |
conclusions: | Required typed output fields, checked before building. |
match Fruit: | A row pattern. A literal means equality; omitted fields are unconstrained. |
name: bind name | Capture a field. Use use name after the binding is established. |
temperature: ge 60.0 | Compare a field with an exact matching scalar type. |
all: / any: / not: | Combine child conditions. All conditions directly under when are ANDed. |
exists / not exists / for_all / not for_all | Quantify a schema pattern. Quantified local bindings do not escape. An empty relation satisfies for_all. |
test ge: | Compare child operands such as use amount and 10 without a callback. |
derive Schema: | Create a typed fact usable by later rules. |
conclude Result: | Emit a typed application result together with its rule evidence. |
priority: 0 / version: "1" | Optional rule entries. These documented defaults appear in analysis. |
Scalar types are text, integer, float, boolean, bytes and null. Unions use text | null required. There are no implicit defaults or numeric conversions. true, 1 and 1.0 remain different values. Bytes use bytes "01ff"; floats must be finite.
Pattern operators are eq, ne, lt, le, gt, ge, between, one_of. Write between [10,20] or one_of [1,2,3] for literal lists. Bind a field and use separate test conditions for several comparisons. Every alternative must establish the same bindings; references to missing bindings and recursion through negation fail compilation.
Add requires feature arrays after the module header for fields such as cells: array i64[2,2] required and values such as array i64[2,2] [1,2,3,4]. Supported element types are bool, i64 and f64; see Arrays for construction and supported operations.
Embedded tests
Each named test runs in a fresh bounded session. Add given seed to include fixed program facts; otherwise the test starts without them. given: supplies facts; expect: lists the exact conclusions; an empty expect block requires none. expect facts: checks the exact final fact collection. firings: optionally checks a count. The expected status defaults to "quiescent". Tests belonging to an imported library do not run as part of the importing program; compile and test each library separately.
By default a test permits at most 1,000,000 candidate rows, 1,000,000 constraint evaluations, 100,000 activations and 10,000 firings. Pass a finite test_budget to change those limits. A failed test prevents that output file from being published. run_tests=False explicitly skips tests; zero tests is reported as zero tests.
Inspection and explanation
parse_source() retains exact text, comments, locations and syntax nodes. format_source() preserves statements and comments while normalizing line endings. analyze_source() returns typed declarations, the resolved graph and semantic identity. AuthoringBackend groups formatting, analysis, test and build operations for applications that provide their own editor.
built.origin_for(conclusion.rule) returns the rule declaration and source line. Keep the build result for this lookup. For a complete example and the distinction between rule proofs and source locations, read Explain a conclusion.
For fixed reference facts, use named seeds with requires feature seed-facts. For explicit nominal types and parent membership, use classifications. Reference fields, units, enums, named export sets and temporal declarations are not supported. Unsupported declarations produce errors rather than being ignored.
Handle build errors
Catch SourceError around compile_file(), compile_source(), save_tingir() and load_tingir(). Its diagnostic supplies a stable code, readable message, correction hint, and file/line/column when available. as_dict() is JSON-compatible. An embedded test failure also provides test_report.
from pathlib import Path
from ting_reasoner import SourceError, compile_file
project_dir = Path.home() / "ting_examples"
source_path = project_dir / "orchard.ting"
output_path = project_dir / "compiled" / "orchard.tingir"
try:
built = compile_file(source_path, output_path)
except SourceError as error:
print(error)
details = error.as_dict() # For an editor, log or application response.
else:
print(built.output, built.digest)| Error | Recovery |
|---|---|
TING-SRC-001 / 002 | Fix encoding, indentation, quoted strings or the indicated syntax. |
TING-SRC-003 | Use a supported feature; unknown declarations are never ignored. |
TING-SRC-004 / 005 / 006 / 007 | Correct duplicate/unknown names, field types, binding order or linked rule dependencies. |
TING-SRC-008 | Inspect failed embedded assertions in error.test_report. |
TING-SRC-009 | Reduce source/graph/batch size or deliberately change its limits. |
TING-SRC-010 / 011 / 012 | Correct module mapping, exports, roles, cycles or exact versions. |
TING-IO-001 / 002 | Check input paths, parent directories and read/write permissions. |
TING-IO-003 | An existing output was protected. Choose another path or pass overwrite=True. |
TING-IO-004 | Use .tingir for output and distinct source/output paths and filenames. |
TING-IR-001 | The compiled file is damaged or incompatible; rebuild from preserved source. |
Some errors concern the whole program and have no source span; check for None before reading location fields. Invalid Python argument types or option values can raise TypeError or ValueError. Inspect built.tests to distinguish passed tests, zero tests and a skipped test run.
Runnable example: catch an invalid field type
from ting_reasoner import SourceError, SourceSnapshot, compile_source
source = """ting source 1
requires semantics 1
module example version "1.0.0" role program
schemas:
schema Item:
value: mystery required
"""
try:
compile_source(SourceSnapshot(source, "example.ting"))
except SourceError as error:
diagnostic = error.diagnostic
assert diagnostic.code == "TING-SRC-006"
assert diagnostic.span.line == 6
print(error)
print(error.as_dict())
else:
raise AssertionError("Invalid type should fail")Protect output and control resources
Compilation and tests finish before publication. Source files cannot be used as compiled destinations, including known path aliases. Existing outputs survive a failed rebuild. Use a destination directory controlled by your application; path checks are not a defense against concurrent malicious directory changes.
SourceLimits defaults: 1 MiB per document, 8 MiB across modules, 64 modules, 50,000 lines per document, 16 KiB per line, 100,000 nodes across the graph, depth 32, and 128 integer digits. IRLimits separately bounds the emitted program. These are logical limits, not exact process-memory accounting.
Several source files
Before this section: Compile readable source and Write Ting rules.
For one program composed from several files, put reusable declarations in library modules with explicit exports. The root imports specific names and exact module versions. Supply a module-to-file mapping to the API; the compiler does no directory or network search.
The complete in-memory example below defines both modules. To use the file-based call first, save its types_source text as fruit_types.ting and its main_source text as orchard_modular.ting in your chosen folder. Matching files are also available in the source bundle. This call requires those two saved files:
from pathlib import Path
from ting_reasoner import compile_file
project_dir = Path.home() / "ting_examples"
output_dir = project_dir / "compiled"
output_dir.mkdir(parents=True, exist_ok=True)
built = compile_file(
project_dir / "orchard_modular.ting",
output_dir / "orchard_modular.tingir",
modules={"orchard.types": project_dir / "fruit_types.ting"},
)The mapping key orchard.types is the module name declared inside the provider. Its value is the actual file path. Each module file may live in a different folder; pass that folder's full path explicitly. Relative module paths are resolved from Path.cwd(), just like other relative file arguments.
Importing a schema alone does not activate rules or seeds. Import the rule you need. Its declared requires: dependencies bring in the named helper rules and seeds automatically; Ting does not infer undeclared dependencies from matching schema names. See Make a rule’s dependencies explicit for a complete example. Aliases use schema Fruit as Produce and retain the same qualified runtime identity. Wildcards, re-exports, dynamic imports, version ranges, and import cycles are rejected. The linked program is checked against one set of global IRLimits.
Complete two-module example in memory
from ting_reasoner import Engine, MemoryResolver, compile_source
types_source = """ting source 1
requires semantics 1
module orchard.types version "1.0.0" role library
exports:
schema Fruit
conclusion SweetFruit
rule recognize_sweet_fruit
schemas:
schema Fruit:
name: text required
sweet: boolean required
conclusions:
conclusion SweetFruit:
name: text required
rules:
rule recognize_sweet_fruit:
when:
match Fruit:
name: bind name
sweet: true
then:
conclude SweetFruit:
name: use name
"""
main_source = """ting source 1
requires semantics 1
module orchard.app version "1.0.0" role program
imports:
from orchard.types version "1.0.0":
schema Fruit
conclusion SweetFruit
rule recognize_sweet_fruit
tests:
test pear_is_sweet:
given:
fact Fruit:
name: "pear"
sweet: true
expect:
conclusion SweetFruit:
name: "pear"
firings: 1
"""
built = compile_source(main_source,
resolver=MemoryResolver({"orchard.types": types_source}))
assert built.tests.passed
engine = Engine(built.program)
engine.declare(built.schema("Fruit")(name="apple", sweet=True))
engine.run()
assert engine.conclusions[0]["name"] == "apple"The source bundle includes matching fruit_types.ting and orchard_modular.ting files, plus a separate equipment-temperature example.
Use the same path rules for every source and output. Relative module paths start at the process’s working directory, as explained under relative filenames.
Build several independent programs
compile_files() takes a list of existing source paths and an output directory. It builds each source separately and returns an ordered receipt for each. To try this example, save or copy orchard.ting and equipment.ting from the source bundle's examples into your chosen folder:
from pathlib import Path
from ting_reasoner import compile_files
project_dir = Path.home() / "ting_examples"
output_dir = project_dir / "compiled"
output_dir.mkdir(parents=True, exist_ok=True)
source_paths = [project_dir / "orchard.ting", project_dir / "equipment.ting"]
reports = compile_files(source_paths, output_dir)
for report in reports:
print(report.source, report.output, report.ok)
if not report.ok:
print(report.as_dict()["diagnostics"])The destinations are output_dir / "orchard.tingir" and output_dir / "equipment.tingir". Each receipt includes the full source and output paths. Pass overwrite=True to compile_files() only when replacing previous outputs is intended. Inspect report.ok and report.diagnostics; a failed file does not stop later files. The output directory must exist. Output names must be unique, including case, and must not alias any source.
Complete batch example with a failed file and a successful file
from pathlib import Path
from tempfile import TemporaryDirectory
from ting_reasoner import compile_files
valid = """ting source 1
requires semantics 1
module empty version "1.0.0" role program
"""
with TemporaryDirectory() as directory:
source_dir = Path(directory) / "My rules"
output_dir = Path(directory) / "Compiled programs"
source_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents=True, exist_ok=True)
(source_dir / "valid.ting").write_text(valid, encoding="utf-8")
(source_dir / "invalid.ting").write_text("bad source", encoding="utf-8")
source_paths = [source_dir / "invalid.ting", source_dir / "valid.ting"]
reports = compile_files(source_paths, output_dir)
assert [report.ok for report in reports] == [False, True]
assert Path(reports[1].output) == output_dir / "valid.tingir"
assert (output_dir / "valid.tingir").is_file()
assert not (output_dir / "invalid.tingir").exists()
for report in reports:
print(report.as_dict())Each file is published atomically. The whole batch is not one filesystem transaction, so earlier successful outputs remain if a later root fails. The default maximum is 256 roots; use max_files to choose another explicit bound. Receipts do not retain full compiled plans or test facts.
Portable program files
| File or API | Purpose |
|---|---|
.ting | Editable UTF-8 Source 1 module. |
.tingir | Compiled program or compiled library. The loader identifies which kind it contains; library selection produces a runnable program. |
compile_file / compile_source | Compile source; validate types/imports and run embedded tests. |
save_tingir / load_tingir | Publish or load a compiled program or indexed library; the binary header identifies its kind. |
.tingspec.json | Editable JSON rule specification used with the ting-build commands. |
To use a compiled program on another computer, install a compatible Ting version, copy the file, and pass its actual path to load_tingir(). Follow Install and verify on that computer before running your application. Preserve the editable source when you need to change the rules.
Use compile_file() to create a compiled file from readable source, and load_tingir() to load it. The loader checks the file's format and compatibility. If loading reports an unsupported format, use a compatible Ting installation or recompile the original source. Renaming a file extension does not convert its contents.
Load an existing binary .ting file
For a compiled binary file with a .ting extension, use load_ting(). Save it as .tingir with these calls. This preserves the compiled program; it does not recover readable source.
from ting_reasoner import load_ting, save_tingir
save_tingir(load_ting("compiled_rules.ting"), "compiled_rules.tingir")This conversion requires an existing binary file at the supplied path. Use .tingir for compiled output. For readable .ting text, use compile_file() instead.
Optional command line
The source commands call the same public authoring backend as Python applications. Run the following commands from the folder containing orchard.ting, or replace the filenames with full paths. Quote paths that contain spaces. The -o argument is the full compiled filename, and its parent directory must already exist. Use repeated --module NAME=PATH arguments for explicit imports.
ting-build check orchard.ting --json
ting-build test-source orchard.ting --json
ting-build compile orchard.ting -o orchard.tingir --jsonExit status 0 means success, 1 means source/test failure, and 2 means argument, I/O or compiled-artifact failure. ting-build build takes a .tingspec.json RuleSpec project, a different input format from readable .ting source. Use ting-build build --help for its options and a .tingir output. HistoryStore manages checkpoints for RuleSpec JSON projects; it does not store readable .ting source history.
Save a program created through Python
This complete example creates a knowledge base, saves it to a temporary compiled file, loads it, and declares one fact. No source file is required.
from pathlib import Path
from tempfile import TemporaryDirectory
from ting_reasoner import Engine, FactSchema, KnowledgeBase, load_tingir, save_tingir
Fruit = FactSchema("fruit", name=str)
knowledge = KnowledgeBase((Fruit,))
with TemporaryDirectory() as directory:
path = Path(directory) / "fruit.tingir"
save_tingir(knowledge, path)
engine = Engine(load_tingir(path))
engine.declare(Fruit(name="apple"))
assert len(engine.facts) == 1Include fixed knowledge
Before this section: Compile readable source; its two-file example also uses source imports.
Put stable facts, such as equipment limits or reference data, in a program's knowledge: region. Each named seed is a typed, immutable fact. It is stored in the compiled program and available when an Engine is created, including in independent process workers.
1. Choose a folder and define reusable knowledge
Create a folder for these two source files and print their full paths:
from pathlib import Path
project_dir = Path.home() / "ting_examples" / "inspection"
project_dir.mkdir(parents=True, exist_ok=True)
print("Save thresholds here:", project_dir / "thresholds.ting")
print("Save the program here:", project_dir / "inspection.ting")Save the following source at project_dir / "thresholds.ting". The value is illustrative; choose limits appropriate to your application. The feature declaration makes the file's requirements explicit. Export both the schema and the seed when consumers need both.
ting source 1
requires semantics 1
module equipment.limits version "1.0.0" role library
requires feature seed-facts
exports:
schema Threshold
seed inspection_limit
schemas:
schema Threshold:
metric: text required
maximum: integer required
knowledge:
seed inspection_limit: Threshold:
metric: "vibration"
maximum: 20
tests:
test fixed_limit:
given seed
expect facts:
fact Threshold:
metric: "vibration"
maximum: 202. Use the knowledge in an inspection rule
Save this source at project_dir / "inspection.ting". The rule compares a session reading with the stored threshold. Its result is a typed record containing the device, measurement and limit.
ting source 1
requires semantics 1
module equipment.inspection version "1.0.0" role program
requires feature seed-facts
imports:
from equipment.limits version "1.0.0":
schema Threshold
seed inspection_limit
schemas:
schema Reading:
device: text required
metric: text required
value: integer required
conclusions:
conclusion InspectionRequired:
device: text required
measured: integer required
limit: integer required
rules:
rule inspection_required:
when:
match Threshold:
metric: bind metric
maximum: bind maximum
match Reading:
device: bind device
metric: eq use metric
value: bind measured
test gt:
use measured
use maximum
then:
conclude InspectionRequired:
device: use device
measured: use measured
limit: use maximum
tests:
test above_limit:
given seed
given:
fact Reading:
device: "pump_a"
metric: "vibration"
value: 25
expect:
conclusion InspectionRequired:
device: "pump_a"
measured: 25
limit: 20
firings: 1
test at_limit:
given seed
given:
fact Reading:
device: "pump_a"
metric: "vibration"
value: 20
expect:
firings: 0
test without_program_knowledge:
given:
fact Reading:
device: "pump_a"
metric: "vibration"
value: 25
expect:
firings: 03. Compile and evaluate a reading
from ting_reasoner import Engine, compile_file, load_tingir
# project_dir is the folder selected in step 1.
output_dir = project_dir / "compiled"
output_dir.mkdir(parents=True, exist_ok=True)
built = compile_file(project_dir / "inspection.ting",
output_dir / "inspection.tingir",
modules={"equipment.limits": project_dir / "thresholds.ting"})
engine = Engine(load_tingir(built.output))
Reading = engine.schema("equipment.inspection.Reading")
engine.declare(Reading(device="pump_a", metric="vibration", value=25))
result = engine.run()
if result.status.value == "quiescent":
print([item.as_dict() for item in engine.conclusions])
# [{"device": "pump_a", "measured": 25, "limit": 20}]Check the run status before treating a result as complete. In this example, no conclusion means the inspection rule did not match. It does not assert that the equipment is safe or that every needed observation was supplied.
Keep stable reference facts in knowledge: and supply changing observations with declare(). Importing a schema alone does not import its provider’s seeds. In this example, import seed inspection_limit explicitly. An imported rule also brings in seeds listed in its requires: dependencies; see rule dependencies. An alias such as seed inspection_limit as local_name changes the source spelling while preserving the qualified runtime name.
Lifetime and error handling
| Operation | Behavior |
|---|---|
Create an Engine / call reset() | All program seeds are present. Reset clears session and derived facts; it retains fixed knowledge. |
| Declare an identical fact | Reuse the seed handle. There is no second mutable copy. |
| Retract or replace a seed | Raise ImmutableKnowledgeError before changing state. The same protection applies inside a transaction and when replacement merges with a seed. |
| Two names with identical facts | Share one fact row while retaining both names for inspection. |
| Reuse a name for different facts | Reject the knowledge base. Source diagnostics identify the declaration when available. |
| Change fixed knowledge | Edit the source, compile a new program, and create an Engine from that program. |
engine.seed_facts lists named seeds. engine.seed_handle(name) looks up an exact qualified name and raises KeyError if absent. engine.seed_names_for(handle) and snapshot.seed_names_for(handle) identify fixed knowledge among a proof's supporting facts. An empty tuple means the handle is not a seed in that session. Handles remain session-local.
Keep the build result to locate a declaration with built.origin_for("equipment.limits.inspection_limit", kind="seed"), or a value with kind="seed-field" and name equipment.limits.inspection_limit.maximum. The compiled file preserves seed identities and values; source locations require the source analysis.
Declare fixed knowledge through Python
from ting_reasoner import (
Engine, FactSchema, ImmutableKnowledgeError, KnowledgeBase, SeedFact,
)
Threshold = FactSchema("threshold", metric=str, maximum=int)
knowledge = KnowledgeBase(seeds=(
SeedFact("inspection_limit", Threshold(metric="vibration", maximum=20)),
))
engine = Engine(knowledge)
handle = engine.seed_handle("inspection_limit")
before = engine.snapshot()
try:
engine.retract(handle)
except ImmutableKnowledgeError as error:
assert error.code == "TING-KNOWLEDGE-001"
print(error.as_dict())
else:
raise AssertionError("Fixed knowledge must be protected")
assert engine.snapshot() == before
engine.reset()
assert engine.seed_facts == knowledge.seeds
assert engine.snapshot().seed_names_for(handle) == ("inspection_limit",)Tests and resource limits
Embedded tests start without program seeds unless they include given seed. Use it with given: to test fixed knowledge and changing observations together. Test fixtures never become deployed facts. A failed test protects any existing compiled output.
Seeds accept typed literals, including supported array values; they cannot execute expressions or callbacks. IRLimits defaults to at most 65,536 named seeds and 1,000,000 seed field values. Overall source, payload, constant and verification-work limits also apply. RuleSpec JSON cannot represent seeds; use readable .ting or the Python API for this knowledge.
Complete example in memory
from ting_reasoner import Engine, MemoryResolver, compile_source
limits = """ting source 1
requires semantics 1
module equipment.limits version "1.0.0" role library
requires feature seed-facts
exports:
schema Threshold
seed inspection_limit
schemas:
schema Threshold:
metric: text required
maximum: integer required
knowledge:
seed inspection_limit: Threshold:
metric: "vibration"
maximum: 20
tests:
test fixed_limit:
given seed
expect facts:
fact Threshold:
metric: "vibration"
maximum: 20
"""
inspection = """ting source 1
requires semantics 1
module equipment.inspection version "1.0.0" role program
requires feature seed-facts
imports:
from equipment.limits version "1.0.0":
schema Threshold
seed inspection_limit
schemas:
schema Reading:
device: text required
metric: text required
value: integer required
conclusions:
conclusion InspectionRequired:
device: text required
measured: integer required
limit: integer required
rules:
rule inspection_required:
when:
match Threshold:
metric: bind metric
maximum: bind maximum
match Reading:
device: bind device
metric: eq use metric
value: bind measured
test gt:
use measured
use maximum
then:
conclude InspectionRequired:
device: use device
measured: use measured
limit: use maximum
tests:
test above_limit:
given seed
given:
fact Reading:
device: "pump_a"
metric: "vibration"
value: 25
expect:
conclusion InspectionRequired:
device: "pump_a"
measured: 25
limit: 20
firings: 1
test at_limit:
given seed
given:
fact Reading:
device: "pump_a"
metric: "vibration"
value: 20
expect:
firings: 0
test without_program_knowledge:
given:
fact Reading:
device: "pump_a"
metric: "vibration"
value: 25
expect:
firings: 0
"""
built = compile_source(inspection,
resolver=MemoryResolver({"equipment.limits": limits}))
assert built.tests.passed and built.tests.total == 3
engine = Engine(built.program)
Reading = built.schema("Reading")
engine.declare(Reading(device="pump_a", metric="vibration", value=25))
result = engine.run()
assert result.status.value == "quiescent"
assert engine.conclusions[0].as_dict() == {
"device": "pump_a", "measured": 25, "limit": 20,
}
assert engine.seed_names_for(engine.seed_handle("equipment.limits.inspection_limit"))
engine.reset()
assert len(engine.facts) == 1 and not engine.conclusionsClassifications and inheritance
Before this section: Facts and types; the file example also uses Ting source declarations.
A schema describes record fields. A classification explicitly states what kind of thing those values represent. A pump and a fan may have the same fields, but sharing fields does not make a fan a pump. Declare membership when your application has the evidence to do so.
A namespace is the dot-separated module name, such as equipment.types. It keeps names distinct: equipment.types.Pump is the fully qualified classification name. A module may contain schemas, classifications, rules, conclusions and fixed facts together. There is no separate classification file type.
Declare classifications in a .ting file
Save this text as UTF-8 at a path of your choice, for example Path.home() / "ting_project" / "editable" / "equipment_types.ting". Create the parent folders first. The complete example in Compiled libraries writes and compiles the files for you.
ting source 1
requires semantics 1
module equipment.types version "1.0.0" role library
requires feature classifications
exports:
schema Equipment
class Machine
class Pump
class CentrifugalPump
schemas:
schema Equipment:
device: text required
vibration: integer required
classifications:
class Machine applies_to Equipment
class Pump is Machine
class CentrifugalPump is Pumprequires feature classifications enables these declarations. applies_to Equipment identifies the record schema. is Pump declares a parent; class Hybrid is Pump, Fan declares multiple parents when they share the same record schema. Cycles, unknown parents, conflicting definitions and incompatible schemas are rejected.
exports: lists the declarations another application may select. Unlisted declarations are private, but Ting can include a private declaration when a selected public rule explicitly requires it. The header's role library makes compilation return a LibraryBuild; no additional classification-file flag is needed.
Use classifications through Python
from ting_reasoner import Classification, Engine, FactSchema, KnowledgeBase
Equipment = FactSchema("equipment.Equipment", device=str, vibration=int)
Machine = Classification("equipment.Machine", Equipment)
Pump = Classification("equipment.Pump", Equipment, parents=(Machine,))
knowledge = KnowledgeBase(classifications=(Pump,)) # Includes its parents.
engine = Engine(knowledge)
record = Equipment(device="pump_a", vibration=25)
engine.declare(record)
engine.run()
assert engine.facts == (record,) # Shape alone does not establish membership.
membership = Pump.classify(record) # Explicitly classify these exact values.
engine.declare(membership)
engine.run() # Derives Machine membership and the underlying Equipment record.
assert Pump.is_a(Machine)
assert Machine(device="pump_a", vibration=25) in engine.facts
assert len(engine.facts) == 3| Call | Meaning |
|---|---|
Classification(name, Record, parents=(Parent,)) | Create a named classification. name is a string; Record is a FactSchema. The parent tuple is optional. |
Pump(device="pump_a", vibration=25) | Return a membership Fact. It does not change an Engine until passed to engine.declare(). |
Pump.classify(record) | Return membership for a Fact using exactly the declared record schema. It copies its immutable values. |
Pump.pattern(vibration=gt(20)) | Match pump membership in a Python Rule. Add the classification to KnowledgeBase(classifications=(Pump,)). |
Pump.is_a(Machine) | Return whether the declared type is Machine or a descendant. This examines declarations; it does not classify a record. |
engine.classification("equipment.types.Pump") | Retrieve a classification from a loaded executable. Unknown names raise KeyError. |
Membership, inference and changing values
Membership belongs to exact record values. Classifying device "pump_a" with vibration 25 does not classify a later record with vibration 30. There is no implicit entity identifier or reference relationship. Classify the new values explicitly when updating input.
engine.run() derives parent memberships and the underlying record through ordinary deterministic rules. These steps consume the same firing and work budgets as other rules. Merely declaring a child does not immediately populate all parents. A stopped or exhausted run can therefore have incomplete inheritance; check its status and resume with an appropriate budget. Use engine.reset() to start a new case; named seeds are retained.
A source rule uses match Pump: in the same way as match Equipment:, followed by field constraints. A derived classification uses derive Pump:. Classification facts also work in named seeds and embedded tests. Generated inheritance rules use the reserved namespace ting.classification.
Compile and import selected knowledge
Before this section: Source imports, fixed knowledge and classifications.
Use compiled libraries when different applications need different parts of the same knowledge. You can keep several .tingir files in different folders, then select only the required public classifications, schemas, rules or fixed facts. A public export is a declaration made available to other modules. Selecting it also includes its declared dependencies: the other declarations it needs.
The header determines what is compiled
| Source header | Compilation and loading |
|---|---|
role program | compile_file() returns SourceBuild with .program. load_tingir() returns a fully verified CompactIR; pass it to Engine. |
role library | compile_file() returns LibraryBuild with .library. load_tingir() returns CompiledLibrary with a verified directory. Select exports through CompiledCatalog. |
role test | Analyze and run embedded tests through AuthoringBackend. This role does not produce a deployable compiled file. |
Both compiled kinds use the .tingir extension. A binary header identifies the kind automatically. A compiled library offers declarations for selection; an executable is a program ready to run. Pass linked.program to Engine after selection. A CompiledLibrary itself is not executable.
Make a rule's dependencies explicit
Save this source as maintenance.ting. It uses the equipment_types.ting source from Classifications and inheritance. Its import refers to the module declared inside equipment_types.ting; the Python mapping below supplies that file's actual path.
ting source 1
requires semantics 1
module equipment.maintenance version "1.0.0" role library
requires feature classifications
requires feature seed-facts
imports:
from equipment.types version "1.0.0":
class Pump
exports:
rule inspect_pump
rule record_pump
conclusion InspectionRequired
schemas:
schema Limit:
maximum: integer required
conclusions:
conclusion InspectionRequired:
device: text required
measured: integer required
conclusion PumpRecorded:
device: text required
knowledge:
seed vibration_limit: Limit:
maximum: 20
rules:
rule inspect_pump:
requires:
seed vibration_limit
when:
match Pump:
device: bind device
vibration: bind measured
match Limit:
maximum: bind maximum
test gt:
use measured
use maximum
then:
conclude InspectionRequired:
device: use device
measured: use measured
rule record_pump:
when:
match Pump:
device: bind device
then:
conclude PumpRecorded:
device: use device
tests:
test high_vibration:
given seed
given:
fact Pump:
device: "pump_a"
vibration: 25
expect:
conclusion InspectionRequired:
device: "pump_a"
measured: 25
conclusion PumpRecorded:
device: "pump_a"Selecting inspect_pump automatically includes the Pump type, its parents, its record schema and the typed conclusion. The rule's requires: block also includes the private vibration_limit seed. You can list rule helper_name there when a helper rule is essential. Naming a schema alone does not select every rule that might produce facts of that schema; declare those rule dependencies explicitly.
Complete example: compile libraries, select a rule and explain its result
This example is self-contained. It creates temporary folders, writes both sources, compiles them, selects the inspection rule and runs it. The temporary files are removed on exit. For files you want to keep, replace the temporary-directory setup with project_dir = Path.home() / "ting_project" and keep the remaining operations in your application.
from pathlib import Path
from tempfile import TemporaryDirectory
from ting_reasoner import (
CompiledCatalog, CompiledLibrary, Engine, LibraryError, SourceError,
compile_file, load_tingir, save_tingir,
)
types_source = """ting source 1
requires semantics 1
module equipment.types version "1.0.0" role library
requires feature classifications
exports:
schema Equipment
class Machine
class Pump
class CentrifugalPump
schemas:
schema Equipment:
device: text required
vibration: integer required
classifications:
class Machine applies_to Equipment
class Pump is Machine
class CentrifugalPump is Pump
"""
maintenance_source = """ting source 1
requires semantics 1
module equipment.maintenance version "1.0.0" role library
requires feature classifications
requires feature seed-facts
imports:
from equipment.types version "1.0.0":
class Pump
exports:
rule inspect_pump
rule record_pump
conclusion InspectionRequired
schemas:
schema Limit:
maximum: integer required
conclusions:
conclusion InspectionRequired:
device: text required
measured: integer required
conclusion PumpRecorded:
device: text required
knowledge:
seed vibration_limit: Limit:
maximum: 20
rules:
rule inspect_pump:
requires:
seed vibration_limit
when:
match Pump:
device: bind device
vibration: bind measured
match Limit:
maximum: bind maximum
test gt:
use measured
use maximum
then:
conclude InspectionRequired:
device: use device
measured: use measured
rule record_pump:
when:
match Pump:
device: bind device
then:
conclude PumpRecorded:
device: use device
tests:
test high_vibration:
given seed
given:
fact Pump:
device: "pump_a"
vibration: 25
expect:
conclusion InspectionRequired:
device: "pump_a"
measured: 25
conclusion PumpRecorded:
device: "pump_a"
"""
# This demonstration removes its temporary folders when it finishes.
# In your application use Path.home() / "ting_project", or your chosen folder.
with TemporaryDirectory() as temporary:
project_dir = Path(temporary)
source_dir = project_dir / "editable"
types_dir = project_dir / "compiled_types"
rules_dir = project_dir / "compiled_rules"
for folder in (source_dir, types_dir, rules_dir):
folder.mkdir(parents=True, exist_ok=True)
types_path = source_dir / "equipment_types.ting"
maintenance_path = source_dir / "maintenance.ting"
with types_path.open("x", encoding="utf-8") as file:
file.write(types_source)
with maintenance_path.open("x", encoding="utf-8") as file:
file.write(maintenance_source)
try:
types_build = compile_file(types_path, types_dir / "types.tingir")
rules_build = compile_file(
maintenance_path, rules_dir / "maintenance.tingir",
modules={"equipment.types": types_path}, # Editable source path.
)
except SourceError as error:
print(error.as_dict())
raise
assert types_build.kind == "library" # Determined by the .ting header.
opened = load_tingir(types_build.output)
assert isinstance(opened, CompiledLibrary)
print(opened.module, opened.version)
print([(item.kind, item.qualified_name) for item in opened.exports])
catalog = CompiledCatalog({
"equipment.types": types_build.output, # Compiled paths at runtime.
"equipment.maintenance": rules_build.output,
})
try:
linked = catalog.link(
classifications=("equipment.types.CentrifugalPump",),
rules=("equipment.maintenance.inspect_pump",),
)
except LibraryError as error:
print(error.code, error.message, error.hint, error.chain)
raise
# Required parents, schema, typed conclusion and vibration_limit are loaded.
# The unrelated record_pump business rule is not part of this program.
assert len(linked.knowledge_base.rules) == 1
Pump = linked.classification("equipment.types.CentrifugalPump")
engine = Engine(linked.program)
engine.declare(Pump(device="pump_a", vibration=25))
engine.run()
assert len(engine.conclusions) == 1
assert engine.conclusions[0].as_dict() == {"device": "pump_a", "measured": 25}
explanation = linked.explain(engine.snapshot(), engine.conclusions[0])
assert explanation.proofs
print(linked.usage.sections, linked.usage.section_bytes)
# Keep the selected executable if this application always uses these imports.
program_path = project_dir / "pump_inspection.tingir"
save_tingir(linked.program, program_path)
another_session = Engine(load_tingir(program_path))
assert not another_session.conclusions
# Directly requesting a private seed produces a structured selection error.
try:
catalog.link(seeds=("equipment.maintenance.vibration_limit",))
except LibraryError as error:
assert error.code == "TING-LIB-003"
assert error.chain
else:
raise AssertionError("Private exports must not be directly selectable")Choose persistent paths
from pathlib import Path
from ting_reasoner import CompiledCatalog
project_dir = Path.home() / "ting_project"
catalog = CompiledCatalog({
"equipment.types": project_dir / "compiled_types" / "types.tingir",
"equipment.maintenance": project_dir / "compiled_rules" / "maintenance.tingir",
})
linked = catalog.link(rules=("equipment.maintenance.inspect_pump",))The files must already exist at those paths. The temporary example above deletes its files when it finishes; use its persistent-folder variation before running this continuation. Follow the path instructions when choosing another location. Relative catalog paths are anchored to Path.cwd() when the catalog is created.
During compilation, modules={...} maps imported module names to editable .ting sources. During runtime selection, CompiledCatalog({...}) maps names to compiled .tingir libraries or already opened CompiledLibrary objects. Runtime selection does not need the original source files. Compiling new source rules against only a binary provider is not supported; retain its source for that operation.
Select exactly what your application needs
| Keyword in catalog.link() | Selection |
|---|---|
classifications=("equipment.types.Pump",) | Pump, its parents and record schema. Activates inheritance propagation; adds no business rules or seeds. |
rules=("equipment.maintenance.inspect_pump",) | The named rule, required types and typed conclusions, plus explicitly required helper rules and seeds. |
decisions=("module.Choice",) | A named request and its explicitly required rules and seeds; see Store and import named decisions. |
schemas=("module.Record",) | The named record definition. |
seeds=("module.reference_fact",) | The named public fixed fact and its type dependencies. |
conclusions=("module.Result",) | The typed result declaration; this alone emits no result. |
namespaces=("equipment.maintenance",) | Every public export in that module, including both business rules in this example, with required dependencies. |
Use tuples or lists of strings; the comma in ("module.Name",) creates a one-item tuple. Passing a bare string is an error. You may combine keywords. Duplicate selections are harmless, and selection order does not change the linked program. linked.imports lists the complete selected dependency closure as (module, kind, name) tuples. linked.classification() and linked.schema() require fully qualified names.
linked.program is reusable across independent Engines and with run_batch(). Link once when the application starts, then reuse that object for many cases. linked.usage reports the amount of knowledge read for that selection. Its counts are useful when comparing selections; they do not measure total memory or elapsed time.
Verification and compatibility
Opening a library verifies its bounded directory, identity and section descriptors. It does not inspect unread payloads. Linking verifies the selected payloads and their dependency closure before returning an executable. Call library.verify_all() to read and verify every payload explicitly; this checks that file, without resolving other libraries. An unselected damaged section may therefore remain unnoticed until selected or fully verified.
Dependencies pin both an exact module version and a semantic contract digest. A changed provider can require recompiling its consumers even when its version string was not changed. Source comments and file locations do not change that semantic contract. SHA-256 checks detect corruption and identity changes; they do not authenticate who created a file. No source parser, Python import, network lookup or folder scan runs while selecting compiled knowledge.
Selected source fragments retain declaration text and original file labels for linked.explain(), even if those source files are absent. These are direct proof explanations. Saving just linked.program stores the executable without this source map; retain the LinkedBuild or reload and link the original libraries when source explanations are required.
Handle errors and bound work
Catch SourceError around source compilation and load_tingir(). Catch LibraryError around catalog selection, CompiledLibrary.open() and verify_all(). LibraryError exposes code, message, hint, chain and as_dict(). The chain names the selected export and dependencies leading to the problem. Wrong Python argument types raise TypeError or ValueError.
| Code | Meaning and response |
|---|---|
TING-LIB-001 | Unreadable or non-regular file. Check the explicit path and access permissions. |
TING-LIB-002 | Malformed or corrupt content, including invalid compiled payloads. Rebuild from preserved source. |
TING-LIB-003 | Missing module mapping, missing export, wrong kind or directly selected private name. Check the name and exports. |
TING-LIB-004 | Wrong module, incompatible version/contract, or file changed after opening. Supply compatible files and reopen. |
TING-LIB-005 | Library size, selection, dependency or source-map limit exceeded. Select less knowledge or review explicit limits. |
TING-LIB-006 / 007 | Conflicting definitions or invalid linked semantics. Correct the declarations before creating an Engine. |
from ting_reasoner import CompiledCatalog, IRLimits, LibraryLimits
# Reuse the catalog paths from the preceding example.
catalog = CompiledCatalog({
"equipment.types": project_dir / "compiled_types" / "types.tingir",
"equipment.maintenance": project_dir / "compiled_rules" / "maintenance.tingir",
}, limits=LibraryLimits(max_selected_bytes=8 * 1024 * 1024, max_sections=100))
linked = catalog.link(rules=("equipment.maintenance.inspect_pump",),
ir_limits=IRLimits(max_rules=100))LibraryLimits bounds file, directory, section, build and selected byte sizes; module and section counts; dependency traversal and depth; and retained source nodes. IRLimits bounds each selected Compact-IR payload and the final executable. An IR policy supplied to load_tingir(limits=...) is retained for lazy payload verification; an explicit link(ir_limits=...) replaces it for that operation. ExecutionBudget separately bounds reasoning. Classification graphs have a hard maximum of 4,096 declarations and 64 levels; lower IRLimits may tighten these.
A failed link returns no executable and changes no existing Engine. Compilation validates and runs embedded tests before publishing a file; existing outputs require overwrite=True. Compile libraries independently to run each library's embedded tests. A consumer's build does not automatically execute the provider's tests.
Reuse a compiled program
Compile rules once, then reuse the returned CompactIR program object for independent cases. The program is immutable; each Engine has its own facts, handles and results.
| You start with | Obtain a reusable program |
|---|---|
Readable file at source_path | built = compile_file(source_path), then program = built.program. Keep built for source explanations. |
Readable text in source_text | built = compile_source(source_text), then program = built.program. No file is written. |
Compiled file at output_path | program = load_tingir(output_path). A program is fully verified here. A library file returns CompiledLibrary; select exports through CompiledCatalog to obtain a program. |
Python KnowledgeBase called knowledge | program = CompactIR(knowledge.compile()). Import CompactIR from ting_reasoner. |
The first two rows assume role program. For role library, use CompiledCatalog and reuse linked.program. The path variables are introduced in Compile readable source. A program contains rules and fixed knowledge; a session adds observations for one case. Reusing program avoids repeating its verification when the bytes and verification limits are unchanged. Passing raw bytes or a knowledge base to each new Engine repeats that work.
from ting_reasoner import CompactIR, Engine, FactSchema, KnowledgeBase, Rule, bind, emit, use
Reading = FactSchema("reading", device=str, value=int)
rule = Rule("record", (Reading.pattern(device=bind("device"), value=bind("value")),),
(emit("recorded", device=use("device"), value=use("value")),))
knowledge = KnowledgeBase((Reading,), (rule,))
program = CompactIR(knowledge.compile()) # Compile and verify once here.
first = Engine(program)
second = Engine(program) # A separate session using the same program.
first.declare(Reading(device="pump_a", value=25))
second.declare(Reading(device="pump_b", value=10))
first.run()
second.run()
assert first.conclusions[0]["device"] == "pump_a"
assert second.conclusions[0]["device"] == "pump_b"
first.reset()
assert not first.conclusions
assert second.conclusions[0]["device"] == "pump_b"first and second are two Python variables holding separate Engine objects. Resetting first clears its session facts and results, while retaining any compiled fixed knowledge. It does not affect second. Fact handles belong to their own sessions; never transfer them between Engines.
For consecutive cases in one session, engine.reset(*facts) can also reuse its program and replace session inputs. The * expands a collection of facts into individual arguments. Save a snapshot before reset if you need to retain the old result.
For many independent cases, pass the same program to batch execution. Use Choose an execution mode to select between a reusable Engine, separate Engines and workers.
Loading with a more permissive IRLimits policy does not raise the Engine’s default limits. Keep runnable programs within those defaults; reduce the selected knowledge if Engine construction rejects an oversized program. See IRLimits for the limit fields.
Explain a conclusion
Before this section: Run and inspect; source locations also require a build from readable source.
Keep the exact conclusion returned by the engine and pass it to engine.explain(conclusion). The result contains its proof edges. You can also inspect a fact handle, a conclusion name, or all recorded provenance. A string selects a conclusion name, not a rule name. A snapshot exposes the same explanation interface after the live session changes. The short continuation below uses engine from Your first reasoner, after its successful run.
conclusion = engine.conclusions[0]
proofs = engine.explain(conclusion)
for proof in proofs:
print(proof)
snapshot = engine.snapshot()
assert snapshot.explain(conclusion) == proofsProofs identify the rule, bindings and fact support. Multiple justifications may support the same logical output. Match and optimization metrics describe physical work; they are not confidence scores or probabilistic evidence.
Find the source behind a result
When you compile readable source, retain the returned SourceBuild object, called built in these examples. built.explain(engine, conclusion) adds source locations and fact values to a recorded proof. It returns a SourceExplanation and does not run inference or modify the session.
A proof edge records which rule and direct fact supports produced an output. A binding is a value captured by a rule. The explanation connects this runtime evidence to the source declaration. It is structured data that your application can inspect or display.
| Expression | What you receive |
|---|---|
built.explain(engine, conclusion) | The recorded direct proofs for that exact conclusion, taken from one consistent snapshot of the Engine. |
built.explain(snapshot, conclusion) | The same source context for a saved EngineSnapshot. It remains usable after the live Engine changes. |
built.explain(engine, handle) | The selected fact and any recorded derivation proofs. An ordinary input fact or fixed seed can have no derivation proofs. |
built.explain(engine) | All recorded proof edges, subject to ExplanationLimits. It does not enumerate unproved input facts. |
explanation.proofs | A tuple of SourceProof objects. Each has rule, provenance, statements and supports. |
proof.rule.span | The rule’s source URI, source digest, line and columns. Lines and columns start at one. |
proof.provenance.bindings | Name/value pairs actually recorded by the runtime for this proof. |
proof.statements | Located condition and action definitions for the entire rule, with role, text, node_id and span. |
proof.supports | Direct supporting FactSupport records: handle, fact values, schema origin and any named seed origins. |
explanation.as_dict() | A detached JSON-compatible dictionary. Bytes use typed hexadecimal records; arrays also include dtype and shape, with little-endian data. |
Complete example, including a saved snapshot
This example includes its readable source and needs no files. The URI memory:orchard is a label for that text. When using compile_file(source_path), the source map instead retains the file location supplied by the file reader.
import json
from ting_reasoner import Engine, ExplanationError, ExplanationLimits, SourceSnapshot, compile_source
# This complete example keeps source in memory; it does not read or write a file.
source = """# Edit this file, then call compile_file("orchard.ting").
ting source 1
requires semantics 1
module orchard version "1.0.0" role program
schemas:
schema Fruit:
name: text required
sweet: boolean required
conclusions:
conclusion SweetFruit:
name: text required
rules:
rule recognize_sweet_fruit:
when:
match Fruit:
name: bind name
sweet: true
then:
conclude SweetFruit:
name: use name
tests:
test apple_is_sweet:
given:
fact Fruit:
name: "apple"
sweet: true
fact Fruit:
name: "lemon"
sweet: false
expect:
conclusion SweetFruit:
name: "apple"
firings: 1
"""
built = compile_source(SourceSnapshot(source, uri="memory:orchard"))
Fruit = built.schema("Fruit")
engine = Engine(built.program)
engine.declare(Fruit(name="pear", sweet=True))
run = engine.run()
assert run.status.value == "quiescent"
snapshot = engine.snapshot()
for conclusion in snapshot.conclusions:
explanation = built.explain(snapshot, conclusion,
limits=ExplanationLimits(max_proofs=20, max_supports=100))
for proof in explanation.proofs:
print(proof.rule.name, proof.rule.span.uri, proof.rule.span.line)
for support in proof.supports:
print(support.handle.id, support.fact.as_dict())
print(json.dumps(explanation.as_dict(), indent=2))
# A saved snapshot remains usable after the live session changes.
conclusion = snapshot.conclusions[0] # The example's pear produces one result.
engine.reset()
assert built.explain(snapshot, conclusion).proofs
try:
built.explain(engine, conclusion)
except ExplanationError as error:
assert error.code == "TING-EXPLAIN-002" # Not present in the current session.
print(error.message, error.hint)
else:
raise AssertionError("A missing conclusion must be reported")The nested loops first visit conclusions, then their proofs, then supporting facts. json.dumps(..., indent=2) converts the dictionary to formatted JSON text for display. snapshot.epoch identifies the captured state within that session. Epochs and numeric handles do not identify a session globally.
Fixed knowledge and exact statement locations
For a program containing named seeds, support.seeds identifies their declarations, including imported providers. An empty tuple means the support has no fixed-knowledge names. Equal seed aliases can share one fact while retaining several names. The continuation below requires built from Include fixed knowledge, rather than the orchard build above. It locates the threshold field in that program:
# With built from the equipment inspection example:
origin = built.origin_for("equipment.limits.Threshold.maximum", kind="schema-field")
statement = built.analysis.node(origin.node_id)
print(statement.text, origin.span.uri, origin.span.line)Handle explanation errors
| Code | Meaning and recovery |
|---|---|
TING-EXPLAIN-001 | The source build and session have different compiled programs, or the snapshot has no program identity. Use the matching build and a snapshot produced by its Engine. |
TING-EXPLAIN-002 | A requested conclusion or fact is absent. Use the saved snapshot from which you obtained that subject. |
TING-EXPLAIN-003 | An explanation limit was exceeded. Select one subject or deliberately raise the named limit; no partial explanation is returned. |
TING-EXPLAIN-004 | A required source declaration is missing. Retain the complete build and imported source analysis. |
Catch ExplanationError and inspect error.code, error.message, error.hint or error.as_dict(). Invalid argument types raise TypeError; invalid limit values raise ValueError. Limits default to 100,000 inspected snapshot records, 1,000 selected proofs, 10,000 direct support references and 10,000 statement references. Repeated references count toward the output limits. These limits do not bound snapshot creation time or JSON byte size.
Interpret the evidence correctly
Check RunResult.status to know whether inference finished. Explanations describe recorded direct evidence; they do not prove that every possible justification was found, recursively expand a support graph, or provide a confidence score. The rule’s source statements include all branches and actions. Their presence does not identify which OR branch matched, which action produced a particular output, or provide an absence witness for a negated condition.
The compiled program digest must match. Source comments and line breaks can change without changing compiled bytes, so equivalent source may supply a different map. explanation.sources records the exact documents used for that map. Digests identify content; they are not signatures. Snapshots and builds are trusted in-process objects, not independently authenticated evidence.
An executable .tingir program does not retain the readable source map. Keep built in your application, or preserve the .ting files and rebuild them to recover source context. If you only retain the executable, engine.explain() still provides rule proofs. Compiled libraries can retain selected source fragments for linked.explain(); saving just linked.program does not preserve that source map.
Keep valid results when inputs change
Before this section: Input changes and explanations.
A result can have several valid reasons. A pump may need inspection because of excessive temperature and independently because of excessive vibration. Clearing one alarm should preserve the result while the other alarm remains.
Pass maintenance=MaintenancePolicy() when creating an Engine to retain grounded results across input changes. A result is grounded when a chain of rule applications reaches current input facts, fixed seeds, or a rule whose conditions need no facts. Circular reasoning alone cannot preserve a result.
Define the rules in readable source
This complete .ting program derives one inspection requirement per device. Several alarm facts can support the same requirement. The final rule emits the application's structured inspection result.
ting source 1
requires semantics 1
module inspection version "1.0.0" role program
schemas:
schema Alarm:
device: text required
cause: text required
schema InspectionNeeded:
device: text required
conclusions:
conclusion Inspect:
device: text required
rules:
rule identify_need:
when:
match Alarm:
device: bind device
then:
derive InspectionNeeded:
device: use device
rule request_inspection:
when:
match InspectionNeeded:
device: bind device
then:
conclude Inspect:
device: use device
tests:
test independent_causes:
given:
fact Alarm:
device: "pump_a"
cause: "temperature"
fact Alarm:
device: "pump_a"
cause: "vibration"
expect:
conclusion Inspect:
device: "pump_a"
firings: 3Complete Python example
This example includes the source string, so it can run without creating files. compile_source() returns the compiled program and the source information used by built.explain(). The two values returned by engine.declare() are handles identifying the input facts to remove later.
from ting_reasoner import Engine, MaintenancePolicy, compile_source
source = """ting source 1
requires semantics 1
module inspection version "1.0.0" role program
schemas:
schema Alarm:
device: text required
cause: text required
schema InspectionNeeded:
device: text required
conclusions:
conclusion Inspect:
device: text required
rules:
rule identify_need:
when:
match Alarm:
device: bind device
then:
derive InspectionNeeded:
device: use device
rule request_inspection:
when:
match InspectionNeeded:
device: bind device
then:
conclude Inspect:
device: use device
tests:
test independent_causes:
given:
fact Alarm:
device: "pump_a"
cause: "temperature"
fact Alarm:
device: "pump_a"
cause: "vibration"
expect:
conclusion Inspect:
device: "pump_a"
firings: 3
"""
built = compile_source(source) # Validates the rules and their embedded test.
Alarm = built.schema("Alarm")
engine = Engine(built.program, maintenance=MaintenancePolicy())
# Two facts independently justify the same inspection.
heat, vibration = engine.declare(
Alarm(device="pump_a", cause="temperature"),
Alarm(device="pump_a", cause="vibration"),
)
engine.run()
assert len(engine.conclusions) == 1
before = engine.snapshot() # Keep this if you need the earlier evidence.
previous_conclusion = engine.conclusions[0]
engine.retract(heat)
report = engine.maintenance_report
assert report.mode == "retained"
assert report.retained_conclusions == 1
assert len(engine.conclusions) == 1 # The vibration cause still supports it.
for change in report.changes:
print(change.status, change.previous_supports, change.remaining_supports)
# run() checks whether more consequences are now possible.
result = engine.run()
assert result.status.value == "quiescent"
assert result.firings == 0 # Existing valid derivations did not fire again.
assert built.explain(engine, engine.conclusions[0]).proofs
engine.retract(vibration)
assert engine.conclusions == () # The final grounding cause disappeared.
assert engine.maintenance_report.withdrawn_conclusions == 1
engine.run()
# A saved snapshot still explains the earlier result after withdrawal.
assert built.explain(before, previous_conclusion).proofsAfter the first removal, the derived inspection requirement changes from two direct supports to one. Its conclusion still has one direct support: the inspection requirement itself. Direct support counts describe proof edges; they are not confidence percentages and do not establish that observations are independent.
Use a source file in your chosen folder
from pathlib import Path
from ting_reasoner import Engine, MaintenancePolicy, compile_file, load_tingir
project_dir = Path.home() / "ting_examples"
source_dir = project_dir / "editable"
compiled_dir = project_dir / "compiled"
source_dir.mkdir(parents=True, exist_ok=True)
compiled_dir.mkdir(parents=True, exist_ok=True)
source_path = source_dir / "inspection_supports.ting"
compiled_path = compiled_dir / "inspection_supports.tingir"
print("Save the complete .ting source above at:", source_path)
# After saving that text as UTF-8 in your editor:
built = compile_file(source_path, compiled_path)
engine = Engine(load_tingir(compiled_path), maintenance=MaintenancePolicy())
Alarm = built.schema("Alarm")Save the complete source above at the printed path before running the compilation line. Use the file-path instructions for other folders, and overwrite=True only to replace an existing compiled file. See Handle build errors for compilation failures.
Support maintenance is a Python session option. The rules remain ordinary portable .ting knowledge, and the same compiled program can be used with or without this option. For a selectively linked library, use Engine(linked.program, maintenance=MaintenancePolicy()). Inheritance rules for classifications are eligible too.
What happens during an update?
| Operation | Behavior with support retention |
|---|---|
engine.declare(fact) | Keeps previously grounded results. It adds the input immediately; call run() to discover new rule consequences. |
engine.retract(handle) | Removes that input and withdraws recorded derivations that no longer have a grounded support. Alternatives already established can preserve the same result. |
engine.replace(handle, fact) | Checks support against the new fact values. Reusing a numeric handle does not preserve evidence from its previous values. |
with engine.transaction() as tx: | Applies retention to the complete final input collection at commit. Intermediate edits are not published. |
engine.run() | Finds new consequences and additional supports. Always check RunResult.status before treating the state as complete. |
engine.reset() | Clears inferred state and starts a new case, retaining named seeds. Discard previous handles; reset may reuse their numeric IDs. |
Retention uses only rule applications already recorded. If you remove an old cause and insert a different cause in the same transaction, an old result may disappear until run() proves the new cause sufficient. A budget-limited prior run may also have left alternative supports undiscovered.
A retained derived fact keeps its negative handle while it remains a derived row. Explicitly declaring that value makes it a base fact with a positive handle, and supporting references are updated. Removing a base assertion that is still justified by rules creates a derived handle. Reacquire handles when facts change roles.
When a conclusion's original representative proof disappears, Ting chooses a remaining proof and updates its rule and fact_ids. Reacquire the current Conclusion from engine.conclusions. Its name and values can remain the same while that evidence changes. Save an EngineSnapshot beforehand to inspect earlier evidence later.
Read the update report
engine.maintenance_report is an immutable report for the latest committed input update. Duplicate declarations, missing-handle retractions, unchanged replacements and failed updates leave the previous report intact. With maintenance disabled it is None.
| Report field | Meaning |
|---|---|
mode / reason | retained / positive_rules, recompute / unsupported_conditions, recompute / limit:NAME, or reset / state_replaced. Initializing fixed seeds also publishes a reset state. |
retained_facts / withdrawn_facts | Counts of previously derived fact values that remain present or have been withdrawn. |
retained_conclusions / withdrawn_conclusions | Counts of previous results preserved or withdrawn by this update. |
retained_proofs / removed_proofs | Numbers of recorded direct proof edges kept or removed. |
work | Logical graph-work units used during maintenance. These are separate from the next run’s ExecutionBudget. |
changes | SupportChange records for changed direct support counts or fact handles. Each contains the previous subject, status, previous_supports, remaining_supports, previous_handle and current_handle. Conclusion handles are None. |
changes_truncated | True when the number of changes exceeded max_changes. Summary counts remain complete, and truncation does not change reasoning. |
The report describes the committed update, before the next run. A recompute or reset report contains summary counts and no individual change records. If a fact loses its rule proofs but remains explicitly asserted as a base fact, its status is retained with zero remaining rule supports.
Supported conditions and recomputation
Retention applies when every loaded rule branch uses positive match conditions and optional test comparisons. Positive AND and OR branches, repeated patterns, recursion, fixed seeds and classification inheritance are supported. Cycles without grounding cannot keep one another alive after their last grounding reason disappears.
If the loaded program contains any negated condition, exists or for_all, input updates use the normal recomputation path: the new inputs are committed, inferred state is cleared, and run() rebuilds it. The report identifies this as unsupported_conditions. This applies to the whole loaded program. Omitting maintenance or setting enabled=False uses the usual recomputation behavior throughout.
Set limits and handle an update failure
from ting_reasoner import (
Engine, FactSchema, KnowledgeBase, MaintenanceLimitError,
MaintenancePolicy, Rule, bind, emit, use,
)
Reading = FactSchema("reading", sensor=str)
knowledge = KnowledgeBase(rules=(
Rule("record", (Reading.pattern(sensor=bind("sensor")),),
(emit("observed", sensor=use("sensor")),)),
))
# Zero proofs is deliberately tiny, to demonstrate an update limit error.
policy = MaintenancePolicy(max_proofs=0, on_limit="raise")
engine = Engine(knowledge, maintenance=policy)
handle = engine.declare(Reading(sensor="north"))
engine.run()
before = engine.snapshot()
try:
engine.retract(handle)
except MaintenanceLimitError as error:
assert error.code == "TING-MAINT-001"
assert error.limit == "max_proofs"
assert engine.snapshot() == before # The input and prior result survive.
print(error.as_dict())
else:
raise AssertionError("Expected the deliberately low limit to reject this update")
# The default on_limit="recompute" commits the base update and clears inferred
# state when retention exceeds a limit. run() then rebuilds the consequences.
fallback = Engine(knowledge, maintenance=MaintenancePolicy(max_proofs=0))
first = fallback.declare(Reading(sensor="north"))
fallback.run()
fallback.declare(Reading(sensor="south"))
assert fallback.maintenance_report.mode == "recompute"
assert fallback.maintenance_report.reason == "limit:max_proofs"
assert not fallback.conclusions
fallback.run()
assert len(fallback.conclusions) == 2| MaintenancePolicy option | Default and scope |
|---|---|
max_facts | 100,000: old base and derived facts plus proposed base facts. Unchanged base facts count in both the old and proposed collections. |
max_proofs | 200,000 existing direct proof records. |
max_supports | 1,000,000 fact references in those proof records. |
max_work | 4,000,000 counted maintenance operations. Compare this limit with engine.maintenance_report.work. |
max_changes | 1,000 individual change records. Set zero for summary counts only. |
on_limit | "recompute" by default. "raise" rejects an over-limit update with MaintenanceLimitError and preserves the previous Engine state. |
Limits are non-negative exact integers; Boolean values are rejected. They bound logical work and records, not exact Python heap bytes or elapsed time. Wrong argument types raise TypeError or ValueError. MaintenanceLimitError exposes code, limit, maximum, used and a JSON-safe as_dict(). Catch it around declare, retract, replace or transaction commit. A rejected transaction requires a new transaction to retry.
With on_limit="recompute", the input update succeeds using the established recomputation behavior. Check the report when your application needs to know which path ran. on_limit="raise" concerns resource limits; programs with unsupported conditions still use recomputation. Allocation or other staging failures propagate before publication, leaving the prior state usable.
Run after changing inputs
Keep the same Engine while updating facts for one case. Group related changes in a transaction, then call engine.run() after the transaction closes. Check result.status.value == "quiescent" before treating inference as complete. For "firing_limit", raise the firing allowance; for "budget_exhausted", inspect result.exhausted_budget. See Work limits for retry behavior.
RunResult counts work performed by that call. A call may report zero new firings while valid conclusions remain in engine.conclusions. Read engine.maintenance_report to see what the input update retained or withdrew. Use run(budget=ExecutionBudget(...)) when your application needs explicit limits on candidate rows, constraint checks, activations and firings.
An input update that changes the committed inputs clears engine.trace; current proof records still explain retained results. Save an EngineSnapshot before an update if you need to inspect the previous state. Reacquire conclusions and fact handles after an update, especially when a fact changes between an asserted input and a derived result.
Use run_batch() for independent cases. Each case starts a fresh session. To retain results as the inputs of one case change, keep its Engine and enable maintenance=MaintenancePolicy().
Make and inspect a decision
Before this section: Run and inspect and Write Ting rules.
Use decide() when your application needs a structured answer about a finite set of options. You supply a compiled program, a DecisionRequest, and the case's input facts. Ting evaluates the rules in a fresh isolated session, checks the evidence for every required condition, and returns a DecisionResult.
The selection policy is unique-feasible-v1: select an option only when it is the single feasible candidate and every other candidate is conclusively infeasible. All requested conditions are mandatory. Two feasible options remain a tie; their order does not choose a winner. The program supplies evidence through ordinary portable conclusion declarations.
Define the evidence in a .ting program
This example chooses equipment with capacity of at least 10 units and a passed inspection. The complete Python example below includes the source text directly and needs no source file. If you prefer an editable file, first choose its full path:
from pathlib import Path
source_path = Path.home() / "ting_examples" / "editable" / "equipment_decision.ting"
source_path.parent.mkdir(parents=True, exist_ok=True)
print("Save the following UTF-8 text at:", source_path)Replace the folder components with your preferred location if needed. This creates the folder and prints the filename; it does not write the source text. Use your editor’s Save As command to save the following complete text at that printed path.
ting source 1
requires semantics 1
module equipment.choice version "1.0.0" role program
schemas:
schema Capacity:
device: text required
available: integer required
schema Inspection:
device: text required
passed: boolean required
conclusions:
conclusion Evidence:
goal: text required
candidate: text required
requirement: text required
satisfied: boolean required
reason: text required
rules:
rule capacity_sufficient:
when:
match Capacity:
device: bind device
available: ge 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
rule capacity_insufficient:
when:
match Capacity:
device: bind device
available: lt 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: false
reason: "CAPACITY_BELOW_10"
rule inspection_passed:
when:
match Inspection:
device: bind device
passed: eq true
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: true
reason: "INSPECTION_PASSED"
rule inspection_failed:
when:
match Inspection:
device: bind device
passed: eq false
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: false
reason: "INSPECTION_FAILED"
tests:
test sufficient_capacity:
given:
fact Capacity:
device: "pump_a"
available: 15
expect:
conclusion Evidence:
goal: "choose_equipment"
candidate: "pump_a"
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
firings: 1The five fields of Evidence form the decision evidence contract. A conclusion with satisfied: true supports one requirement; false explicitly opposes it. An absent conclusion does neither. reason is an author-written code that your application can display or map to its own labels.
| Evidence field | Meaning |
|---|---|
goal | The exact DecisionRequest.goal string. |
candidate | One exact name from request.candidates. |
requirement | One exact name from request.requirements. |
satisfied | An exact Boolean: true or false in source, True or False in Python. Integers 1 and 0 are rejected. |
reason | A nonempty author-defined reason code. The four text fields accept valid Unicode, at most 256 UTF-8 bytes each, with no control characters. |
The designated evidence conclusion must have exactly these fields. Other conclusion names are ignored. Well-formed evidence for a different goal is ignored. Evidence for this goal with an unknown candidate or requirement raises DecisionError, making spelling mistakes visible. Every conclusion on the designated channel is checked for the correct shape, even if it names a different goal.
Complete Python example
This example runs without creating a source file. compile_source() validates the declarations and their embedded test; built.schema() retrieves the fact constructors. A tuple such as ("capacity", "inspection") lists the names in order. A one-item tuple needs a trailing comma, for example ("capacity",).
from ting_reasoner import (
DecisionError, DecisionLimits, DecisionOutcome, DecisionRequest,
DecisionStatus, compile_source, decide,
)
source = """ting source 1
requires semantics 1
module equipment.choice version "1.0.0" role program
schemas:
schema Capacity:
device: text required
available: integer required
schema Inspection:
device: text required
passed: boolean required
conclusions:
conclusion Evidence:
goal: text required
candidate: text required
requirement: text required
satisfied: boolean required
reason: text required
rules:
rule capacity_sufficient:
when:
match Capacity:
device: bind device
available: ge 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
rule capacity_insufficient:
when:
match Capacity:
device: bind device
available: lt 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: false
reason: "CAPACITY_BELOW_10"
rule inspection_passed:
when:
match Inspection:
device: bind device
passed: eq true
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: true
reason: "INSPECTION_PASSED"
rule inspection_failed:
when:
match Inspection:
device: bind device
passed: eq false
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: false
reason: "INSPECTION_FAILED"
tests:
test sufficient_capacity:
given:
fact Capacity:
device: "pump_a"
available: 15
expect:
conclusion Evidence:
goal: "choose_equipment"
candidate: "pump_a"
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
firings: 1
"""
built = compile_source(source)
Capacity = built.schema("Capacity")
Inspection = built.schema("Inspection")
request = DecisionRequest(
goal="choose_equipment",
candidates=("pump_a", "pump_b"),
requirements=("capacity", "inspection"),
evidence_name="equipment.choice.Evidence",
closed=True,
)
facts = (
Capacity(device="pump_a", available=15),
Inspection(device="pump_a", passed=True),
Capacity(device="pump_b", available=5),
)
result = decide(built.program, request, facts=facts)
assert result.status is DecisionStatus.COMPLETE
assert result.outcome is DecisionOutcome.SELECTED
assert result.selected_option == "pump_a"
print(result.selected_option)
# Inspect the rule evidence behind pump_a's capacity assessment.
capacity_check = result.candidates[0].constraints[0]
evidence = capacity_check.support[0]
print(evidence["reason"]) # CAPACITY_AT_LEAST_10
explanation = built.explain(result.snapshot, evidence)
assert explanation.proofs
# Export the structured summary for your application.
import json
print(json.dumps(result.as_dict(), indent=2))The result selects pump_a. Pump_b is excluded by its known capacity failure, so its missing inspection cannot rescue it or block pump_a. Every condition remains visible in the candidate records, including that unknown inspection.
Understand the request
| Argument | How to choose it |
|---|---|
goal | Name the question your evidence addresses, such as choose_equipment. |
candidates | List distinct candidate names. Their order controls output presentation, never selection. |
requirements | List distinct hard requirements applying to every candidate. These names must match the emitted evidence. |
evidence_name | Use the exact qualified conclusion name. Module equipment.choice and conclusion Evidence produce equipment.choice.Evidence. A Python emit("evidence", ...) uses evidence directly. |
closed | Supply True only if this is the entire candidate universe for the request. This is your application’s assertion; Ting cannot discover omitted real-world options. False prevents a final selection or no-feasible-option claim. |
logical_time | Optional nonnegative integer context, default 0. It is recorded, not read from the wall clock. It does not expire evidence or inject a fact; time-dependent rules need explicit time facts supplied by the caller. |
The request is immutable and accepts at most 4,096 candidates and 256 requirements. An empty closed candidate list yields NO_FEASIBLE_OPTION within that empty domain. With no requirements, each listed candidate is feasible by definition. Use nonempty requirements whenever your application intends to impose conditions.
Your .ting rules contain the domain knowledge and emit evidence. You can construct the request in Python for each case, or store a named decision declaration in the .ting source and retrieve it from the compiled file. Both use the same evidence and outcome contract. Use Store and import named decisions when the request should travel with the saved program.
Read the result before acting
| Field | Meaning |
|---|---|
status | COMPLETE means inference reached quiescence and assessment finished. BUDGET_EXHAUSTED means the requested computation did not finish. |
outcome | The decision established by this evaluation; see the outcome table below. |
selected_option | A candidate name only for SELECTED; otherwise None. |
feasible_options | Candidates proved feasible after a complete evaluation, in request order. An open domain or unresolved competing candidate can still prevent a selection. |
candidates | CandidateAssessment records with candidate, feasibility and ordered ConstraintAssessment records. Each condition includes requirement, state, support and opposition. |
reason_codes | Machine-readable decision reasons, such as MISSING_EVIDENCE, CONFLICTING_EVIDENCE and OPEN_CANDIDATE_DOMAIN. More than one blocker can be present. |
snapshot | The exact immutable inference snapshot. Use it with built.explain(snapshot, evidence) or snapshot.explain(evidence). |
usage / exhausted_limit | Assessment work and the exhausted boundary, if any. Inference counters are separately available through result.session.run. |
| Outcome | Interpretation |
|---|---|
SELECTED | Exactly one candidate is feasible in the closed domain, and every alternative is infeasible. |
NO_FEASIBLE_OPTION | Every candidate in the closed domain has a decisive hard failure, or the closed domain is empty. |
NEEDS_DATA | An otherwise viable candidate lacks evidence, or the candidate domain remains open. |
CONFLICTED | A candidate that has not already been excluded has both support and opposition for a requirement. Opposing evidence is retained. |
MULTIPLE_VALID_OPTIONS | Two or more options are feasible, all other options are excluded, and no tie-breaking policy is defined. |
UNDETERMINED | Inference or assessment stopped at a resource boundary. No candidate assessments or feasible-option claims are published. |
For each requirement, SUPPORTED means positive evidence only, REFUTED means negative evidence only, UNKNOWN means neither, and CONFLICTED means both. A candidate with any REFUTED hard requirement is INFEASIBLE. Otherwise a conflict takes precedence over missing evidence; all other candidates are UNKNOWN or FEASIBLE. A conflict alone does not authorize exclusion. No support count becomes a confidence percentage, and duplicate derivations do not constitute voting.
The overall outcome reports CONFLICTED before NEEDS_DATA when both problems affect viable candidates; reason_codes and condition records preserve the additional blockers. Missing or conflicting evidence on an independently excluded candidate does not block the remaining choice.
Handle missing data, conflicts, ties and incomplete runs
from ting_reasoner import (
DecisionError, DecisionLimits, DecisionOutcome, DecisionRequest,
DecisionStatus, compile_source, decide,
)
source = """ting source 1
requires semantics 1
module equipment.choice version "1.0.0" role program
schemas:
schema Capacity:
device: text required
available: integer required
schema Inspection:
device: text required
passed: boolean required
conclusions:
conclusion Evidence:
goal: text required
candidate: text required
requirement: text required
satisfied: boolean required
reason: text required
rules:
rule capacity_sufficient:
when:
match Capacity:
device: bind device
available: ge 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
rule capacity_insufficient:
when:
match Capacity:
device: bind device
available: lt 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: false
reason: "CAPACITY_BELOW_10"
rule inspection_passed:
when:
match Inspection:
device: bind device
passed: eq true
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: true
reason: "INSPECTION_PASSED"
rule inspection_failed:
when:
match Inspection:
device: bind device
passed: eq false
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: false
reason: "INSPECTION_FAILED"
tests:
test sufficient_capacity:
given:
fact Capacity:
device: "pump_a"
available: 15
expect:
conclusion Evidence:
goal: "choose_equipment"
candidate: "pump_a"
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
firings: 1
"""
built = compile_source(source)
Capacity = built.schema("Capacity")
Inspection = built.schema("Inspection")
request = DecisionRequest(
goal="choose_equipment",
candidates=("pump_a", "pump_b"),
requirements=("capacity", "inspection"),
evidence_name="equipment.choice.Evidence",
closed=True,
)
capacity_a = Capacity(device="pump_a", available=15)
capacity_b = Capacity(device="pump_b", available=5)
# Missing inspection is unknown: it is not an automatic failure.
missing = decide(built.program, request, facts=(capacity_a, capacity_b))
assert missing.status is DecisionStatus.COMPLETE
assert missing.outcome is DecisionOutcome.NEEDS_DATA
assert missing.selected_option is None
# Opposing reports for the same requirement remain visible.
conflict = decide(built.program, request, facts=(
capacity_a, capacity_b,
Inspection(device="pump_a", passed=True),
Inspection(device="pump_a", passed=False),
))
assert conflict.outcome is DecisionOutcome.CONFLICTED
check = conflict.candidates[0].constraints[1]
assert check.support and check.opposition
# If both pumps qualify, Ting preserves the tie.
tie = decide(built.program, request, facts=(
capacity_a, Capacity(device="pump_b", available=12),
Inspection(device="pump_a", passed=True),
Inspection(device="pump_b", passed=True),
))
assert tie.outcome is DecisionOutcome.MULTIPLE_VALID_OPTIONS
assert tie.feasible_options == ("pump_a", "pump_b")
assert tie.selected_option is None
# A firing limit is incomplete computation, not missing information.
limited = decide(built.program, request, facts=(capacity_a, capacity_b), max_firings=0)
assert limited.status is DecisionStatus.BUDGET_EXHAUSTED
assert limited.outcome is DecisionOutcome.UNDETERMINED
assert limited.candidates == ()
print(limited.exhausted_limit)COMPLETE with NEEDS_DATA is valid: Ting finished evaluating the information supplied, and determined that it is insufficient. A computation limit instead returns BUDGET_EXHAUSTED with UNDETERMINED. Never infer an application action from selected_option alone without handling the other outcomes your workflow permits.
Choose source and compiled file paths
from pathlib import Path
from ting_reasoner import DecisionRequest, compile_file, decide, load_tingir
project_dir = Path.home() / "ting_examples"
source_dir = project_dir / "editable"
compiled_dir = project_dir / "compiled"
source_dir.mkdir(parents=True, exist_ok=True)
compiled_dir.mkdir(parents=True, exist_ok=True)
source_path = source_dir / "equipment_decision.ting"
compiled_path = compiled_dir / "equipment_decision.tingir"
print("Save the complete .ting text above at:", source_path)
# After saving that UTF-8 file in your editor:
built = compile_file(source_path, compiled_path)
program = load_tingir(compiled_path)
request = DecisionRequest("choose_equipment", ("pump_a", "pump_b"),
("capacity", "inspection"), "equipment.choice.Evidence", closed=True)
Capacity, Inspection = built.schema("Capacity"), built.schema("Inspection")
result = decide(program, request, facts=(
Capacity(device="pump_a", available=15),
Inspection(device="pump_a", passed=True),
Capacity(device="pump_b", available=5),
))
print(result.outcome.value, result.selected_option)This continuation requires the evidence source saved earlier in this section. Use the file-path instructions to choose another folder, and overwrite=True only when replacing an existing compiled file. Keep the build result for schema helpers and source explanations.
Set limits and handle errors
from ting_reasoner import (
DecisionError, DecisionLimits, DecisionOutcome, DecisionRequest,
DecisionStatus, compile_source, decide,
)
source = """ting source 1
requires semantics 1
module equipment.choice version "1.0.0" role program
schemas:
schema Capacity:
device: text required
available: integer required
schema Inspection:
device: text required
passed: boolean required
conclusions:
conclusion Evidence:
goal: text required
candidate: text required
requirement: text required
satisfied: boolean required
reason: text required
rules:
rule capacity_sufficient:
when:
match Capacity:
device: bind device
available: ge 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
rule capacity_insufficient:
when:
match Capacity:
device: bind device
available: lt 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: false
reason: "CAPACITY_BELOW_10"
rule inspection_passed:
when:
match Inspection:
device: bind device
passed: eq true
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: true
reason: "INSPECTION_PASSED"
rule inspection_failed:
when:
match Inspection:
device: bind device
passed: eq false
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: false
reason: "INSPECTION_FAILED"
tests:
test sufficient_capacity:
given:
fact Capacity:
device: "pump_a"
available: 15
expect:
conclusion Evidence:
goal: "choose_equipment"
candidate: "pump_a"
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
firings: 1
"""
built = compile_source(source)
Capacity = built.schema("Capacity")
Inspection = built.schema("Inspection")
request = DecisionRequest(
goal="choose_equipment",
candidates=("pump_a", "pump_b"),
requirements=("capacity", "inspection"),
evidence_name="equipment.choice.Evidence",
closed=True,
)
# A typo must not silently turn an observation into missing evidence.
try:
decide(built.program, request, facts=(Capacity(device="pump_typo", available=15),))
except DecisionError as error:
assert error.code == "TING-DEC-003"
print(error.as_dict())
else:
raise AssertionError("Expected an out-of-domain evidence error")
# Limit the assessment separately from the inference run.
limited = decide(built.program, request,
facts=(Capacity(device="pump_a", available=15),),
limits=DecisionLimits(max_work=0))
assert limited.status is DecisionStatus.BUDGET_EXHAUSTED
assert limited.exhausted_limit == "max_work"
assert limited.selected_option is None| DecisionLimits field | Default and behavior |
|---|---|
max_candidates / max_requirements | 4,096 / 256. A larger request raises TING-DEC-004 before inference. These defaults are also the hard maximum values. |
max_inputs | 100,000 case facts, also the hard maximum. decide() consumes at most one extra input item to detect overflow before creating a session. Program seeds are governed by IRLimits instead. |
max_conclusions | 100,000 stored conclusions across all names/goals; configurable up to 1,000,000. A larger completed session returns an incomplete decision before evidence scanning. |
max_work | 1,000,000 logical operations; configurable up to 100,000,000. Charges one per inspected conclusion, candidate and requirement cell. A limit is checked before each charged operation. |
Limits accept nonnegative exact integers; Boolean values are rejected. They bound assessment records and logical work, not exact heap usage or elapsed time. DecisionLimits applies after inference except for request and case-input checks. To bound inference itself, use budget=ExecutionBudget(...) or max_firings=..., never both. With neither argument, decide() allows at most 10,000 firings. An explicit ExecutionBudget uses its own ceilings; unspecified fields are unbounded.
An incomplete decision has empty candidates and feasible_options and selected_option=None; partial assessments are not published as final conclusions. Its snapshot can contain partial inference evidence. Retry decide() with adequate limits to start a fresh evaluation. assess_decision() can reassess an already completed batch session with a larger assessment allowance without rerunning inference.
| Error code | Action |
|---|---|
TING-DEC-001 | Correct invalid request names, duplicate entries, argument types or limit values. Inspect error.path. |
TING-DEC-002 | Correct the emitted evidence fields or types. The path identifies the offending conclusion or field. |
TING-DEC-003 | Align emitted candidate/requirement names with the request, or correct a spelling mistake. |
TING-DEC-004 | Reduce request/input size or increase the relevant allowed size limit within its hard maximum. |
Source, compiled-file and engine validation errors retain their existing exception types. Catch SourceError around compilation and DecisionError around request creation or assessment as appropriate. Unexpected execution faults propagate as exceptions; they are never converted into NO_FEASIBLE_OPTION. decide() owns a new session, so an error cannot alter a separate live Engine.
Evaluate independent cases with multiprocessing
decide() uses the current process. For multiple independent cases, run the program through run_batch() or iter_batch(), then pass each SessionResult to assess_decision(). The inference work can run in workers; the parent performs the deterministic evidence assessment.
from ting_reasoner import (
DecisionRequest, ExecutionPolicy, InferenceJob,
assess_decision, compile_source, run_batch,
)
source = """ting source 1
requires semantics 1
module equipment.choice version "1.0.0" role program
schemas:
schema Capacity:
device: text required
available: integer required
schema Inspection:
device: text required
passed: boolean required
conclusions:
conclusion Evidence:
goal: text required
candidate: text required
requirement: text required
satisfied: boolean required
reason: text required
rules:
rule capacity_sufficient:
when:
match Capacity:
device: bind device
available: ge 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
rule capacity_insufficient:
when:
match Capacity:
device: bind device
available: lt 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: false
reason: "CAPACITY_BELOW_10"
rule inspection_passed:
when:
match Inspection:
device: bind device
passed: eq true
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: true
reason: "INSPECTION_PASSED"
rule inspection_failed:
when:
match Inspection:
device: bind device
passed: eq false
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: false
reason: "INSPECTION_FAILED"
tests:
test sufficient_capacity:
given:
fact Capacity:
device: "pump_a"
available: 15
expect:
conclusion Evidence:
goal: "choose_equipment"
candidate: "pump_a"
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
firings: 1
"""
def main():
built = compile_source(source)
Capacity = built.schema("Capacity")
Inspection = built.schema("Inspection")
request = DecisionRequest("choose_equipment", ("pump_a", "pump_b"),
("capacity", "inspection"), "equipment.choice.Evidence", closed=True)
jobs = (
InferenceJob((Capacity(device="pump_a", available=15),
Inspection(device="pump_a", passed=True),
Capacity(device="pump_b", available=5))),
InferenceJob((Capacity(device="pump_a", available=15),
Capacity(device="pump_b", available=5))),
)
batch = run_batch(built.program, jobs,
policy=ExecutionPolicy(mode="owned", max_workers=2, minimum_parallel_facts=0))
results = tuple(assess_decision(session, request) for session in batch.results)
assert results[0].selected_option == "pump_a"
assert results[1].outcome.value == "NEEDS_DATA"
for result in results:
print(result.outcome.value, result.selected_option)
if __name__ == "__main__":
main()Save the example as a .py file and run it from a terminal. The main guard supports spawned workers on Windows, Linux and macOS. The example sets minimum_parallel_facts=0 only to demonstrate workers on tiny inputs; process startup can outweigh their benefit. Use measured thresholds for your workloads. Each result is bound to its own snapshot. For different candidate lists, use the corresponding request when assessing each session.
Save and explain a decision result
Keep the returned result and its snapshot when your application needs to show why an option passed or failed. See Explain a conclusion for reading the rule evidence and Reuse a compiled program for evaluating further cases.
Support and opposition contain actual Conclusion records, so all underlying rule proofs remain available through the snapshot. as_dict() includes each evidence record’s index in snapshot.conclusions. Keep the snapshot if you need to resolve those references later. The request digest identifies request contents and order; program_digest identifies the compiled program. snapshot_epoch and session_sequence are local labels, not globally unique snapshot identities. The summary is not an independent verification certificate.
This policy evaluates hard requirements and preserves ties. It does not rank preferences, resolve authority conflicts, expire observations, construct candidate domains, or search for counterfactual changes. Domain closure and the relevance of input observations remain explicit application responsibilities.
Store a decision in a compiled file
A named decision keeps its candidate list, hard requirements, evidence channel and selection policy with the reasoning program. Write it in a readable .ting file, compile that file, then retrieve the request from the resulting .tingir file. Your application supplies each case’s facts and calls decide(). Loading a request does not run rules or choose an option.
This section uses the equipment rules from Make and inspect a decision. The complete examples include their source text so you can run each example on its own. See that section for outcome handling, inference limits and evidence inspection.
Where the files go
Save the first complete Python example as named_decision.py and run it with python named_decision.py (or py named_decision.py on Windows). Path.cwd() is the folder your terminal is currently using. The example creates a ting_projects/named equipment folder inside it and prints the full source and compiled paths.
To choose another folder, replace the folder = ... line with folder = Path.home() / "Ting Files", or use a path from the file-path instructions.
mkdir(parents=True, exist_ok=True) creates missing folders. write_text(..., encoding="utf-8") creates the readable source file, replacing that example file if it already exists. compile_file() creates the compiled file after validation and embedded tests succeed. It does not create missing parent folders. The example passes overwrite=True so it can replace its previous compiled output; omit this argument when existing compiled files should be protected.
Declare the decision in .ting source
Add requires feature decisions immediately after the module header. Add this decisions: region alongside schemas:, conclusions: and rules:. Use two spaces for each indentation level. In source, Boolean literals are lowercase true and false; Python uses True and False.
decisions:
decision ChooseEquipment:
goal: "choose_equipment"
candidates: ["pump_a", "pump_b"]
requirements: ["capacity", "inspection"]
evidence: Evidence
closed: true
policy: "unique-feasible-v1"
requires:
rule capacity_sufficient
rule capacity_insufficient
rule inspection_passed
rule inspection_failed| Field | Meaning |
|---|---|
decision ChooseEquipment: | The local declaration name. Its compiled name is equipment.choice.ChooseEquipment because the module is equipment.choice. |
goal | Nonempty text matching the goal emitted by the evidence rules. |
candidates | An ordered list of distinct text identifiers; at most 4,096. List order is preserved and never breaks a tie. |
requirements | An ordered list of distinct hard requirement names; at most 256. Every requirement must pass. |
evidence | An unquoted, source-visible conclusion name. Its exact fields are goal, candidate, requirement, satisfied and reason; satisfied is boolean and the other four are text. |
closed | Required true or false. true asserts that the candidate list is complete for this decision. The application author owns this assertion. |
policy | Required "unique-feasible-v1". This selects only a unique feasible candidate, retaining missing evidence, conflicts and ties. |
logical_time | Optional integer from 0 to 2**63 − 1; defaults to 0. It is recorded metadata, not an automatic clock or evidence-expiry filter. |
requires: | Required block listing the rule and seed declarations needed when this decision is imported selectively. The block may be empty. Rule dependencies are followed transitively; the evidence declaration is included automatically. |
Every field except logical_time is mandatory. Candidate and requirement identifiers follow the DecisionRequest text limits: nonempty valid Unicode, at most 256 UTF-8 bytes and no ASCII control characters. Empty lists are permitted: an empty closed candidate universe is infeasible within that empty universe; an empty requirement list imposes no conditions. Use these cases only when they describe your intended scope.
The compiler checks that dependency names exist. You must list the rules and fixed facts needed for the intended decision, including producer rules declared in a rule’s own requires: block. Ting does not discover missing domain knowledge or assume that all rules in a folder should run. Schema/classification definitions required by the selected rules are included automatically. Session observations still come from your application.
Complete example: save, compile, load and decide
from pathlib import Path
from ting_reasoner import (
CompiledCatalog, DecisionOutcome, Engine, SourceError,
compile_file, compile_source, decide, load_tingir,
)
source = """ting source 1
requires semantics 1
module equipment.choice version "1.0.0" role program
requires feature decisions
schemas:
schema Capacity:
device: text required
available: integer required
schema Inspection:
device: text required
passed: boolean required
conclusions:
conclusion Evidence:
goal: text required
candidate: text required
requirement: text required
satisfied: boolean required
reason: text required
rules:
rule capacity_sufficient:
when:
match Capacity:
device: bind device
available: ge 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
rule capacity_insufficient:
when:
match Capacity:
device: bind device
available: lt 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: false
reason: "CAPACITY_BELOW_10"
rule inspection_passed:
when:
match Inspection:
device: bind device
passed: eq true
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: true
reason: "INSPECTION_PASSED"
rule inspection_failed:
when:
match Inspection:
device: bind device
passed: eq false
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: false
reason: "INSPECTION_FAILED"
tests:
test sufficient_capacity:
given:
fact Capacity:
device: "pump_a"
available: 15
expect:
conclusion Evidence:
goal: "choose_equipment"
candidate: "pump_a"
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
firings: 1
decisions:
decision ChooseEquipment:
goal: "choose_equipment"
candidates: ["pump_a", "pump_b"]
requirements: ["capacity", "inspection"]
evidence: Evidence
closed: true
policy: "unique-feasible-v1"
requires:
rule capacity_sufficient
rule capacity_insufficient
rule inspection_passed
rule inspection_failed
"""
# Change this one folder if you want to store the files elsewhere.
folder = Path.cwd() / "ting_projects" / "named equipment"
folder.mkdir(parents=True, exist_ok=True)
source_path = folder / "equipment.ting"
compiled_path = folder / "equipment.tingir"
source_path.write_text(source, encoding="utf-8")
# overwrite=True allows this example to be run again in the same folder.
built = compile_file(source_path, compiled_path, overwrite=True)
assert built.decision("ChooseEquipment").closed is True
print("Source:", source_path)
print("Compiled:", built.output)
# Everything below also works in a separate Python program.
program = load_tingir(compiled_path)
print(program.decision_names)
request = program.decision("equipment.choice.ChooseEquipment")
# Use the loaded program's schemas to construct correctly typed input facts.
schemas = Engine(program)
Capacity = schemas.schema("equipment.choice.Capacity")
Inspection = schemas.schema("equipment.choice.Inspection")
result = decide(program, request, facts=(
Capacity(device="pump_a", available=15),
Inspection(device="pump_a", passed=True),
Capacity(device="pump_b", available=5),
))
assert result.outcome is DecisionOutcome.SELECTED
assert result.selected_option == "pump_a"
print(result.outcome.value, result.selected_option)The final line prints SELECTED pump_a. pump_a passes both requirements; pump_b fails capacity, so its absent inspection does not restore its eligibility. The returned value is a DecisionResult, with the same outcome and error behavior as a request constructed directly in Python.
built.decision("ChooseEquipment") accepts names visible in the source, including import aliases. A loaded CompactIR uses exact qualified names: program.decision("equipment.choice.ChooseEquipment"). Its decision_names property lists the available names. Neither method changes the program or runs inference.
To run on another computer, copy the compiled program, install a Ting version that supports its format, and set compiled_path to the file’s actual location on that computer. The loading-and-running part of the example needs no source file or compiler call. The inspection Engine supplies the schemas; decide() owns its separate reasoning session. Both reuse the same verified program.
Import one decision from a compiled library
Use role library in the source header and export decision ChooseEquipment. Other declarations can stay private. CompiledCatalog.link(decisions=("equipment.choice.ChooseEquipment",)) includes that request, its evidence declaration, and the rule/seed dependencies you listed. The comma creates a one-element Python tuple. It does not select unrelated exported decisions or every rule in the library.
from pathlib import Path
from ting_reasoner import (
CompiledCatalog, DecisionOutcome, Engine, SourceError,
compile_file, compile_source, decide, load_tingir,
)
source = """ting source 1
requires semantics 1
module equipment.choice version "1.0.0" role program
requires feature decisions
schemas:
schema Capacity:
device: text required
available: integer required
schema Inspection:
device: text required
passed: boolean required
conclusions:
conclusion Evidence:
goal: text required
candidate: text required
requirement: text required
satisfied: boolean required
reason: text required
rules:
rule capacity_sufficient:
when:
match Capacity:
device: bind device
available: ge 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
rule capacity_insufficient:
when:
match Capacity:
device: bind device
available: lt 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: false
reason: "CAPACITY_BELOW_10"
rule inspection_passed:
when:
match Inspection:
device: bind device
passed: eq true
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: true
reason: "INSPECTION_PASSED"
rule inspection_failed:
when:
match Inspection:
device: bind device
passed: eq false
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: false
reason: "INSPECTION_FAILED"
tests:
test sufficient_capacity:
given:
fact Capacity:
device: "pump_a"
available: 15
expect:
conclusion Evidence:
goal: "choose_equipment"
candidate: "pump_a"
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
firings: 1
decisions:
decision ChooseEquipment:
goal: "choose_equipment"
candidates: ["pump_a", "pump_b"]
requirements: ["capacity", "inspection"]
evidence: Evidence
closed: true
policy: "unique-feasible-v1"
requires:
rule capacity_sufficient
rule capacity_insufficient
rule inspection_passed
rule inspection_failed
"""
# Two compiled libraries can live in different folders.
root = Path.cwd() / "ting_projects" / "decision libraries"
primary_folder = root / "primary rules"
backup_folder = root / "backup rules"
for folder in (primary_folder, backup_folder):
folder.mkdir(parents=True, exist_ok=True)
# The header and export determine the library's contents and public entry.
library_source = source.replace("role program", "role library")
library_source += "\nexports:\n decision ChooseEquipment\n"
primary_source = primary_folder / "equipment.ting"
primary_source.write_text(library_source, encoding="utf-8")
backup_source = backup_folder / "equipment.ting"
backup_source.write_text(library_source.replace("equipment.choice", "equipment.backup"), encoding="utf-8")
primary = compile_file(primary_source, overwrite=True)
backup = compile_file(backup_source, overwrite=True)
catalog = CompiledCatalog({
"equipment.choice": primary.output,
"equipment.backup": backup.output,
})
linked = catalog.link(decisions=("equipment.choice.ChooseEquipment",))
request = linked.decision("equipment.choice.ChooseEquipment")
assert linked.program.decision_names == ("equipment.choice.ChooseEquipment",)
assert linked.usage.modules == 1 # The backup library was not opened.
Capacity = linked.schema("equipment.choice.Capacity")
Inspection = linked.schema("equipment.choice.Inspection")
result = decide(linked.program, request, facts=(
Capacity(device="pump_a", available=15),
Inspection(device="pump_a", passed=True),
Capacity(device="pump_b", available=5),
))
assert result.selected_option == "pump_a"
assert len(linked.knowledge_base.rules) == 4
print(linked.imports) # Includes the decision's private required rules.
# Retain linked if your application needs the compiled source explanations.
evidence = result.candidates[0].constraints[0].support[0]
assert linked.explain(result.snapshot, evidence).proofsA catalog maps each exact module name to its compiled file path. You can supply files from several folders and select several decisions in one link call. Every external dependency also needs a catalog entry matching its compiled version and contract. The linker does not scan folders automatically. linked.imports records the selected declarations and dependencies; linked.usage reports the sections and bytes read.
namespaces=("equipment.choice",) selects every public export in that module, including decisions and their dependencies. Selecting one decision reads the container directory and the selected sections; unrelated payloads are not verified by that operation. Use CompiledLibrary.verify_all() when you need to check every payload. Keep linked for source-linked explanations, or save linked.program with save_tingir() for a standalone executable file. The standalone program retains requests and rules, while the separate linked source map remains with linked.
Retrieving a request from a whole program does not filter that program’s rules. Compile and link a selected library subset when rule isolation is required. All conclusions matching the request’s goal and evidence channel must use candidate/requirement names from that request. Different decisions can use separate goals or evidence channels to keep their evidence domains distinct.
Import a source declaration
Source imports support the same named entry and an optional local alias:
ting source 1
requires semantics 1
module application version "1.0.0" role program
requires feature decisions
imports:
from equipment.choice version "1.0.0":
decision ChooseEquipment as EquipmentChoicePass modules={"equipment.choice": provider_source_path} to compile_file(), where provider_source_path is the actual library .ting path. The build then supports built.decision("EquipmentChoice"); after loading its compiled file, use program.decision("equipment.choice.ChooseEquipment"). An alias does not rename the portable declaration. With in-memory text, use compile_source(..., resolver=MemoryResolver({...})) as shown in Source modules.
Handle declaration, file and lookup errors
from pathlib import Path
from ting_reasoner import (
CompiledCatalog, DecisionOutcome, Engine, SourceError,
compile_file, compile_source, decide, load_tingir,
)
source = """ting source 1
requires semantics 1
module equipment.choice version "1.0.0" role program
requires feature decisions
schemas:
schema Capacity:
device: text required
available: integer required
schema Inspection:
device: text required
passed: boolean required
conclusions:
conclusion Evidence:
goal: text required
candidate: text required
requirement: text required
satisfied: boolean required
reason: text required
rules:
rule capacity_sufficient:
when:
match Capacity:
device: bind device
available: ge 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
rule capacity_insufficient:
when:
match Capacity:
device: bind device
available: lt 10
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "capacity"
satisfied: false
reason: "CAPACITY_BELOW_10"
rule inspection_passed:
when:
match Inspection:
device: bind device
passed: eq true
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: true
reason: "INSPECTION_PASSED"
rule inspection_failed:
when:
match Inspection:
device: bind device
passed: eq false
then:
conclude Evidence:
goal: "choose_equipment"
candidate: use device
requirement: "inspection"
satisfied: false
reason: "INSPECTION_FAILED"
tests:
test sufficient_capacity:
given:
fact Capacity:
device: "pump_a"
available: 15
expect:
conclusion Evidence:
goal: "choose_equipment"
candidate: "pump_a"
requirement: "capacity"
satisfied: true
reason: "CAPACITY_AT_LEAST_10"
firings: 1
decisions:
decision ChooseEquipment:
goal: "choose_equipment"
candidates: ["pump_a", "pump_b"]
requirements: ["capacity", "inspection"]
evidence: Evidence
closed: true
policy: "unique-feasible-v1"
requires:
rule capacity_sufficient
rule capacity_insufficient
rule inspection_passed
rule inspection_failed
"""
from dataclasses import replace
from ting_reasoner import DecisionError, SourceSnapshot
# A declaration error is rejected before a compiled artifact is published.
invalid_source = source.replace("closed: true", "closed: 1")
try:
compile_source(SourceSnapshot(invalid_source, uri="memory:equipment.ting"))
except SourceError as error:
diagnostic = error.diagnostic
assert diagnostic.code == "TING-SRC-006"
print(diagnostic.code, diagnostic.message)
print(diagnostic.span.uri, diagnostic.span.line)
print(diagnostic.hint)
else:
raise AssertionError("An integer must not stand in for true or false")
program = compile_source(source).program
try:
program.decision("equipment.choice.Misspelled")
except KeyError:
print("Available decisions:", program.decision_names)
# Requests are immutable. A validated copy can represent a different case.
original = program.decision("equipment.choice.ChooseEquipment")
open_request = replace(original, closed=False, logical_time=120)
assert original.closed is True and open_request.closed is False
assert original.digest != open_request.digest
try:
replace(original, candidates=("pump_a", "pump_a"))
except DecisionError as error:
assert error.code == "TING-DEC-001"
print(error.as_dict())| Operation / error | What to do |
|---|---|
SourceError: TING-SRC-002 / 004 | Correct missing/unknown fields, indentation, or duplicate fields/dependencies. Inspect error.diagnostic.span and hint. |
TING-SRC-003 / 005 / 006 | Declare the decisions feature; use the supported policy; resolve names; correct types, duplicate candidates, or the evidence schema. |
TING-SRC-007 / TING-IR-001 | Check IRLimits or the compiled artifact’s integrity and supported versions. load_tingir() wraps IR errors in SourceError, which may have no source location; direct CompactIR(bytes) raises IRValidationError. |
LibraryError | Inspect code, message, hint and chain. Supply missing compiled dependencies, use exported names, or rebuild an incompatible/corrupt artifact. |
KeyError | The request name is absent. Check program.decision_names; compiled names must be fully qualified. |
OSError | Catch around ordinary Python folder creation and text writes. Check the actual folder, permissions and available storage. File API errors from Ting retain their SourceError or LibraryError category. |
DecisionError | Handle invalid Python request changes and malformed/out-of-domain evidence as described in the decision API section. |
A failed compilation leaves an existing compiled output intact. Compilation protects existing destinations by default. Requests are frozen objects; dataclasses.replace() creates and validates a new request for an application-controlled variation. That copy has its own digest and does not rewrite the compiled declaration. It is not an authorization mechanism or a signed policy. Keep the original request when the compiled candidate domain is the intended contract.
Build the same portable definition directly in Python
from pathlib import Path
from ting_reasoner import (
DecisionDefinition, DecisionRequest, FactSchema, KnowledgeBase, Rule,
bind, decide, emit, load_tingir, save_tingir, use,
)
Inspection = FactSchema("equipment.Inspection", device=str, passed=bool)
rule = Rule("inspect", (Inspection.pattern(device=bind("device"), passed=bind("passed")),),
(emit("equipment.Evidence", goal="choose", candidate=use("device"),
requirement="inspection", satisfied=use("passed"), reason="INSPECTED"),))
request = DecisionRequest("choose", ("pump_a", "pump_b"), ("inspection",),
"equipment.Evidence", closed=True)
knowledge = KnowledgeBase(rules=(rule,), decisions=(
DecisionDefinition("equipment.Choose", request),
))
folder = Path.cwd() / "ting_projects" / "python decisions"
folder.mkdir(parents=True, exist_ok=True)
path = folder / "inspection.tingir"
save_tingir(knowledge, path, overwrite=True)
program = load_tingir(path)
result = decide(program, program.decision("equipment.Choose"), facts=(
Inspection(device="pump_a", passed=True),
Inspection(device="pump_b", passed=False),
))
assert result.selected_option == "pump_a"DecisionDefinition(name, request) adds a named request to KnowledgeBase(..., decisions=(...)). The knowledge base includes the rules you supply. Named source imports and exports are available through the .ting library format; a Python definition alone does not declare a selective source dependency list. Both approaches can save a named decision in a compiled program. Use readable .ting source to declare selective library imports and dependencies. RuleSpec JSON does not support named decisions.
Reuse a request and set declaration limits
Retrieve the request once with program.decision(name) and reuse it across independent cases. For batch results, pass it to assess_decision(session, request), as shown in Make and inspect a decision. IRLimits.max_decisions defaults to 4,096; max_decision_items defaults to 100,000 total candidate plus requirement entries across the table. These bound compiled declarations; DecisionLimits and ExecutionBudget separately bound runtime assessment and inference.
Fixed-shape arrays
NDValue holds an immutable grid of values in row-major order (each row follows the previous one). Supported element types are bool, signed 64-bit i64, and finite 64-bit f64. A schema specifies dtype and exact shape.
from ting_reasoner import Engine, FactSchema, KnowledgeBase, NDField, NDValue
Grid = FactSchema("grid", (NDField("cells", "i64", (2, 2)),))
value = NDValue.from_flat([1, 2, 3, 4], dtype="i64", shape=(2, 2))
engine = Engine(KnowledgeBase((Grid,)))
engine.declare(Grid(cells=value))
assert engine.facts[0].values[0].tobytes() == value.tobytes()Whole-array equality, inequality, membership, binding and derivation are supported. Shape, element count and byte limits are checked before construction. Broadcasting, reductions, masks and general array arithmetic are not supported. Scalar Python integers and array i64 elements deliberately have different range contracts.
Choose an execution mode
Choose how to organize your cases before changing performance settings. A case is one set of observations evaluated by the rules.
| Your task | Use this approach |
|---|---|
| Update observations for the same case | Keep one Engine and enable support maintenance when its conditions are supported. Call run() after changing inputs. |
| Start another independent case | Reuse a compiled program with a new Engine, or call reset(*facts) on an existing Engine. |
| Evaluate many independent cases | Use run_batch() or iter_batch(). Start with inline mode; measure process workers on substantial cases. |
| Tune matching within one case | Set an OptimizationPolicy and inspect counters on representative facts and rules. |
Measure the operation your application actually performs, including loading, declaring facts, inference and inspecting results. Check that the results and proofs remain correct. Larger snapshots cost more to store and transfer; request only the work and outputs your application needs.
Pruning and performance
Pass an OptimizationPolicy to Engine when you want to set limits on optional search indexes. Start with the defaults, then inspect the counters using facts and rules representative of your application.
from ting_reasoner import (
Engine, FactSchema, KnowledgeBase, OptimizationPolicy, Rule,
bind, emit, ge, use,
)
Reading = FactSchema("reading", sensor=str, value=int)
rule = Rule("high_reading",
(Reading.pattern(sensor=bind("sensor"), value=ge(35)),),
(emit("high_reading", sensor=use("sensor")),))
knowledge = KnowledgeBase((Reading,), (rule,))
engine = Engine(knowledge, optimization=OptimizationPolicy(
minimum_rows=32,
max_indexes=32,
max_cache_cells=1_048_576,
max_build_work=4_194_304,
))
engine.declare(*(Reading(sensor=str(i), value=i) for i in range(40)))
result = engine.run()
assert result.status.value == "quiescent"
assert len(engine.conclusions) == 5
print(engine.match_metrics)
print(engine.optimization_metrics)minimum_rows sets the smallest candidate collection considered for these indexes. max_indexes limits their number; max_cache_cells limits stored index entries, and max_build_work limits the work used to construct them. These are work and entry limits, not a limit on the whole Python process's memory. Reaching an optional index limit can make a query slower but does not omit valid matches.
Read engine.match_metrics after run() to see candidate-row and constraint counts. engine.optimization_metrics.pruned_rows reports avoided candidate checks, and build_rows reports rows examined while constructing indexes. The example emits five high-reading conclusions. Counters may be zero when an index is unnecessary. Use OptimizationPolicy(enabled=False) when comparing execution with these optional indexes disabled; it does not disable every optimization in Ting.
For repeated positive-rule updates, enable MaintenancePolicy() as described in Keep valid results when inputs change. When writing a join, include fields that identify the intended relationship, as in Rules and conditions. Matching every fact against every other fact can create many combinations even with indexing.
Independent sessions in parallel
Before this section: Reuse a compiled program and Work limits.
Use batch execution for separate cases that share a knowledge base. Each case gets its own Engine, fact memory, handles and proofs. Results return in input order regardless of which process finishes first.
from ting_reasoner import (
ExecutionPolicy, FactSchema, InferenceJob, KnowledgeBase,
Rule, bind, emit, run_batch, use,
)
def main():
Fruit = FactSchema("fruit", name=str, sweet=bool)
rule = Rule("sweet", (Fruit.pattern(name=bind("name"), sweet=True),),
(emit("sweet_fruit", name=use("name")),))
knowledge = KnowledgeBase((Fruit,), (rule,))
jobs = [
InferenceJob((Fruit(name="apple", sweet=True),)),
InferenceJob((Fruit(name="lemon", sweet=False),)),
]
result = run_batch(knowledge, jobs, policy=ExecutionPolicy(
mode="owned", max_workers=2, max_in_flight=2,
minimum_parallel_facts=0, # Force processes for this tiny demonstration.
))
assert [len(item.snapshot.conclusions) for item in result.results] == [1, 0]
for item in result.results:
print(item.sequence, item.snapshot.conclusions)
if __name__ == "__main__":
main()| Policy | Ownership and behavior |
|---|---|
inline | Default. No executor or process is created. |
owned | Explicit process pool using the requested local context; defaults to spawn. Ting closes its pool. |
borrowed | Pass an Executor-compatible object. Its owner remains responsible for shutdown. |
ParallelBudget | Shared nonblocking submission permits. When exhausted, work runs inline. |
| Nested / background thread calls | Run inline to avoid recursive submission and pool deadlocks. |
| Small chunks | Run inline below minimum_parallel_facts. This is a dispatch heuristic, not a measured break-even guarantee. |
Save the complete example as parallel_example.py and run it from a terminal. The if __name__ == "__main__": guard prevents a new worker from launching more workers as it loads the script. Use inline mode in environments that cannot launch processes. Worker processes are part of your application; they do not isolate untrusted code.
Read results one at a time
This complete example uses inline execution and prints results for apple, then pear. closing() releases the iterator’s resources if you stop early. For process workers, use the main-guard structure in the preceding example.
from contextlib import closing
from ting_reasoner import (
ExecutionPolicy, FactSchema, InferenceJob, KnowledgeBase,
Rule, bind, emit, iter_batch, use,
)
Item = FactSchema("item", name=str)
rule = Rule("record", (Item.pattern(name=bind("name")),),
(emit("recorded", name=use("name")),))
knowledge = KnowledgeBase((Item,), (rule,))
jobs = [InferenceJob((Item(name="apple"),)),
InferenceJob((Item(name="pear"),))]
policy = ExecutionPolicy(mode="inline", max_in_flight=2, chunk_size=1)
with closing(iter_batch(knowledge, jobs, policy=policy)) as results:
for result in results:
assert result.run.status.value == "quiescent"
for conclusion in result.snapshot.conclusions:
print(result.sequence, conclusion.as_dict())
# closing() also cleans up if a loop exits early with break or an exception.max_in_flight × chunk_size bounds submitted/prefetched jobs; each job is limited to 100,000 facts. It does not bound the total bytes in facts or output. run_batch retains all results until the whole batch succeeds, with a configurable total-job ceiling. iter_batch yields ordered results and allows earlier results to remain usable if a later job fails.
Cancellation accepts an object with is_set(), such as a threading Event. It stops submissions and publication at coordinator boundaries. Already-running jobs finish; cancellation is not a hard process kill or a deadline. Owned-pool cleanup waits for those jobs. Use logical job budgets to bound their work.
BatchExecutionError.sequence identifies a failed job. Caller-owned engines are never mutated. Worker startup and executor failures propagate; Ting does not silently hide a failed process launch. A borrowed executor remains usable afterward.
Batch execution does not split one live reasoning session across cores. It improves throughput only when independent work outweighs process and serialization costs. For CPU work, a borrowed process pool is generally more appropriate than a thread pool.
Troubleshooting
| Symptom | What to check |
|---|---|
| No conclusion | Inspect exact field types, rule bindings, negation, fact presence and RunResult.status. A Boolean is not an integer fact value. |
| Repeated budget exhaustion | Increase the specific exhausted budget or reduce the matching problem. A partial match scan does not automatically become a resumable cursor. |
| Process launch fails | Use an importable .py file and a main guard. Check host process permissions and selected start method. Interactive notebooks may require inline execution. |
| Processes are slower | Measure serialization, snapshot size and startup. Try larger independent jobs, bounded chunks, a reused caller-owned pool, or inline execution. |
| Stale transaction | Re-read the epoch and retry the logical change against current facts. |
| Memory grows | Bound facts, conclusions and traces; use iter_batch, limit in-flight jobs, and inspect index/storage metrics. Logical cache cells do not measure all heap memory. |
| A derived handle stopped working | An input change withdrew the fact, or reset reused its numeric handle. Reacquire handles from current engine.fact_items; see input changes. |
| Need package diagnostics | Run python -m ting_reasoner.selftest and retain the JSON output with your report. |
Exceptions preserve category-specific information: schema/rule validation errors, compilation/IR verification errors, engine errors, stale transactions, history errors and batch errors. Catch the specific category you can recover from. Use build errors, decision errors, or batch errors for recovery examples, and the exception reference for their base class.
Availability and limits
The package provides typed facts, declarative conditions, verified compiled programs, deterministic inference, base-fact transactions, immutable snapshots, explanations, fixed-shape arrays, bounded optional pruning and independent-session multiprocessing. The decision API evaluates hard requirements over an explicit candidate list, preserving unknowns, conflicts and ties.
For changing observations, follow the input-update rules. The API does not provide aggregate operators, temporal or backward reasoning, exact process-memory budgets, or parallel matching inside one Engine. Implement application actions in Python after checking the returned results.
The supported application scope excludes banking and financial use. See the package’s LICENSE and LICENSING.md files for the terms and project policy.
API reference
Look up a name here after choosing the relevant tutorial. The reference lists every symbol exported by ting_reasoner, including advanced and optional APIs; you do not need all of them to build a reasoner. Read Understand the Python calls for signature notation. Each usage link returns to the related explanation and examples.
__version__View API
The version of the installed Ting Reasoner package.
__version__ = '0.8.5'ActionView API
A safe, portable rule consequence.
Action(kind: 'str', target: 'FactSchema | str', values: 'tuple[tuple[str, Value | BindingRef], ...]') -> NoneUsage and examples: Rules and conditions.
all_ofView API
Require every child condition to match.
all_of(*conditions: 'Condition') -> 'Condition'Usage and examples: Rules and conditions.
AllConditionView API
A conjunction of portable rule conditions.
AllCondition(conditions: 'tuple[Condition, ...]') -> NoneUsage and examples: Rules and conditions.
analyze_sourceView API
Parse and statically link source; reject unknown names, cycles and invalid rules.
analyze_source(source: 'str | SourceSnapshot', *, resolver: 'SourceResolver | None' = None, limits: 'SourceLimits | None' = None, ir_limits: 'IRLimits | None' = None) -> 'SourceAnalysis'Usage and examples: Compile readable source.
any_ofView API
Accept any matching alternative; alternatives must establish the same bindings.
any_of(*conditions: 'Condition') -> 'Condition'Usage and examples: Rules and conditions.
AnyConditionView API
A disjunction of portable rule conditions.
AnyCondition(conditions: 'tuple[Condition, ...]') -> NoneUsage and examples: Rules and conditions.
assess_decisionView API
Assess a run_batch()/iter_batch() session without rerunning or mutating it.
assess_decision(session: 'SessionResult', request: 'DecisionRequest', *, limits: 'DecisionLimits | None' = None) -> 'DecisionResult'Usage and examples: Make and inspect a decision.
AuthoringBackendView API
Formatting, analysis, test and build operations for applications that provide their own source editor.
AuthoringBackend()Usage and examples: Compile readable source.
| Member | Behavior |
|---|---|
analyze(self, source: 'str | SourceSnapshot', *, resolver: 'SourceResolver | None' = None, limits: 'SourceLimits | None' = None, ir_limits: 'IRLimits | None' = None) -> 'SourceAnalysis' | Resolve and validate a source graph without writing files. |
build(self, analysis: 'SourceAnalysis', *, run_tests: 'bool' = True, ir_limits: 'IRLimits | None' = None, test_budget: 'ExecutionBudget | None' = None, library_limits: 'LibraryLimits | None' = None) -> 'SourceBuild | LibraryBuild' | Build the header-selected program or library; test roots remain nondeployable. |
diff(self, left: 'SourceAnalysis', right: 'SourceAnalysis') -> 'dict[str, Any]' | Compare normalized source identities; this is not behavioral equivalence. |
format(self, source: 'str | SourceSnapshot | SourceDocument', *, limits: 'SourceLimits | None' = None) -> 'str' | Normalize source line endings while preserving statements and comments. |
inspect(self, target: 'SourceAnalysis | SourceBuild') -> 'dict[str, Any]' | Return the target's stable JSON-compatible public view. |
test(self, analysis: 'SourceAnalysis', *, budget: 'ExecutionBudget | None' = None, ir_limits: 'IRLimits | None' = None) -> 'RuleSpecTestReport' | Run root-module tests in separate sessions under finite work budgets. |
BatchCancelledErrorView API
The caller cancelled before the next ordered result was published.
BatchCancelledErrorUsage and examples: Multiprocessing.
BatchExecutionErrorView API
An independent job failed; sequence identifies its input position.
BatchExecutionError(sequence: 'int', message: 'str') -> 'None'Usage and examples: Multiprocessing.
BatchMetricsView API
Observed dispatch, with result order independent of completion order.
BatchMetrics(jobs: 'int' = 0, submitted_chunks: 'int' = 0, inline_jobs: 'int' = 0, peak_in_flight: 'int' = 0, pool_created: 'bool' = False, nested_inline: 'bool' = False) -> NoneUsage and examples: Multiprocessing.
BatchResultView API
All independent results, published only after every job succeeds.
BatchResult(results: 'tuple[SessionResult, ...]', metrics: 'BatchMetrics') -> NoneUsage and examples: Multiprocessing.
betweenView API
Match an inclusive lower/upper interval.
between(low: 'Value | BindingRef', high: 'Value | BindingRef') -> 'Constraint'Usage and examples: Rules and conditions.
bindView API
Bind a field value to name (or compare it if already bound).
bind(name: 'str') -> 'Constraint'Usage and examples: Rules and conditions.
BindingRefView API
A reference to a fixed binding slot in a rule.
BindingRef(name: 'str') -> NoneUsage and examples: Rules and conditions.
BudgetUsageView API
Exact logical work charged during one run call.
BudgetUsage(candidate_rows: 'int' = 0, constraint_evaluations: 'int' = 0, activations: 'int' = 0, firings: 'int' = 0) -> NoneUsage and examples: Work limits.
CandidateAssessmentView API
Complete requirement assessments for one candidate, in request order.
CandidateAssessment(candidate: 'str', feasibility: 'CandidateFeasibility', constraints: 'tuple[ConstraintAssessment, ...]') -> NoneUsage and examples: Make and inspect a decision.
CandidateFeasibilityView API
Whether a candidate satisfies every requested hard requirement.
CandidateFeasibility(*values)Usage and examples: Make and inspect a decision.
canonical_source_bytesView API
Encode one RuleSpec as byte-stable, newline-terminated UTF-8 JSON.
canonical_source_bytes(spec: 'RuleSpec', *, limits: 'RuleSpecLimits | None' = None) -> 'bytes'Usage and examples: Portable program files.
CheckpointRevisionView API
Verified immutable core of one source checkpoint.
CheckpointRevision(revision: 'str', parent: 'str | None', restored_from: 'str | None', source_object: 'str', rulespec_object: 'str', artifact_object: 'str', source_digest: 'str', semantic_digest: 'str', artifact_digest: 'str', artifact_payload_digest: 'str', artifact_size: 'int', ting_version: 'str', format_version: 'int', semantics_version: 'int', rulespec_version: 'int', test_passed: 'bool', test_passed_count: 'int', test_failed_count: 'int') -> NoneUsage and examples: Portable program files.
| Member | Behavior |
|---|---|
id | Alias useful to callers that display revision identifiers. |
CLASS_FORMAT_VERSIONView API
Public constant or type alias; its value or definition is shown below.
CLASS_FORMAT_VERSION = 6CLASS_SEMANTICS_VERSIONView API
Public constant or type alias; its value or definition is shown below.
CLASS_SEMANTICS_VERSION = 7ClassificationView API
A named membership predicate over one record schema.
Classification(name: 'str', record_schema: 'FactSchema', *, parents: 'Iterable[Classification]' = ()) -> 'None'Usage and examples: Classifications and inheritance.
| Member | Behavior |
|---|---|
classify(self, fact: 'Fact') -> 'Fact' | Copy a record's exact values into an explicit membership assertion. |
is_a(self, other: 'Classification') -> 'bool' | Whether this classification is the same nominal type or a descendant. |
pattern(self, **constraints) | Match membership facts, including parents derived during inference. |
schema | The distinct schema storing explicit or derived membership assertions. |
CommitReceiptView API
A committed delta plus staged-to-committed handle resolutions.
CommitReceipt(delta: 'DeltaBatch', resolutions: 'tuple[tuple[StagedHandle, FactHandle | None], ...]' = ()) -> NoneUsage and examples: Transactions.
| Member | Behavior |
|---|---|
changed | Read-only property. |
epoch | Read-only property. |
resolve(self, staged: 'StagedHandle') -> 'FactHandle | None' | Resolve a handle created by the transaction represented here. |
CompactIRView API
Verified immutable program bytes with a reusable execution plan.
CompactIR(data: 'bytes | bytearray | memoryview', *, limits: 'IRLimits | None' = None) -> 'None'Usage and examples: Reuse a compiled program.
| Member | Behavior |
|---|---|
decision(self, name: 'str') -> 'DecisionRequest' | Get a compiled request by exact qualified name; raise KeyError if absent. |
decision_names | Exact qualified names of the decisions stored in this program. |
digest | SHA-256 identity of the exact compiled program bytes. |
verify(self, *, limits: 'IRLimits | None' = None) -> 'RuntimePlan' | Return a verified immutable plan under the requested limit policy. |
CompilationErrorView API
A valid-looking authoring model could not be lowered to Compact-IR.
CompilationErrorcompileView API
Alias for compile_knowledge_base(): compile a KnowledgeBase to binary program bytes without writing a file. Use save_tingir() to save a compiled file.
compile(kb: 'KnowledgeBase', *, limits: 'IRLimits | None' = None) -> 'bytes'Usage and examples: Reuse a compiled program.
compile_fileView API
Compile readable .ting source and atomically save its .tingir artifact.
compile_file(path: 'str | os.PathLike[str]', output: 'str | os.PathLike[str] | None' = None, *, modules: 'Mapping[str, str | os.PathLike[str]] | None' = None, overwrite: 'bool' = False, run_tests: 'bool' = True, limits: 'SourceLimits | None' = None, ir_limits: 'IRLimits | None' = None, test_budget: 'ExecutionBudget | None' = None, library_limits: 'LibraryLimits | None' = None) -> 'SourceBuild | LibraryBuild'Usage and examples: Compile readable source.
compile_filesView API
Build independent roots in input order, returning success/error for each file.
compile_files(paths: 'Iterable[str | os.PathLike[str]]', output_directory: 'str | os.PathLike[str]', *, modules: 'Mapping[str, str | os.PathLike[str]] | None' = None, overwrite: 'bool' = False, run_tests: 'bool' = True, max_files: 'int' = 256, limits: 'SourceLimits | None' = None, ir_limits: 'IRLimits | None' = None, test_budget: 'ExecutionBudget | None' = None, library_limits: 'LibraryLimits | None' = None) -> 'tuple[FileBuildResult, ...]'Usage and examples: Compile readable source.
compile_knowledge_baseView API
Compile a KnowledgeBase to binary program bytes without writing a file. Wrap the bytes in CompactIR for reuse, or use save_tingir() to save a compiled file.
compile_knowledge_base(kb: 'KnowledgeBase', *, limits: 'IRLimits | None' = None) -> 'bytes'Usage and examples: Reuse a compiled program.
compile_rulespecView API
Compile a validated RuleSpec JSON project to binary program bytes.
compile_rulespec(spec: 'RuleSpec', *, limits: 'IRLimits | None' = None) -> 'bytes'Usage and examples: Portable program files.
compile_sourceView API
Compile readable Ting text and run embedded tests by default. Returns SourceBuild for role program or LibraryBuild for role library.
compile_source(source: 'str | SourceSnapshot', *, resolver: 'SourceResolver | None' = None, limits: 'SourceLimits | None' = None, ir_limits: 'IRLimits | None' = None, run_tests: 'bool' = True, test_budget: 'ExecutionBudget | None' = None, library_limits: 'LibraryLimits | None' = None) -> 'SourceBuild | LibraryBuild'Usage and examples: Compile readable source.
CompiledCatalogView API
Resolve compiled namespaces only from explicit paths or open libraries.
CompiledCatalog(libraries: 'Mapping[str, str | os.PathLike | CompiledLibrary]', *, limits: 'LibraryLimits | None' = None)Usage and examples: Compile and import selected knowledge.
| Member | Behavior |
|---|---|
link(self, *, namespaces=(), classifications=(), schemas=(), rules=(), seeds=(), conclusions=(), decisions=(), ir_limits: 'IRLimits | None' = None) -> 'LinkedBuild' | Select public exports and link their required definitions atomically. |
CompiledLibraryView API
An opened compiled library. Inspect exports, then use CompiledCatalog to select a runnable program. Call verify_all() to check every saved declaration.
CompiledLibrary(data: 'bytes', *, limits: 'LibraryLimits | None' = None)Usage and examples: Compile and import selected knowledge.
| Member | Behavior |
|---|---|
as_dict(self) | Inspect identity and exports without loading their payloads. |
contract_digest | Semantic module identity excluding source comments and file locations. |
digest | Directory identity committing to all payload hashes, without reading them. |
exports | Read-only property. |
format_version | Indexed container format, separate from the author's module version. |
kind | Read-only property. |
module | Read-only property. |
open(path, *, limits=None) | Read just the header and directory of a regular compiled library file. |
to_bytes(self) | Read and verify the complete artifact for explicit copying or saving. |
verify_all(self, *, ir_limits: 'IRLimits | None' = None) | Verify every payload; this deliberately reads the complete library. |
version | Read-only property. |
ConclusionView API
A deduplicated conclusion emitted by the current inference state.
Conclusion(name: 'str', values: 'tuple[tuple[str, Value], ...]', rule: 'str', fact_ids: 'tuple[int, ...]') -> NoneUsage and examples: Multiprocessing.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Value]' | Public method. |
ConditionView API
Public constant or type alias; its value or definition is shown below.
Condition = ting_reasoner.model.Pattern | ting_reasoner.model.TestCondition | ting_reasoner.model.ExistsCondition | ting_reasoner.model.ForAllCondition | ting_reasoner.model.AllCondition | ting_reasoner.model.AnyConditionConstraintView API
A portable field constraint.
Constraint(operator: 'str', operands: 'tuple[Value | BindingRef, ...]' = ()) -> NoneUsage and examples: Rules and conditions.
ConstraintAssessmentView API
Evidence for one requirement; conclusion records preserve proof access.
ConstraintAssessment(requirement: 'str', state: 'EvidenceState', support: 'tuple[Conclusion, ...]' = (), opposition: 'tuple[Conclusion, ...]' = ()) -> NoneUsage and examples: Make and inspect a decision.
decideView API
Run an isolated session and return its bounded decision assessment.
decide(knowledge: 'KnowledgeBase | CompactIR | bytes | bytearray | memoryview', request: 'DecisionRequest', *, facts: 'Iterable[Fact]' = (), budget: 'ExecutionBudget | None' = None, max_firings: 'int | None' = None, limits: 'DecisionLimits | None' = None) -> 'DecisionResult'Usage and examples: Make and inspect a decision.
DECISION_FORMAT_VERSIONView API
Public constant or type alias; its value or definition is shown below.
DECISION_FORMAT_VERSION = 7DECISION_LIBRARY_FORMAT_VERSIONView API
Public constant or type alias; its value or definition is shown below.
DECISION_LIBRARY_FORMAT_VERSION = 2DECISION_POLICYView API
Public constant or type alias; its value or definition is shown below.
DECISION_POLICY = 'unique-feasible-v1'DECISION_SEMANTICS_VERSIONView API
Public constant or type alias; its value or definition is shown below.
DECISION_SEMANTICS_VERSION = 8DecisionDefinitionView API
A named DecisionRequest stored in a KnowledgeBase and its Compact-IR.
DecisionDefinition(name: 'str', request: 'DecisionRequest') -> NoneUsage and examples: Store and import named decisions.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, object]' | Return the name and complete JSON-safe request contract. |
DecisionErrorView API
Invalid request, malformed evidence, or exceeded input-size boundary.
DecisionError(code: 'str', message: 'str', path: 'str' = '') -> 'None'Usage and examples: Make and inspect a decision.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, str]' | Return stable code, diagnostic message and offending field path. |
DecisionLimitsView API
Input and assessment limits, separate from inference ExecutionBudget.
DecisionLimits(max_candidates: 'int' = 4096, max_requirements: 'int' = 256, max_inputs: 'int' = 100000, max_conclusions: 'int' = 100000, max_work: 'int' = 1000000) -> NoneUsage and examples: Make and inspect a decision.
DecisionOutcomeView API
What the evaluation establishes within the requested candidate domain.
DecisionOutcome(*values)Usage and examples: Make and inspect a decision.
DecisionRequestView API
Explicit candidate domain, hard requirements and evidence channel.
DecisionRequest(goal: 'str', candidates: 'tuple[str, ...]', requirements: 'tuple[str, ...]', evidence_name: 'str', closed: 'bool', logical_time: 'int' = 0) -> NoneUsage and examples: Make and inspect a decision.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, object]' | Return the request and its explicit, versioned selection policy. |
digest | SHA-256 of canonical request JSON, including declared ordering. |
DecisionResultView API
Immutable decision plus the exact inference session used to assess it.
DecisionResult(request: 'DecisionRequest', status: 'DecisionStatus', outcome: 'DecisionOutcome', candidates: 'tuple[CandidateAssessment, ...]', feasible_options: 'tuple[str, ...]', selected_option: 'str | None', reason_codes: 'tuple[str, ...]', usage: 'DecisionUsage', limits: 'DecisionLimits', session: 'SessionResult', exhausted_limit: 'str | None' = None) -> NoneUsage and examples: Make and inspect a decision.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, object]' | Export a JSON-safe summary with snapshot conclusion indices. |
snapshot | The immutable facts, conclusions and proofs evaluated by this result. |
DecisionStatusView API
Whether inference and decision assessment both finished.
DecisionStatus(*values)Usage and examples: Make and inspect a decision.
DecisionUsageView API
Assessment work completed before success or exhaustion.
DecisionUsage(conclusions_examined: 'int' = 0, candidates_examined: 'int' = 0, requirement_checks: 'int' = 0) -> NoneUsage and examples: Make and inspect a decision.
| Member | Behavior |
|---|---|
work | Total charged assessment operations. |
DeltaBatchView API
The immutable externally visible result of one transaction commit.
DeltaBatch(base_epoch: 'int', epoch: 'int', added: 'tuple[FactItem, ...]' = (), removed: 'tuple[FactItem, ...]' = (), replacements: 'tuple[ReplacementDelta, ...]' = ()) -> NoneUsage and examples: Transactions.
| Member | Behavior |
|---|---|
changed | Return whether the commit changed the visible fact state. |
deriveView API
Declare a pure derived-fact consequence.
derive(schema: 'FactSchema', /, **values: 'Value | BindingRef') -> 'Action'Usage and examples: Rules and conditions.
dump_rulespecView API
Atomically write canonical RuleSpec source, protecting files by default.
dump_rulespec(spec: 'RuleSpec', path: 'RuleSpecPath', *, limits: 'RuleSpecLimits | None' = None, overwrite: 'bool' = False) -> 'Path'Usage and examples: Portable program files.
emitView API
Declare a typed conclusion consequence (no external side effect).
emit(name: 'str', /, **values: 'Value | BindingRef') -> 'Action'Usage and examples: Rules and conditions.
EngineView API
A reasoning session over a knowledge base or compiled program. Declare input facts, call run(), and inspect conclusions.
Engine(knowledge: 'KnowledgeBase | CompactIR | bytes | bytearray | memoryview', *, firing_limit: 'int' = 10000, optimization: 'OptimizationPolicy | None' = None, maintenance: 'MaintenancePolicy | None' = None) -> 'None'Usage and examples: Run and inspect.
| Member | Behavior |
|---|---|
classification(self, name: 'str') | Look up an exact qualified classification name in a loaded program. |
classifications | Declared nominal types, including their parent definitions. |
compact_ir | Read-only property. |
compact_ir_digest | Read-only property. |
compact_store(self) -> 'StoreMetrics' | Release unused fact storage after many updates. Call outside a transaction; fact values, handles, results and epoch are preserved. |
conclusions | Current emitted Conclusion objects, returned as an immutable tuple. Check the latest run status before treating them as complete. |
declare(self, *facts: 'Fact') -> 'FactHandle | tuple[FactHandle, ...]' | Atomically validate and declare one or more base facts. |
epoch | Monotonic session epoch used by optimistic transactions. |
explain(self, subject: 'FactHandle | Conclusion | int | str | None' = None) -> 'tuple[Provenance, ...]' | Return proof edges for all subjects or one fact/conclusion subject. |
fact_items | Return handles and immutable API facts in deterministic store order. |
facts | Current input, fixed and derived Fact objects, returned as an immutable tuple. |
maintenance_report | Last committed support-maintenance report; no-op updates leave it intact. |
match_metrics | Return matching counters from the most recent run call. |
optimization_metrics | Return optional-index work and pruning counts for the latest run. |
provenance | Recorded rule proofs for current derived facts and conclusions. |
replace(self, handle: 'FactHandle | int', fact: 'Fact') -> 'FactHandle' | Atomically replace a base fact, normally preserving its handle. |
reset(self, *facts: 'Fact') -> 'tuple[FactHandle, ...]' | Start a new case, retain fixed seeds, and optionally declare new inputs. Discard previous handles and call run() to infer results. |
retract(self, handle: 'FactHandle | int') -> 'bool' | Retract a base fact by handle; return False if it is absent. |
run(self, *, max_firings: 'int | None' = None, budget: 'ExecutionBudget | None' = None) -> 'RunResult' | Run deterministically to quiescence or a clean firing-budget boundary. |
schema(self, name: 'str') -> 'FactSchema' | Get an exact schema name from a loaded program without its source files. |
schemas | Read-only schema facades reconstructed from the verified plan. |
seed_facts | Return the program's named immutable facts, including equal named aliases. |
seed_handle(self, name: 'str') -> 'FactHandle' | Get the protected fact handle for an exact qualified seed name. |
seed_names_for(self, handle: 'FactHandle | int') -> 'tuple[str, ...]' | Identify named program knowledge used by a provenance fact handle. |
snapshot(self) -> 'EngineSnapshot' | Return one immutable, epoch-consistent public session view. |
store_metrics | Return deterministic physical row/chunk counts for this session. |
trace | Recorded events for the current inference state. A committed input change clears this trace. |
transaction(self, *, expected_epoch: 'int | None' = None) -> 'Transaction' | Open an optimistic transaction over a private base-fact projection. |
EngineErrorView API
An operation is invalid for the current engine session.
EngineErrorEngineSnapshotView API
Immutable, epoch-labelled public view of an engine session.
EngineSnapshot(epoch: 'int', fact_items: 'tuple[FactItem, ...]', conclusions: 'tuple[Conclusion, ...]', trace: 'tuple[TraceEvent, ...]', provenance: 'tuple[Provenance, ...]', conclusion_provenance: 'tuple[tuple[Conclusion, tuple[Provenance, ...]], ...]' = (), seed_names: 'tuple[tuple[str, FactHandle], ...]' = (), program_digest: 'str | None' = None) -> None| Member | Behavior |
|---|---|
explain(self, subject: 'FactHandle | Conclusion | int | str | None' = None) -> 'tuple[Provenance, ...]' | Return proof edges using the same subject forms as Engine.explain. |
facts | Read-only property. |
seed_names_for(self, handle: 'FactHandle | int') -> 'tuple[str, ...]' | Return the names of immutable program facts supporting this handle. |
eqView API
Match an exactly equal value with the same type, or the value of an established binding.
eq(value: 'Value | BindingRef') -> 'Constraint'Usage and examples: Rules and conditions.
EvidenceStateView API
Positive support and explicit opposition for one requirement.
EvidenceState(*values)Usage and examples: Make and inspect a decision.
ExecutionBudgetView API
Deterministic per-call work ceilings checked before each charged unit.
ExecutionBudget(max_candidate_rows: 'int | None' = None, max_constraint_evaluations: 'int | None' = None, max_activations: 'int | None' = None, max_firings: 'int | None' = None) -> NoneUsage and examples: Work limits.
ExecutionPolicyView API
Choose inline, owned-process, or borrowed-executor batch execution.
ExecutionPolicy(mode: 'str' = 'inline', max_workers: 'int' = 2, max_in_flight: 'int' = 4, chunk_size: 'int' = 1, minimum_parallel_facts: 'int' = 256, start_method: 'str' = 'spawn', max_jobs: 'int' = 100000) -> NoneUsage and examples: Multiprocessing.
existsView API
Succeed when at least one row matches pattern.
exists(pattern: 'Pattern') -> 'ExistsCondition'Usage and examples: Rules and conditions.
ExistsConditionView API
Existential row test over one schema without exporting bindings.
ExistsCondition(pattern: 'Pattern', negated: 'bool' = False) -> NoneUsage and examples: Rules and conditions.
ExplanationErrorView API
Source explanation failed; code, message and hint describe the remedy.
ExplanationError(code: 'str', message: 'str', hint: 'str') -> 'None'Usage and examples: Explain a conclusion.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, str]' | Return a detached error record suitable for JSON or application logs. |
ExplanationLimitsView API
Positive record-count limits for source explanation construction.
ExplanationLimits(max_records: 'int' = 100000, max_proofs: 'int' = 1000, max_supports: 'int' = 10000, max_statements: 'int' = 10000) -> NoneUsage and examples: Explain a conclusion.
FactView API
An immutable record with a FactSchema and matching field values. Create it by calling the schema, then pass it to Engine.declare().
Fact(schema: 'FactSchema', values: 'tuple[Value, ...]') -> NoneUsage and examples: Facts and types.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Value]' | Materialize a convenience mapping at the API boundary. |
items(self) -> 'Iterator[tuple[str, Value]]' | Public method. |
FactHandleView API
FactSchemaView API
An immutable ordered scalar and fixed-shape ND fact schema.
FactSchema(name: 'str', fields: 'Mapping[str, type | tuple[type, ...]] | Sequence[Field | NDField] | None' = None, /, **field_types: 'type | tuple[type, ...]') -> 'None'Usage and examples: Facts and types.
| Member | Behavior |
|---|---|
field_names | Read-only property. |
pattern(self, **constraints: 'Any') -> 'Pattern' | Create a positive typed pattern; call .not_() to negate it. |
FactSupportView API
A present fact with its schema declaration and any fixed-knowledge names.
FactSupport(handle: 'FactHandle', fact: 'Fact', schema: 'SourceOrigin', seeds: 'tuple[SourceOrigin, ...]' = ()) -> NoneUsage and examples: Explain a conclusion.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return JSON-safe fact values and source declarations. |
FieldView API
A named scalar field in a FactSchema.
Field(name: 'str', type_: 'type | tuple[type, ...]') -> 'None'Usage and examples: Facts and types.
| Member | Behavior |
|---|---|
accepts(self, value: 'Value') -> 'bool' | Return whether value has one of the field's exact declared types. |
FileBuildResultView API
One batch receipt; retains identities and errors, not full programs or test facts.
FileBuildResult(source: 'str', output: 'str', digest: 'str | None', diagnostics: 'tuple[SourceDiagnostic, ...]' = ()) -> NoneUsage and examples: Compile readable source.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return a JSON-safe per-file receipt. |
ok | True only when this file was compiled and successfully published. |
FileResolverView API
Resolve explicit module-name to file mappings; no directory or network search.
FileResolver(modules: 'Mapping[str, str | os.PathLike[str]]', *, limits: 'SourceLimits | None' = None) -> 'None'Usage and examples: Compile readable source.
| Member | Behavior |
|---|---|
resolve(self, module: 'str') -> 'SourceSnapshot' | Read only a specifically mapped module under the source byte limit. |
for_allView API
Succeed when every row of the pattern schema matches pattern.
for_all(pattern: 'Pattern') -> 'ForAllCondition'Usage and examples: Rules and conditions.
ForAllConditionView API
Require every fact of the selected schema to match the pattern. Succeeds when that schema has no facts.
ForAllCondition(pattern: 'Pattern', negated: 'bool' = False) -> NoneUsage and examples: Rules and conditions.
format_sourceView API
Preserve statements and comments, normalize line endings and final newline.
format_source(source: 'str | SourceSnapshot | SourceDocument', *, limits: 'SourceLimits | None' = None) -> 'str'Usage and examples: Write Ting rules.
FORMAT_VERSIONView API
Public constant or type alias; its value or definition is shown below.
FORMAT_VERSION = 4geView API
Match a value greater than or equal to the operand. Values must have matching, orderable types.
ge(value: 'Value | BindingRef') -> 'Constraint'Usage and examples: Rules and conditions.
gtView API
Match a value greater than the operand. Values must have matching, orderable types.
gt(value: 'Value | BindingRef') -> 'Constraint'Usage and examples: Rules and conditions.
HistoryCorruptionErrorView API
Stored history bytes do not match their immutable identities.
HistoryCorruptionErrorUsage and examples: Portable program files.
HistoryEntryView API
One committed generation plus its immutable checkpoint core.
HistoryEntry(generation: 'int', revision: 'str', parent: 'str | None', restored_from: 'str | None', timestamp: 'str', message: 'str', operation: 'str', source_digest: 'str', semantic_digest: 'str', artifact_digest: 'str') -> NoneUsage and examples: Portable program files.
| Member | Behavior |
|---|---|
created_at | Read-only property. |
id | Read-only property. |
HistoryErrorView API
A history request or store contract is invalid.
HistoryErrorUsage and examples: Portable program files.
HistoryExportErrorView API
One or both requested history outputs could not be published.
HistoryExportError(source_path: 'Path', artifact_path: 'Path', *, source_available: 'bool', artifact_available: 'bool') -> 'None'Usage and examples: Portable program files.
HistoryLimitsView API
Resource ceilings for source capture, tests, and history traversal.
HistoryLimits(rulespec_limits: 'RuleSpecLimits' = RuleSpecLimits(max_source_bytes=33554432, max_depth=64, max_collection_items=100000, max_total_nodes=1000000, max_string_bytes=16777216, max_integer_digits=128, max_test_cases=1000, max_facts_per_test=10000), ir_limits: 'IRLimits' = IRLimits(max_total_bytes=16777216, max_body_bytes=8388608, max_integer_digits=128, max_symbols=100000, max_symbol_bytes=4096, max_constants=250000, max_schemas=4096, max_fields_per_schema=4096, max_rules=100000, max_patterns_per_rule=1024, max_constraints_per_pattern=4096, max_operands_per_constraint=4096, max_actions_per_rule=1024, max_values_per_action=4096, max_bindings_per_rule=4096, max_instruction_fuel=5000000, max_nd_rank=8, max_nd_dimension=1000000, max_nd_elements=1000000, max_nd_bytes=8388608, max_seed_facts=65536, max_seed_values=1000000, max_classifications=4096, max_classification_edges=16384, max_classification_depth=64, max_decisions=4096, max_decision_items=100000), max_source_bytes: 'int' = 33554432, max_manifest_bytes: 'int' = 1048576, max_generation_bytes: 'int' = 262144, max_test_report_bytes: 'int' = 4194304, max_history_entries: 'int' = 100000, max_message_bytes: 'int' = 16384, max_test_cases: 'int' = 1000, test_budget: 'ExecutionBudget' = ExecutionBudget(max_candidate_rows=1000000, max_constraint_evaluations=1000000, max_activations=100000, max_firings=10000)) -> NoneUsage and examples: Portable program files.
HistoryRestoreExportErrorView API
A restore revision committed, but its requested export was incomplete.
HistoryRestoreExportError(entry: 'HistoryEntry', source_path: 'Path', artifact_path: 'Path', *, source_available: 'bool', artifact_available: 'bool') -> 'None'Usage and examples: Portable program files.
HistoryStoreView API
A content-addressed, append-only RuleSpec history directory.
HistoryStore(root: 'HistoryPath', *, limits: 'HistoryLimits | None' = None) -> 'None'Usage and examples: Portable program files.
| Member | Behavior |
|---|---|
checkpoint(self, source_path: 'HistoryPath', message: 'str' = '', expected_head: 'str | None' = None) -> 'HistoryEntry' | Validate, test, compile, and append an exact source checkpoint. |
checkpoint_source(self, source_path: 'HistoryPath', message: 'str' = '', expected_head: 'str | None' = None) -> 'HistoryEntry' | Validate, test, compile, and append an exact source checkpoint. |
export(self, selector: 'RevisionSelector', source_path: 'HistoryPath', artifact_path: 'HistoryPath', overwrite: 'bool' = False) -> 'CheckpointRevision' | Export exact retained source and artifact without changing history. |
head | Return the current full revision ID, or None for an empty store. |
list(self) -> 'tuple[HistoryEntry, ...]' | Return committed generations newest first after verifying the chain. |
list_history(self) -> 'tuple[HistoryEntry, ...]' | Return committed generations newest first after verifying the chain. |
resolve(self, selector: 'RevisionSelector') -> 'HistoryEntry' | Resolve HEAD, a generation, or an unambiguous revision prefix. |
restore(self, selector: 'RevisionSelector', source_path: 'HistoryPath', artifact_path: 'HistoryPath', message: 'str' = '', overwrite: 'bool' = False, expected_head: 'str | None' = None) -> 'RestoreResult' | Append a rebuilt forward revision and export its source/artifact. |
restore_revision(self, selector: 'RevisionSelector', message: 'str' = '', expected_head: 'str | None' = None) -> 'HistoryEntry' | Verify retained source and append its rebuilt forward revision. |
verify_revision(self, selector: 'RevisionSelector') -> 'CheckpointRevision' | Fully verify one manifest and every source/artifact object it names. |
HistoryTestFailureView API
Embedded RuleSpec tests failed before a checkpoint could be published.
HistoryTestFailure(failed_count: 'int', report: 'Mapping[str, Any]') -> 'None'Usage and examples: Portable program files.
ImmutableKnowledgeErrorView API
A session operation attempted to change a compiled seed fact.
ImmutableKnowledgeError(handle_id: 'int', names: 'tuple[str, ...]') -> 'None'| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, object]' | Return the error code, protected handle and seed names. |
InferenceJobView API
Immutable facts and per-session run limits for one independent job.
InferenceJob(facts: 'tuple[Fact, ...]', max_firings: 'int | None' = None, budget: 'ExecutionBudget | None' = None) -> NoneUsage and examples: Multiprocessing.
IRLimitsView API
Hard ceilings applied before a Compact-IR artifact becomes executable.
IRLimits(max_total_bytes: 'int' = 16777216, max_body_bytes: 'int' = 8388608, max_integer_digits: 'int' = 128, max_symbols: 'int' = 100000, max_symbol_bytes: 'int' = 4096, max_constants: 'int' = 250000, max_schemas: 'int' = 4096, max_fields_per_schema: 'int' = 4096, max_rules: 'int' = 100000, max_patterns_per_rule: 'int' = 1024, max_constraints_per_pattern: 'int' = 4096, max_operands_per_constraint: 'int' = 4096, max_actions_per_rule: 'int' = 1024, max_values_per_action: 'int' = 4096, max_bindings_per_rule: 'int' = 4096, max_instruction_fuel: 'int' = 5000000, max_nd_rank: 'int' = 8, max_nd_dimension: 'int' = 1000000, max_nd_elements: 'int' = 1000000, max_nd_bytes: 'int' = 8388608, max_seed_facts: 'int' = 65536, max_seed_values: 'int' = 1000000, max_classifications: 'int' = 4096, max_classification_edges: 'int' = 16384, max_classification_depth: 'int' = 64, max_decisions: 'int' = 4096, max_decision_items: 'int' = 100000) -> NoneUsage and examples: Reuse a compiled program.
IRValidationErrorView API
Compact-IR is malformed, unsupported, non-canonical, or over budget.
IRValidationErroriter_batchView API
Stream independent results in input order with bounded prefetch.
iter_batch(knowledge: 'KnowledgeBase | CompactIR | bytes | bytearray | memoryview', jobs: 'Iterable[InferenceJob]', *, policy: 'ExecutionPolicy | None' = None, executor: 'Executor | None' = None, parallel_budget: 'ParallelBudget | None' = None, cancel: 'Any' = None, optimization: 'OptimizationPolicy | None' = None, firing_limit: 'int' = 10000) -> 'Iterator[SessionResult]'Usage and examples: Multiprocessing.
KnowledgeBaseView API
Immutable schemas, rules, named facts and decisions ready for compilation.
KnowledgeBase(schemas: 'Iterable[FactSchema]' = (), rules: 'Iterable[Rule]' = (), *, seeds: 'Iterable[SeedFact]' = (), classifications: 'Iterable' = (), decisions: 'Iterable' = ()) -> 'None'Usage and examples: Rules and conditions.
| Member | Behavior |
|---|---|
compile(self) -> 'bytes' | Compile this knowledge base to canonical verified .tingir bytes. |
leView API
Match a value less than or equal to the operand. Values must have matching, orderable types.
le(value: 'Value | BindingRef') -> 'Constraint'Usage and examples: Rules and conditions.
LIBRARY_FORMAT_VERSIONView API
Public constant or type alias; its value or definition is shown below.
LIBRARY_FORMAT_VERSION = 1LIBRARY_MAGICView API
Public constant or type alias; its value or definition is shown below.
LIBRARY_MAGIC = b'tlib'LibraryBuildView API
Compilation result for a source header declaring role library.
LibraryBuild(library: 'CompiledLibrary', analysis: 'Any', tests: 'Any', output: 'Path | None' = None) -> NoneUsage and examples: Compile and import selected knowledge.
| Member | Behavior |
|---|---|
as_dict(self) | Inspect the library, output path and embedded test report. |
classification(self, name) | Find a source-visible classification before selecting compiled exports. |
decision(self, name) | Get a source-visible request before selecting compiled exports. |
digest | Read-only property. |
kind | Read-only property. |
schema(self, name) | Find a source-visible schema before selecting compiled exports. |
LibraryErrorView API
A compiled library could not be read, selected, verified or linked.
LibraryError(code: 'str', message: 'str', hint: 'str', chain: 'tuple[str, ...]' = ())Usage and examples: Compile and import selected knowledge.
| Member | Behavior |
|---|---|
as_dict(self) | A detached JSON-safe diagnostic, including its dependency chain. |
LibraryExportView API
One public declaration in a compiled library's verified directory.
LibraryExport(kind: 'str', name: 'str', qualified_name: 'str') -> NoneUsage and examples: Compile and import selected knowledge.
LibraryLimitsView API
Finite file, directory, dependency and selected-payload ceilings.
LibraryLimits(max_file_bytes: 'int' = 134217728, max_index_bytes: 'int' = 4194304, max_section_bytes: 'int' = 20971520, max_sections: 'int' = 4096, max_modules: 'int' = 64, max_selected_bytes: 'int' = 67108864, max_build_bytes: 'int' = 134217728, max_dependencies: 'int' = 100000, max_dependency_depth: 'int' = 128, max_source_nodes: 'int' = 100000) -> NoneUsage and examples: Compile and import selected knowledge.
LinkedBuildView API
One verified executable assembled from selected compiled exports.
LinkedBuild(program: 'CompactIR', knowledge_base: 'KnowledgeBase', analysis: 'LinkedSourceMap', imports: 'tuple[tuple[str, str, str], ...]', usage: 'LinkUsage') -> NoneUsage and examples: Compile and import selected knowledge.
| Member | Behavior |
|---|---|
as_dict(self) | Inspect linked identity, selected definitions and measured I/O. |
classification(self, name: 'str') | Find an exact qualified nominal type and its parent definitions. |
decision(self, name: 'str') | Get an exact qualified request from the selected compiled program. |
digest | Read-only property. |
explain(self, session, subject=None, *, limits=None) | Explain a matching Engine or snapshot using the retained source map. |
kind | Read-only property. |
origin_for(self, name: 'str', *, kind: 'str' = 'rule') | Public method. |
schema(self, name: 'str') | Find an exact qualified schema; imported aliases are not guessed. |
LinkedSourceMapView API
Selected declaration fragments and exact original source identities.
LinkedSourceMap(program_digest: 'str', semantic_digest: 'str', origins: 'tuple[SourceOrigin, ...]', nodes: 'tuple[SourceNode, ...]', source_identities: 'tuple[tuple[str, str], ...]') -> NoneUsage and examples: Compile and import selected knowledge.
| Member | Behavior |
|---|---|
node(self, node_id: 'str') | Read a selected source statement retained in the compiled library. |
origin_for(self, name: 'str', *, kind: 'str' = 'rule') | Locate an imported declaration by qualified name. |
LinkUsageView API
Logical I/O counts for one link; cached directories incur no new disk read.
LinkUsage(modules: 'int', sections: 'int', directory_bytes: 'int', section_bytes: 'int') -> NoneUsage and examples: Compile and import selected knowledge.
load_rulespecView API
Read one bounded regular file and parse its declarative RuleSpec.
load_rulespec(path: 'RuleSpecPath', *, limits: 'RuleSpecLimits | None' = None) -> 'RuleSpec'Usage and examples: Portable program files.
load_tingView API
Load a compiled binary program. For readable .ting source use compile_file(); for compiled .tingir programs or libraries use load_tingir().
load_ting(path: 'TingPath', *, limits: 'IRLimits | None' = None) -> 'CompactIR'Usage and examples: Portable program files.
load_tingirView API
Identify the binary header and load a program or compiled library directory.
load_tingir(path: 'str | os.PathLike[str]', *, limits: 'IRLimits | None' = None, library_limits: 'LibraryLimits | None' = None) -> 'CompactIR | CompiledLibrary'Usage and examples: Compile readable source.
ltView API
Match a value less than the operand. Values must have matching, orderable types.
lt(value: 'Value | BindingRef') -> 'Constraint'Usage and examples: Rules and conditions.
MaintenanceLimitErrorView API
A strict maintenance limit rejected an update; the Engine is unchanged.
MaintenanceLimitError(limit: 'str', maximum: 'int', used: 'int')Usage and examples: Keep valid results when inputs change.
| Member | Behavior |
|---|---|
as_dict(self) | Return a detached JSON-safe diagnostic. |
MaintenancePolicyView API
Opt in to grounded positive-rule support retention on base updates.
MaintenancePolicy(enabled: 'bool' = True, max_facts: 'int' = 100000, max_proofs: 'int' = 200000, max_supports: 'int' = 1000000, max_work: 'int' = 4000000, max_changes: 'int' = 1000, on_limit: 'str' = 'recompute') -> NoneUsage and examples: Keep valid results when inputs change.
MaintenanceReportView API
Latest committed base update; reset/fallback reports contain no changes.
MaintenanceReport(mode: 'str', reason: 'str', retained_facts: 'int' = 0, withdrawn_facts: 'int' = 0, retained_conclusions: 'int' = 0, withdrawn_conclusions: 'int' = 0, retained_proofs: 'int' = 0, removed_proofs: 'int' = 0, work: 'int' = 0, changes: 'tuple[SupportChange, ...]' = (), changes_truncated: 'bool' = False) -> NoneUsage and examples: Keep valid results when inputs change.
MatchMetricsView API
Read-only counters for matching work performed by the latest run.
MatchMetrics(index_probes: 'int' = 0, indexed_rows: 'int' = 0, full_scan_rows: 'int' = 0, constraint_evaluations: 'int' = 0, activations_generated: 'int' = 0) -> NoneUsage and examples: Multiprocessing.
MemoryResolverView API
Resolve only the supplied module-name to text/snapshot mapping.
MemoryResolver(modules: 'Mapping[str, str | SourceSnapshot]') -> 'None'Usage and examples: Compile readable source.
| Member | Behavior |
|---|---|
resolve(self, module: 'str') -> 'SourceSnapshot' | Resolve a named in-memory module; never consult files or a network. |
NDDTypeView API
Supported array element types: bool, signed 64-bit i64, and finite 64-bit f64.
NDDType(*values)Usage and examples: Arrays.
NDFieldView API
A named, fixed-shape multidimensional field in a FactSchema.
NDField(name: 'str', dtype: 'NDDType | str', shape: 'Iterable[int]') -> 'None'Usage and examples: Rules and conditions.
| Member | Behavior |
|---|---|
accepts(self, value: 'Value') -> 'bool' | Return whether value has this field's exact dtype and shape. |
types | Expose the value class for schema-introspection compatibility. |
NDScalarView API
Public constant or type alias; its value or definition is shown below.
NDScalar = bool | int | floatNDValueView API
An immutable, bounded, canonical C-order multidimensional value.
NDValue() -> 'None'Usage and examples: Arrays.
| Member | Behavior |
|---|---|
at(self, *coords: 'int') -> 'NDScalar' | Return the element at exact non-negative coordinates. |
buffer | A new read-only byte view of the canonical storage. |
digest | SHA-256 hex digest of the dtype, shape, and canonical bytes. |
dtype | Read-only property. |
from_bytes(data: 'object', *, dtype: 'NDDType | str', shape: 'object') -> 'NDValue' | Build from canonical little-endian bytes, copying mutable input. |
from_flat(values: 'object', *, dtype: 'NDDType | str', shape: 'object') -> 'NDValue' | Build from exactly product(shape) typed values in C order. |
iter_flat(self) -> 'Iterator[NDScalar]' | Iterate typed elements in canonical C order. |
nbytes | Read-only property. |
rank | Read-only property. |
shape | Read-only property. |
size | Read-only property. |
strides | Canonical C-order byte strides. |
to_flat_tuple(self) -> 'tuple[NDScalar, ...]' | Return all elements as a flat immutable tuple in C order. |
tobytes(self) -> 'bytes' | Return immutable canonical little-endian storage. |
neView API
Match a value that differs by type or exact value from the supplied operand.
ne(value: 'Value | BindingRef') -> 'Constraint'Usage and examples: Rules and conditions.
new_rulespecView API
Create a canonical empty project for a builder init command.
new_rulespec(name: 'str', description: 'str' = '') -> 'RuleSpec'Usage and examples: Portable program files.
normalized_source_viewView API
Return a detached, source-normalized document suitable for structural diff.
normalized_source_view(spec: 'RuleSpec') -> 'dict[str, Any]'Usage and examples: Portable program files.
not_View API
Negate a declarative condition. See Rules and conditions for binding and negation requirements.
not_(condition: 'Condition') -> 'Condition'Usage and examples: Rules and conditions.
one_ofView API
Match any one of the supplied portable values.
one_of(*values: 'Value | BindingRef') -> 'Constraint'Usage and examples: Rules and conditions.
OptimizationMetricsView API
Latest-run physical work, separate from deterministic logical budgets.
OptimizationMetrics(index_builds: 'int' = 0, index_hits: 'int' = 0, build_rows: 'int' = 0, pruned_rows: 'int' = 0, empty_branches: 'int' = 0, fallbacks: 'int' = 0, cached_cells: 'int' = 0) -> NoneUsage and examples: Pruning and performance.
OptimizationPolicyView API
Control optional candidate pruning, with exact scan fallback.
OptimizationPolicy(enabled: 'bool' = True, minimum_rows: 'int' = 32, max_indexes: 'int' = 32, max_cache_cells: 'int' = 1048576, max_build_work: 'int' = 4194304) -> NoneUsage and examples: Pruning and performance.
ParallelBudgetView API
Caller-owned nonblocking submission permits shared by batch calls.
ParallelBudget(capacity: 'int') -> 'None'Usage and examples: Multiprocessing.
| Member | Behavior |
|---|---|
active | Read-only property. |
capacity | Read-only property. |
parse_rulespecView API
Parse bounded declarative JSON and construct a validated RuleSpec.
parse_rulespec(source: 'RuleSpecSource', *, limits: 'RuleSpecLimits | None' = None) -> 'RuleSpec'Usage and examples: Portable program files.
parse_sourceView API
Parse readable .ting text, retaining exact text, comments and locations.
parse_source(source: 'str | SourceSnapshot', *, uri: 'str' = '<memory>', limits: 'SourceLimits | None' = None) -> 'SourceDocument'Usage and examples: Write Ting rules.
PatternView API
A schema and field constraints used in a rule condition. Create one with schema.pattern(...).
Pattern(schema: 'FactSchema', constraints: 'tuple[tuple[int, Constraint], ...]', negated: 'bool' = False) -> NoneUsage and examples: Rules and conditions.
| Member | Behavior |
|---|---|
not_(self) -> 'Pattern' | Return a copy with its negation flag toggled. |
ProvenanceView API
A compact proof edge from a conclusion or fact to its support.
Provenance(subject_kind: 'str', subject: 'int | str', rule: 'str', fact_ids: 'tuple[int, ...]', bindings: 'tuple[tuple[str, Value], ...]') -> NoneUsage and examples: Multiprocessing.
read_sourceView API
Read strict UTF-8 source into an immutable snapshot under a byte limit.
read_source(path: 'str | os.PathLike[str]', *, limits: 'SourceLimits | None' = None) -> 'SourceSnapshot'Usage and examples: Compile readable source.
ReplacementDeltaView API
One atomic committed replacement and its optional changed-field mask.
ReplacementDelta(before_handle: 'FactHandle', before_fact: 'Fact', after_handle: 'FactHandle', after_fact: 'Fact', changed_mask: 'int | None' = None) -> NoneUsage and examples: Transactions.
RestoreResultView API
Result of a verified forward restore and its two exported files.
RestoreResult(entry: 'HistoryEntry', source_path: 'Path', artifact_path: 'Path') -> NoneUsage and examples: Portable program files.
| Member | Behavior |
|---|---|
generation | Read-only property. |
revision | Read-only property. |
RuleView API
A deterministic Boolean condition and pure consequences.
Rule(name: 'str', when: 'Iterable[Pattern] | Condition', then: 'Iterable[Action]', *, salience: 'int' = 0, namespace: 'str' = 'default', version: 'str' = '1') -> 'None'Usage and examples: Rules and conditions.
| Member | Behavior |
|---|---|
qualified_name | Read-only property. |
RuleErrorView API
A rule, pattern, constraint, or action is invalid.
RuleErrorRuleSpecView API
An immutable editable project plus executable authoring model and tests.
RuleSpec(project_name: 'str', description: 'str', knowledge_base: 'KnowledgeBase', tests: 'tuple[RuleSpecTestCase, ...]' = ()) -> NoneUsage and examples: Portable program files.
| Member | Behavior |
|---|---|
canonical_bytes | Canonical editable source using the default RuleSpec limits. |
digest | SHA-256 of :attr:`canonical_bytes`. |
semantic_view | A detached normalized executable-semantic document. |
test(self, *, ir_limits: 'IRLimits | None' = None, budget: 'ExecutionBudget | None' = None, rulespec_limits: 'RuleSpecLimits | None' = None) -> 'RuleSpecTestReport' | Run embedded cases under an explicit finite work budget. |
RULESPEC_FORMATView API
Public constant or type alias; its value or definition is shown below.
RULESPEC_FORMAT = 'ting-rulespec'RULESPEC_FORMAT_VERSIONView API
Public constant or type alias; its value or definition is shown below.
RULESPEC_FORMAT_VERSION = 1RuleSpecConclusionExpectationView API
Expected conclusion identity, excluding proof- and session-local IDs.
RuleSpecConclusionExpectation(name: 'str', values: 'tuple[tuple[str, Value], ...]' = ()) -> NoneUsage and examples: Portable program files.
RuleSpecErrorView API
A RuleSpec source or object violates the bounded format contract.
RuleSpecErrorUsage and examples: Portable program files.
RuleSpecLimitsView API
Resource ceilings applied before RuleSpec data becomes an object graph.
RuleSpecLimits(max_source_bytes: 'int' = 33554432, max_depth: 'int' = 64, max_collection_items: 'int' = 100000, max_total_nodes: 'int' = 1000000, max_string_bytes: 'int' = 16777216, max_integer_digits: 'int' = 128, max_test_cases: 'int' = 1000, max_facts_per_test: 'int' = 10000) -> NoneUsage and examples: Portable program files.
RuleSpecTestCaseView API
One isolated deterministic engine scenario embedded in source.
RuleSpecTestCase(name: 'str', facts: 'tuple[Fact, ...]' = (), expected_conclusions: 'tuple[RuleSpecConclusionExpectation, ...] | None' = None, expected_facts: 'tuple[Fact, ...] | None' = None, expected_status: 'str | None' = 'quiescent', expected_firings: 'int | None' = None) -> NoneUsage and examples: Portable program files.
RuleSpecTestReportView API
Aggregate result for all tests in one RuleSpec project.
RuleSpecTestReport(results: 'tuple[RuleSpecTestResult, ...]', budget: 'ExecutionBudget') -> NoneUsage and examples: Portable program files.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return a deterministic JSON-compatible report view. |
failed_count | Read-only property. |
passed | Read-only property. |
passed_count | Read-only property. |
total | Read-only property. |
RuleSpecTestResultView API
Deterministic structured outcome of one embedded test.
RuleSpecTestResult(name: 'str', passed: 'bool', status: 'str', firings: 'int', failures: 'tuple[str, ...]', facts: 'tuple[Fact, ...]', conclusions: 'tuple[Conclusion, ...]', budget_usage: 'BudgetUsage', exhausted_budget: 'str | None' = None) -> NoneUsage and examples: Portable program files.
run_batchView API
Run a finite batch and return all results only if every job succeeds.
run_batch(knowledge: 'KnowledgeBase | CompactIR | bytes | bytearray | memoryview', jobs: 'Iterable[InferenceJob]', *, policy: 'ExecutionPolicy | None' = None, executor: 'Executor | None' = None, parallel_budget: 'ParallelBudget | None' = None, cancel: 'Any' = None, optimization: 'OptimizationPolicy | None' = None, firing_limit: 'int' = 10000) -> 'BatchResult'Usage and examples: Multiprocessing.
run_rulespec_testsView API
Compile once and run every embedded case in a fresh deterministic engine.
run_rulespec_tests(spec: 'RuleSpec', *, ir_limits: 'IRLimits | None' = None, budget: 'ExecutionBudget | None' = None, rulespec_limits: 'RuleSpecLimits | None' = None) -> 'RuleSpecTestReport'Usage and examples: Portable program files.
RunResultView API
Bounded outcome of one engine run.
RunResult(status: 'RunStatus', firings: 'int', derived_facts: 'int', conclusions: 'int', trace_events: 'int', budget_usage: 'BudgetUsage' = BudgetUsage(candidate_rows=0, constraint_evaluations=0, activations=0, firings=0), exhausted_budget: 'str | None' = None) -> NoneUsage and examples: Work limits.
| Member | Behavior |
|---|---|
firing_count | Read-only property. |
RunStatusView API
RuntimePlanView API
The read-only execution plan associated with a verified CompactIR. Applications normally reuse the CompactIR with Engine rather than constructing a plan directly.
RuntimePlan(digest: 'str', payload_digest: 'str', symbols: 'tuple[str, ...]', constants: 'tuple[Any, ...]', schemas: 'tuple[RuntimeSchema, ...]', rules: 'tuple[RuntimeRule, ...]', instruction_fuel: 'int', seeds: 'tuple[RuntimeSeed, ...]' = (), format_version: 'int' = 4, semantics_version: 'int' = 5, classifications: 'tuple[RuntimeClassification, ...]' = (), decisions: 'tuple[DecisionDefinition, ...]' = ()) -> NoneUsage and examples: Reuse a compiled program.
save_rulespecView API
Compatibility spelling for dump_rulespec.
save_rulespec(spec: 'RuleSpec', path: 'RuleSpecPath', *, limits: 'RuleSpecLimits | None' = None, overwrite: 'bool' = False) -> 'Path'Usage and examples: Portable program files.
save_tingView API
Validate and atomically save canonical Compact-IR as a .tingir file.
save_ting(source: 'TingArtifactSource', path: 'TingPath', *, limits: 'IRLimits | None' = None, overwrite: 'bool' = False) -> 'Path'Usage and examples: Portable program files.
save_tingirView API
Verify and atomically publish a compiled .tingir; protect existing files by default.
save_tingir(program: 'KnowledgeBase | CompactIR | CompiledLibrary | bytes | bytearray | memoryview', path: 'str | os.PathLike[str]', *, overwrite: 'bool' = False, limits: 'IRLimits | None' = None) -> 'Path'Usage and examples: Compile readable source.
SchemaErrorView API
A fact schema or fact value is invalid.
SchemaErrorSEED_FORMAT_VERSIONView API
Public constant or type alias; its value or definition is shown below.
SEED_FORMAT_VERSION = 5SEED_SEMANTICS_VERSIONView API
Public constant or type alias; its value or definition is shown below.
SEED_SEMANTICS_VERSION = 6SeedFactView API
A named fact included as immutable knowledge in a compiled program.
SeedFact(name: 'str', fact: 'Fact') -> NoneUsage and examples: Rules and conditions.
semantic_digestView API
Return SHA-256 of normalized executable RuleSpec semantics.
semantic_digest(spec: 'RuleSpec') -> 'str'Usage and examples: Portable program files.
semantic_viewView API
Return normalized executable semantics, excluding prose and test scenarios.
semantic_view(spec: 'RuleSpec') -> 'dict[str, Any]'Usage and examples: Portable program files.
SEMANTICS_VERSIONView API
Public constant or type alias; its value or definition is shown below.
SEMANTICS_VERSION = 5SessionResultView API
One ordered job outcome with complete facts, trace and explanations.
SessionResult(sequence: 'int', run: 'RunResult', snapshot: 'EngineSnapshot', match_metrics: 'MatchMetrics', optimization_metrics: 'OptimizationMetrics') -> NoneUsage and examples: Multiprocessing.
source_digestView API
Return SHA-256 of the exact canonical editable source bytes.
source_digest(spec: 'RuleSpec', *, limits: 'RuleSpecLimits | None' = None) -> 'str'Usage and examples: Portable program files.
SOURCE_FEATURESView API
Public constant or type alias; its value or definition is shown below.
SOURCE_FEATURES = frozenset({'arrays', 'seed-facts', 'classifications', 'rules-core', 'decisions'})SOURCE_FORMAT_VERSIONView API
Public constant or type alias; its value or definition is shown below.
SOURCE_FORMAT_VERSION = 1SOURCE_SEMANTICS_VERSIONView API
Public constant or type alias; its value or definition is shown below.
SOURCE_SEMANTICS_VERSION = 1SourceAnalysisView API
Validated linked source, typed authoring model, identities and locations.
SourceAnalysis(root: 'SourceModule', modules: 'tuple[SourceModule, ...]', knowledge_base: 'KnowledgeBase', test_spec: 'RuleSpec', semantic_digest: 'str', origins: 'tuple[SourceOrigin, ...]', program_digest: 'str' = '') -> NoneUsage and examples: Compile readable source.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return linked identities and the normalized public authoring model. |
classification(self, name: 'str') -> 'Classification' | Find a root-visible classification or an exact qualified name. |
decision(self, name: 'str') -> 'DecisionRequest' | Get a root-visible decision request or an exact qualified name. |
node(self, node_id: 'str') -> 'SourceNode' | Get a located source statement by the ID from an origin or diagnostic. |
origin_for(self, name: 'str', *, kind: 'str' = 'rule') -> 'SourceOrigin' | Locate a qualified declaration, including schema-field and seed-field. |
schema(self, name: 'str') -> 'FactSchema' | Get a root-visible schema by local name, or an exact qualified name. |
source_identities | The URI and exact digest of each contributing source document. |
SourceBuildView API
Verified program, source analysis and embedded test results from one build.
SourceBuild(program: 'CompactIR', analysis: 'SourceAnalysis', tests: 'RuleSpecTestReport | None', output: 'Path | None' = None) -> NoneUsage and examples: Compile readable source.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return JSON-safe build identities, output path and test evidence. |
classification(self, name: 'str') -> 'Classification' | Get a nominal type for declaring explicit membership facts. |
decision(self, name: 'str') -> 'DecisionRequest' | Get a source-visible named request; raises KeyError when absent. |
digest | Digest of the exact compiled program bytes. |
explain(self, session: 'Engine | EngineSnapshot', subject: 'Conclusion | FactHandle | int | None' = None, *, limits: 'ExplanationLimits | None' = None) -> 'SourceExplanation' | Attach this source's declarations to an Engine or EngineSnapshot proof. |
kind | The header-selected artifact kind: program. |
origin_for(self, rule: 'str', *, kind: 'str' = 'rule') -> 'SourceOrigin' | Locate a qualified declaration; use kind for schemas, seeds or fields. |
schema(self, name: 'str') -> 'FactSchema' | Look up a fact schema for declaring session facts with Engine. |
SourceDiagnosticView API
Stable error code plus a human explanation and optional source location.
SourceDiagnostic(code: 'str', message: 'str', hint: 'str', span: 'SourceSpan | None' = None, node_id: 'str | None' = None, severity: 'str' = 'error') -> NoneUsage and examples: Write Ting rules.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return a detached JSON-compatible diagnostic. |
SourceDocumentView API
Exact source plus a comment-free tree; comments remain in snapshot.text.
SourceDocument(snapshot: 'SourceSnapshot', exact_digest: 'str', module: 'str', version: 'str', role: 'str', features: 'tuple[str, ...]', regions: 'tuple[SourceNode, ...]') -> NoneUsage and examples: Write Ting rules.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Inspect the source header and every located syntax node. |
SourceErrorView API
A source, build, test or file operation failed without publishing output.
SourceError(diagnostic: 'SourceDiagnostic', *, test_report: 'Any' = None) -> 'None'Usage and examples: Write Ting rules.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return diagnostics and, when present, the failed embedded test report. |
SourceExplanationView API
Immutable source context for proofs recorded at one session epoch.
SourceExplanation(program_digest: 'str', semantic_digest: 'str', sources: 'tuple[tuple[str, str], ...]', epoch: 'int', subject: 'FactSupport | Conclusion | None', proofs: 'tuple[SourceProof, ...]') -> NoneUsage and examples: Explain a conclusion.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return detached JSON-safe context, including the selected subject. |
SourceLimitsView API
Limits for each document and for its entire statically linked graph.
SourceLimits(max_source_bytes: 'int' = 1048576, max_total_bytes: 'int' = 8388608, max_modules: 'int' = 64, max_lines: 'int' = 50000, max_line_bytes: 'int' = 16384, max_nodes: 'int' = 100000, max_depth: 'int' = 32, max_integer_digits: 'int' = 128) -> NoneUsage and examples: Write Ting rules.
SourceModuleView API
Typed declarations for one source module after its explicit imports resolve.
SourceModule(document: 'SourceDocument', schemas: 'tuple[tuple[str, FactSchema], ...]', conclusions: 'tuple[tuple[str, FactSchema], ...]', rules: 'tuple[tuple[str, Rule], ...]', exports: 'tuple[tuple[str, str], ...]', imported_rules: 'tuple[Rule, ...]', tests: 'tuple[RuleSpecTestCase, ...]', origins: 'tuple[SourceOrigin, ...]', seeds: 'tuple[tuple[str, SeedFact], ...]' = (), seed_tests: 'tuple[str, ...]' = (), classifications: 'tuple[tuple[str, Classification], ...]' = (), requirements: 'tuple[tuple[str, tuple[tuple[str, str], ...]], ...]' = (), decisions: 'tuple[tuple[str, DecisionDefinition], ...]' = ()) -> NoneUsage and examples: Compile readable source.
SourceNodeView API
A syntax statement with immutable children and a stable structural ID.
SourceNode(text: 'str', span: 'SourceSpan', node_id: 'str', children: 'tuple[SourceNode, ...]' = ()) -> NoneUsage and examples: Write Ting rules.
SourceOriginView API
A named declaration and its location in an exact source document.
SourceOrigin(kind: 'str', name: 'str', node_id: 'str', span: 'SourceSpan') -> NoneUsage and examples: Write Ting rules.
SourceProofView API
A recorded proof, its rule definition and its direct supporting facts.
SourceProof(provenance: 'Provenance', rule: 'SourceOrigin', statements: 'tuple[SourceStatement, ...]', supports: 'tuple[FactSupport, ...]') -> NoneUsage and examples: Explain a conclusion.
| Member | Behavior |
|---|---|
as_dict(self) -> 'dict[str, Any]' | Return a JSON-safe direct proof; arrays use typed little-endian hex. |
SourceResolverView API
Explicit static provider; resolve a module name without implicit search.
SourceResolver(*args, **kwargs)Usage and examples: Compile readable source.
| Member | Behavior |
|---|---|
resolve(self, module: 'str') -> 'SourceSnapshot' | Return the requested source or raise KeyError when it is unavailable. |
SourceSnapshotView API
An immutable in-memory document; URI is a label, not a fetch request.
SourceSnapshot(text: 'str', uri: 'str' = '<memory>', revision: 'str' = '') -> NoneUsage and examples: Write Ting rules.
SourceSpanView API
One-based character location bound to the digest of exact source bytes.
SourceSpan(uri: 'str', source_digest: 'str', line: 'int', column: 'int', end_column: 'int') -> NoneUsage and examples: Write Ting rules.
SourceStatementView API
One condition or action definition, with its exact source location.
SourceStatement(role: 'str', text: 'str', node_id: 'str', span: 'SourceSpan') -> NoneUsage and examples: Explain a conclusion.
StagedHandleView API
Transaction-local reference to a declaration that is not committed yet.
StagedHandle(ordinal: 'int', _owner: 'object') -> NoneUsage and examples: Transactions.
StaleHistoryErrorView API
The current HEAD differs from the caller's optimistic expectation.
StaleHistoryError(expected_head: 'str | None', current_head: 'str | None') -> 'None'Usage and examples: Portable program files.
StaleTransactionErrorView API
A transaction was based on an engine epoch that is no longer current.
StaleTransactionError(expected_epoch: 'int', current_epoch: 'int') -> 'None'StoreMetricsView API
Deterministic physical-store counts; deliberately not byte accounting.
StoreMetrics(chunk_size: 'int', row_chunks: 'int', column_chunks: 'int', row_capacity: 'int', live_base_rows: 'int', live_derived_rows: 'int', tombstone_rows: 'int', compactions: 'int') -> NoneSupportChangeView API
A result whose direct support count or fact handle changed in an update.
SupportChange(subject: 'Fact | Conclusion', status: 'str', previous_supports: 'int', remaining_supports: 'int', previous_handle: 'FactHandle | None' = None, current_handle: 'FactHandle | None' = None) -> NoneUsage and examples: Keep valid results when inputs change.
testView API
Create a portable TEST condition over values and existing bindings.
test(operator: 'str', *operands: 'Value | BindingRef') -> 'TestCondition'Usage and examples: Rules and conditions.
TestConditionView API
A callback-free comparison over constants and existing bindings.
TestCondition(operator: 'str', operands: 'tuple[Value | BindingRef, ...]', negated: 'bool' = False) -> NoneUsage and examples: Rules and conditions.
TING_FILE_SUFFIXView API
Public constant or type alias; its value or definition is shown below.
TING_FILE_SUFFIX = '.tingir'TING_MAGICView API
Public constant or type alias; its value or definition is shown below.
TING_MAGIC = b'ting'TingErrorView API
Base class for all Ting Reasoner errors.
TingErrorTraceEventView API
One deterministic event in a run.
TraceEvent(sequence: 'int', kind: 'str', rule: 'str', fact_ids: 'tuple[int, ...]', bindings: 'tuple[tuple[str, Value], ...]', subject: 'int | str | None' = None) -> NoneUsage and examples: Multiprocessing.
TransactionView API
Stage fact changes and commit them atomically against an expected epoch.
Transaction(*args, **kwargs)Usage and examples: Transactions.
| Member | Behavior |
|---|---|
base_epoch | Read-only property. |
closed | Read-only property. |
commit(self) -> 'CommitReceipt' | Public method. |
declare(self, *facts: 'Fact') -> 'StagedHandle | tuple[StagedHandle, ...]' | Public method. |
receipt | Read-only property. |
replace(self, handle: 'FactHandle | StagedHandle | int', fact: 'Fact') -> 'StagedHandle' | Public method. |
retract(self, handle: 'FactHandle | StagedHandle | int') -> 'bool' | Public method. |
rollback(self) -> 'None' | Public method. |
useView API
Reference a binding from a later constraint or consequence.
use(binding: 'str | BindingRef') -> 'BindingRef'Usage and examples: Rules and conditions.
ValidationErrorView API
A public model value violates Ting's declarative contract.
ValidationErrorValueView API
Public constant or type alias; its value or definition is shown below.
Value = None | bool | int | float | str | bytes | ting_reasoner.nd.NDValueverify_compact_irView API
Verify and materialize Compact-IR, failing closed on every discrepancy.
verify_compact_ir(data: 'bytes | bytearray | memoryview', *, limits: 'IRLimits | None' = None) -> 'RuntimePlan'Usage and examples: Reuse a compiled program.