A production-ready, asynchronous backward-chaining rule engine in Python.
Project description
Siamese Prototype v2.0: Production-Ready Async Rule Engine
siamese-prototypeis a high-performance, asynchronous backward-chaining rule engine in Python. It is designed for production environments where logic-based decisions may need to interact with external, non-blocking I/O resources like web APIs or databases.
sequenceDiagram
participant App as User Application
participant Engine as RuleEngine (Facade)
participant Resolver as Async Resolver
participant KB as KnowledgeBase
participant Unificator
participant Builtin as Async Built-in (e.g., http_get_json)
App->>+Engine: await query("sibling", "john", "?S")
Engine->>Engine: _to_internal() converts args
Engine->>+Resolver: new Resolver(kb, builtins)
Resolver-->>-Engine: resolver_instance
Engine->>+Resolver: prove([goal], max_depth)
note right of Resolver: Stack created with initial goal: [sibling(john, ?S)]
loop Proof Search
Resolver->>Resolver: Pop goal from stack
Resolver->>Engine: yield TraceEvent("CALL", ...)
Engine->>Engine: logger.trace(...)
%% First, try matching a rule %%
Resolver->>+KB: find_rules(sibling/2)
KB-->>-Resolver: Return sibling rule
Resolver->>Resolver: _rename_rule()
Resolver->>+Unificator: unify(goal, rule.head)
Unificator-->>-Resolver: new_bindings
%% Rule head matches, add body to stack %%
Resolver->>Resolver: Push rule body to stack: [parent(?P, john), parent(?P, mary), neq(john, mary)]
Resolver->>Engine: yield TraceEvent("EXIT", ...)
Engine->>Engine: logger.success(...)
%% Next iteration of the loop for the 'parent' goal %%
Resolver->>Resolver: Pop goal from stack: [parent(?P, john)]
Resolver->>+KB: find_facts(parent/2)
KB-->>-Resolver: Return parent(david, john)
Resolver->>+Unificator: unify(...)
Unificator-->>-Resolver: new_bindings {?P: david}
%% ... continues similarly for the second 'parent' goal ... %%
%% Finally, process the 'neq' built-in goal %%
Resolver->>Resolver: Pop goal from stack: [neq(john, mary)]
Resolver->>+Builtin: neq_builtin(goal, bindings)
note right of Builtin: Performs synchronous check
Builtin-->>-Resolver: yield new_bindings
%% All goals in rule body are satisfied. The original goal list is now empty %%
Resolver->>Resolver: Pop empty goal list from stack
Resolver->>Engine: yield final_bindings
end
Engine->>Engine: Format solution, check for uniqueness
Engine-->>-App: async for sol in solutions: yield solution_dict
%% Example of an async built-in call
loop Another Query
App->>Engine: await query("http_get_json", url, "?R")
%% ... setup is similar ... %%
Resolver->>Resolver: Pop goal: [http_get_json(...)]
Resolver->>+Builtin: await http_get_json(goal, bindings)
note over Builtin: Makes async HTTP call via aiohttp. <br/> Event loop is free to run other tasks.
Builtin-->>-Resolver: yield new_bindings_with_json_data
%% ... proof continues ... %%
end
Key Features
- 😎Fully Asynchronous: Built on
asyncioto handle I/O-bound tasks without blocking. - 👀Structured Logging: Integrated with
logurufor powerful and configurable tracing and debugging. - ⚙️External Configuration: Load facts and rules from human-readable YAML files.
- 🪜Extensible Async Built-ins: Easily write custom Python
asyncfunctions to extend the engine's logic (e.g., for database access, API calls). - 🚀Robust and Thread-Safe: The query resolution process is stateless, allowing for safe concurrent querying from multiple async tasks.
- 🧰Control & Safety: Prevents infinite loops with configurable search depth and limits the number of solutions.
Installation
To install the library and its dependencies from the project root, run:
uv sync
Quick Start
-
Define your knowledge in a YAML file (
knowledge.yaml):facts: - [parent, david, john] - [parent, john, mary] rules: - head: [grandparent, '?GP', '?GC'] body: - [parent, '?GP', '?P'] - [parent, '?P', '?GC']
-
Write your async Python script:
import asyncio from siamese import RuleEngine async def main(): # Initialize the engine engine = RuleEngine() # Load knowledge from the file engine.load_from_file("knowledge.yaml") print("Knowledge base loaded.") # Asynchronously query the engine print("Query: Who is David's grandchild?") solutions = await engine.query("grandparent", "david", "?GC") async for sol in solutions: print(f" - Solution: {sol}") if __name__ == "__main__": asyncio.run(main())
Advanced Features
Async Built-ins
The engine supports custom async built-in functions for external I/O operations:
async def my_custom_builtin(goal, bindings):
# Perform async operations like database queries or API calls
result = await some_async_operation()
if result:
new_bindings = bindings.copy()
new_bindings[goal.args[0]] = result
yield new_bindings
# Register your custom built-in
engine = RuleEngine(builtins={"my_builtin": my_custom_builtin})
Structured Logging
Configure detailed tracing of the engine's reasoning process:
engine.configure_logging(level="TRACE") # For detailed tracing
engine.configure_logging(level="INFO") # For production use
External Knowledge Bases
Load complex knowledge bases from YAML files:
facts:
- [person, alice]
- [person, bob]
- [parent, alice, bob]
rules:
- head: [ancestor, '?A', '?D']
body:
- [parent, '?A', '?D']
- head: [ancestor, '?A', '?D']
body:
- [parent, '?A', '?P']
- [ancestor, '?P', '?D']
License
MIT License - see LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file siamese_prototype-2.0.0.tar.gz.
File metadata
- Download URL: siamese_prototype-2.0.0.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae2d23863fbfb793422a4249fc108085b5e59b83bc8ef7a4c74b00af82bf2456
|
|
| MD5 |
a1948133fec5cd0027037bf7d6b0f90a
|
|
| BLAKE2b-256 |
64a963523628b861d500f041c2d94f8916929540ff3a58603cb39912c732e04f
|
File details
Details for the file siamese_prototype-2.0.0-py3-none-any.whl.
File metadata
- Download URL: siamese_prototype-2.0.0-py3-none-any.whl
- Upload date:
- Size: 11.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52d26b2d0fe252d3bf691a6b7be8119aba5210a6de2b4ccd78d9ad568113db49
|
|
| MD5 |
eed37e2de36ea2244c60914cf29c0fab
|
|
| BLAKE2b-256 |
ee62e6755045dad009f641dafa3be356323b64649e61fcde415e9a8eb389de8f
|