Skip to main content

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 asyncio to handle I/O-bound tasks without blocking.
  • 👀Structured Logging: Integrated with loguru for 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 async functions 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

  1. 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']
    
  2. 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


Download files

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

Source Distribution

siamese_prototype-2.0.0.tar.gz (12.6 kB view details)

Uploaded Source

Built Distribution

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

siamese_prototype-2.0.0-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

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

Hashes for siamese_prototype-2.0.0.tar.gz
Algorithm Hash digest
SHA256 ae2d23863fbfb793422a4249fc108085b5e59b83bc8ef7a4c74b00af82bf2456
MD5 a1948133fec5cd0027037bf7d6b0f90a
BLAKE2b-256 64a963523628b861d500f041c2d94f8916929540ff3a58603cb39912c732e04f

See more details on using hashes here.

File details

Details for the file siamese_prototype-2.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for siamese_prototype-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 52d26b2d0fe252d3bf691a6b7be8119aba5210a6de2b4ccd78d9ad568113db49
MD5 eed37e2de36ea2244c60914cf29c0fab
BLAKE2b-256 ee62e6755045dad009f641dafa3be356323b64649e61fcde415e9a8eb389de8f

See more details on using hashes here.

Supported by

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