Skip to main content

A Goal-Task-Network planning package written in Python

Project description

GTPyhop version 1.7.0

Python Version License PyPI

GTPyhop is an HTN planning system based on Pyhop, but generalized to plan for both goals and tasks.

Dana Nau is the original author of GTPyhop.

The pip Branch

This pip branch is forked from Dana Nau's GTPyhop main branch and refactored for PyPI distribution.

The file tree structure of this pip branch, produced with the help of _GithubTree, is the following:

๐Ÿ“„ LICENSE.txt
๐Ÿ“„ pyproject.toml
๐Ÿ“„ README.md
๐Ÿ“ docs/
    โ”œโ”€โ”€ ๐Ÿ“„ all_examples.md
    โ”œโ”€โ”€ ๐Ÿ“„ changelog.md
    โ”œโ”€โ”€ ๐Ÿ“„ gtpyhop_domain_style_guide.md
    โ”œโ”€โ”€ ๐Ÿ“„ gtpyhop_problems_style_guide.md
    โ”œโ”€โ”€ ๐Ÿ“„ logging.md
    โ”œโ”€โ”€ ๐Ÿ“„ running_examples.md
    โ””โ”€โ”€ ๐Ÿ“„ thread_safe_sessions.md
๐Ÿ“ src/
    โ””โ”€โ”€ ๐Ÿ“ gtpyhop/
        โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
        โ”œโ”€โ”€ ๐Ÿ“ examples/
            โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
            โ”œโ”€โ”€ ๐Ÿ“„ backtracking_htn.py
            โ”œโ”€โ”€ ๐Ÿ“ blocks_goal_splitting/
                โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                โ”œโ”€โ”€ ๐Ÿ“„ actions.py
                โ”œโ”€โ”€ ๐Ÿ“„ examples.py
                โ”œโ”€โ”€ ๐Ÿ“„ methods.py
                โ””โ”€โ”€ ๐Ÿ“„ README.txt
            โ”œโ”€โ”€ ๐Ÿ“ blocks_gtn/
                โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                โ”œโ”€โ”€ ๐Ÿ“„ actions.py
                โ”œโ”€โ”€ ๐Ÿ“„ examples.py
                โ”œโ”€โ”€ ๐Ÿ“„ methods.py
                โ””โ”€โ”€ ๐Ÿ“„ README.txt
            โ”œโ”€โ”€ ๐Ÿ“ blocks_hgn/
                โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                โ”œโ”€โ”€ ๐Ÿ“„ actions.py
                โ”œโ”€โ”€ ๐Ÿ“„ examples.py
                โ””โ”€โ”€ ๐Ÿ“„ methods.py
            โ”œโ”€โ”€ ๐Ÿ“ blocks_htn/
                โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                โ”œโ”€โ”€ ๐Ÿ“„ actions.py
                โ”œโ”€โ”€ ๐Ÿ“„ examples.py
                โ””โ”€โ”€ ๐Ÿ“„ methods.py
            โ”œโ”€โ”€ ๐Ÿ“ ipc-2020-total-order/
                โ”œโ”€โ”€ ๐Ÿ“„ benchmarking.py
                โ”œโ”€โ”€ ๐Ÿ“„ benchmarking_quickstart.md
                โ”œโ”€โ”€ ๐Ÿ“ Blocksworld-GTOHP/
                    โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                    โ”œโ”€โ”€ ๐Ÿ“„ domain.py/
                    โ”œโ”€โ”€ ๐Ÿ“„ ipc-2020-to-bw-gtohp-readme.md
                    โ””โ”€โ”€ ๐Ÿ“„ problems.py/
                โ””โ”€โ”€ ๐Ÿ“ Childsnack/
                    โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                    โ”œโ”€โ”€ ๐Ÿ“„ domain.py/
                    โ”œโ”€โ”€ ๐Ÿ“„ ipc-2020-to-childsnack-readme.md
                    โ””โ”€โ”€ ๐Ÿ“„ problems.py/
            โ”œโ”€โ”€ ๐Ÿ“ mcp-orchestration/
                โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                โ”œโ”€โ”€ ๐Ÿ“„ benchmarking.py
                โ”œโ”€โ”€ ๐Ÿ“„ benchmarking_quickstart.md
                โ”œโ”€โ”€ ๐Ÿ“ bio_opentrons/
                    โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                    โ”œโ”€โ”€ ๐Ÿ“„ domain.py
                    โ”œโ”€โ”€ ๐Ÿ“„ problems.py
                    โ””โ”€โ”€ ๐Ÿ“„ README.md
                โ”œโ”€โ”€ ๐Ÿ“ cross_server/
                    โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                    โ”œโ”€โ”€ ๐Ÿ“„ domain.py
                    โ”œโ”€โ”€ ๐Ÿ“„ problems.py
                    โ””โ”€โ”€ ๐Ÿ“„ README.md
                โ”œโ”€โ”€ ๐Ÿ“ drug_target_discovery/
                    โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                    โ”œโ”€โ”€ ๐Ÿ“„ domain.py
                    โ”œโ”€โ”€ ๐Ÿ“„ problems.py
                    โ””โ”€โ”€ ๐Ÿ“„ README.md
                โ”œโ”€โ”€ ๐Ÿ“ omega_hdq_dna_bacteria_flex_96_channel/
                    โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                    โ”œโ”€โ”€ ๐Ÿ“„ domain.py
                    โ”œโ”€โ”€ ๐Ÿ“„ problems.py
                    โ””โ”€โ”€ ๐Ÿ“„ README.md
                โ””โ”€โ”€ ๐Ÿ“ tnf_cancer_modelling/
                    โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
                    โ”œโ”€โ”€ ๐Ÿ“„ domain.py
                    โ”œโ”€โ”€ ๐Ÿ“„ problems.py
                    โ””โ”€โ”€ ๐Ÿ“„ README.md
            โ”œโ”€โ”€ ๐Ÿ“„ logistics_hgn.py
            โ”œโ”€โ”€ ๐Ÿ“„ pyhop_simple_travel_example.py
            โ”œโ”€โ”€ ๐Ÿ“„ regression_tests.py
            โ”œโ”€โ”€ ๐Ÿ“„ simple_hgn.py
            โ”œโ”€โ”€ ๐Ÿ“„ simple_htn_acting_error.py
            โ””โ”€โ”€ ๐Ÿ“„ simple_htn.py
        โ”œโ”€โ”€ ๐Ÿ“„ logging_system.py
        โ”œโ”€โ”€ ๐Ÿ“„ main.py
        โ””โ”€โ”€ ๐Ÿ“ test_harness/
            โ”œโ”€โ”€ ๐Ÿ“„ __init__.py
            โ””โ”€โ”€ ๐Ÿ“„ test_harness.py

Installation from PyPI (Recommended: Version 1.7.0)

GTPyhop 1.7.0 is the latest version with enhanced MCP orchestration examples, Opentrons Flex domains, and comprehensive style guides. For new projects, especially those requiring reliable planning and benchmarking, use 1.7.0:

pip install gtpyhop>=1.7.0

For basic single-threaded planning, any version works:

pip install gtpyhop

uv can of course be used if you prefer:

uv pip install gtpyhop

Installation from github

Alternatively, you can directly install from github:

git clone -b pip https://github.com/PCfVW/GTPyhop.git
cd GTPyhop
pip install .

Testing your installation

We suggest you give gtpyhop a try straight away; open a terminal and start an interactive python session:

python

.. and import gtpyhop to run the regression tests:

# Import the main GTPyhop planning system
import gtpyhop

The following should be printed in your terminal:

Imported GTPyhop version 1.7.0
Messages from find_plan will be prefixed with 'FP>'.
Messages from run_lazy_lookahead will be prefixed with 'RLL>'.
Using session-based architecture with structured logging.

Now import the regression tests module:

from gtpyhop.examples import regression_tests

Be prepared to see a lot of information on the screen about the examples and how to solve them, with different levels of verbosity; with this in mind, run the regression tests:

# Run legacy regression tests (backward compatible)
regression_tests.main()

# Or run session-based regression tests (recommended for 1.3.0+)
regression_tests.main_session()

The last line printed in your terminal should be:

Finished without error.

For GTPyhop 1.3.0+: You can also run regression tests from the command line:

# Legacy mode
python -m gtpyhop.examples.regression_tests

# Session mode (thread-safe)
python -m gtpyhop.examples.regression_tests --session

Happy Planning!

Thread-Safe Sessions (1.3.0+)

GTPyhop 1.3.0 introduces session-based, thread-safe planning that enables reliable concurrent execution and isolated planning contexts. This is a major architectural enhancement while maintaining 100% backward compatibility.

Key Benefits

  • Thread-safe concurrent planning: Multiple planners can run simultaneously without interference
  • Isolated execution contexts: Each session has its own configuration, logs, and statistics
  • Structured logging system: Programmatic access to planning traces, statistics, and debugging information
  • Timeout management: Built-in timeout enforcement and resource management
  • Session persistence: Save and restore planning sessions across runs

Quick Start with Sessions

import gtpyhop

# Create a Domain and define actions/methods (same as before)
my_domain = gtpyhop.Domain('my_domain')
# ... define actions and methods ...

# NEW: Use session-based planning for thread safety
with gtpyhop.PlannerSession(domain=my_domain, verbose=1) as session:
    with session.isolated_execution():
        result = session.find_plan(state, [('transport', 'obj1', 'loc2')])
        if result.success:
            print(result.plan)

For detailed examples, concurrent planning patterns, and complete API reference, see Thread-Safe Sessions Guide

Let's HTN Start!

You have successfully installed and tested gtpyhop; it's time to declare your own planning problems in gtpyhop.

Very first HTN example

The key pattern is: create a Domain โ†’ define actions/methods โ†’ declare them โ†’ define initial state โ†’ use gtpyhop.find_plan() to solve problems.

In the first three steps, we give simple illustrations on Domain creation, action and task method definition, and how to declare them; make sure you declared an initial state before calling for planning; in step 5 below, you'll find the code for a complete example.

1. First, create a Domain to hold your definitions

import gtpyhop

# Create a Domain
gtpyhop.Domain('my_domain')

2. Define Actions

Actions are atomic operations that directly modify a state: actions are Python functions where the first argument is the current state, and the others are the action's arguments telling what changes the action shall bring to the state.

For example, the function my_action(state, arg1, arg2) below implements the action ('my_action', arg1, arg2). In the following code, arg1 is used as an object key to check and modify its position, while arg2 is used both as a condition to check against and as a key to update the status:

def my_action(state, arg1, arg2):
    # Check preconditions using arg1 and arg2
    if state.pos[arg1] == arg2:
        # Modify state using arg1 and arg2
        state.pos[arg1] = 'new_location'
        state.status[arg2] = 'updated'
        return state  # Success
    return False  # Failure

# Declare actions
gtpyhop.declare_actions(my_action, another_action)

3. Define Task Methods

During planning, Task methods decompose compound tasks into subtasks (which shall be further decomposed) and actions (whose Python functions will be executed).

Task methods are also Python functions where the first argument is the current state, and the others can be passed to the subtasks and actions.

In the following code, arg1 is used as an argument to the subtasks (perhaps specifying what object to work with), while arg2 is used as an argument to the action (perhaps specifying a target location or condition):

def method_for_task(state, arg1, arg2):
    # Check if this method is applicable
    if some_condition:
        # Return list of subtasks/actions
        return [('subtask1', arg1), ('action1', arg2)]
    return False  # Method not applicable

# Declare task methods
gtpyhop.declare_task_methods('task_name', method_for_task, alternative_method)

4. Define Initial State Give a description of the initial state, including the values of all relevant state variables; in the following code, pos is a dictionary that maps objects to their locations:

# Define initial state
initial_state = gtpyhop.State('initial_state')
initial_state.pos = {'obj1': 'loc1', 'obj2': 'loc2'}

5. Here is the complete example:

import gtpyhop

# Domain creation
gtpyhop.Domain('my_domain')

# Actions
def move(state, obj, target):
    if obj in state.pos:
        state.pos[obj] = target
        return state
    return False

gtpyhop.declare_actions(move)

# Task methods
def transport(state, obj, destination):
    current = state.pos[obj]
    if current != destination:
        return [('move', obj, destination)]
    return []

gtpyhop.declare_task_methods('transport', transport)

# Define initial state
initial_state = gtpyhop.State('initial_state')
initial_state.pos = {'obj1': 'loc1', 'obj2': 'loc2'}

# Find plan
gtpyhop.set_verbose_level(1)
plan = gtpyhop.find_plan(initial_state, [('transport', 'obj1', 'loc2')])
print(plan)

Put this code in a file, say my_very_first_htn_example.py, and run it from a terminal:

python my_very_first_htn_example.py

Does it run correctly? Increase the verbosity level to 2 or 3 and run it again to see more information about the planning process.

Session-Based Version (Recommended for 1.3.0+)

For better isolation and thread safety, use the session-based approach:

import gtpyhop

# Domain creation (same as above)
my_domain = gtpyhop.Domain('my_domain')
state = gtpyhop.State('initial_state')
state.pos = {'obj1': 'loc1', 'obj2': 'loc2'}

# Define actions and methods (same as above)
def move(state, obj, target):
    if obj in state.pos:
        state.pos[obj] = target
        return state
    return False

gtpyhop.declare_actions(move)

def transport(state, obj, destination):
    current = state.pos[obj]
    if current != destination:
        return [('move', obj, destination)]
    return []

gtpyhop.declare_task_methods('transport', transport)

# NEW: Use session-based planning
with gtpyhop.PlannerSession(domain=my_domain, verbose=1) as session:
    with session.isolated_execution():
        result = session.find_plan(state, [('transport', 'obj1', 'loc2')])
        if result.success:
            print("Plan found:", result.plan)
            print("Planning stats:", result.stats)

            # NEW: Access structured logs
            logs = session.logger.get_logs()
            print(f"Generated {len(logs)} log entries during planning")
        else:
            print("Planning failed:", result.error)

Benefits of the session approach:

  • Thread-safe for concurrent use
  • Isolated configuration per session
  • Built-in timeout and resource management
  • Structured result objects with statistics
  • Comprehensive logging system with programmatic access to planning traces
  • Session persistence capabilities

๐Ÿ”„ Migration from Pre-1.3.0 Versions

Existing code continues to work unchanged - GTPyhop 1.3.0 maintains 100% backward compatibility.

To leverage 1.3.0 features:

  1. For single-threaded code: No changes required, but consider sessions for better structure
  2. For concurrent code: Migrate to PlannerSession to avoid race conditions
  3. For production systems: Use sessions for timeout management and structured logging

Simple migration pattern:

# Before (still works)
plan = gtpyhop.find_plan(state, tasks)

# After (recommended)
with gtpyhop.PlannerSession(domain=my_domain) as session:
    with session.isolated_execution():
        result = session.find_plan(state, tasks)
        plan = result.plan if result.success else None

๐Ÿ“‹ Version Selection Guide

Use Case Recommended Version Why
New projects 1.7.0+ Latest features, enhanced MCP orchestration, comprehensive style guides
MCP integration 1.7.0+ Cross-server orchestration, Opentrons Flex domains, drug discovery workflows
Concurrent/parallel planning 1.7.0+ Thread-safe sessions by default, prevent race conditions
Production systems 1.7.0+ Robustness improvements, timeout management, structured logging
Benchmarking/evaluation 1.7.0+ Resource monitoring, IPC domains, 5 validated MCP examples
Web APIs/servers 1.7.0+ Isolated sessions per request, timeout handling
Educational/simple scripts Any version All versions support basic planning
Legacy code maintenance Keep current All versions are backward compatible

Additional Information

Please read Dana's additional information of how to implement:

Examples

GTPyhop includes comprehensive examples demonstrating various planning techniques. All examples support both legacy and session modes for maximum flexibility and thread safety.

๐Ÿš€ Quick Example Run

Try GTPyhop immediately:

# Legacy mode (backward compatible)
python -m gtpyhop.examples.simple_htn

# Session mode (thread-safe, recommended for 1.3.0+)
python -m gtpyhop.examples.simple_htn --session

๐Ÿ“– For comprehensive example documentation, see All Examples Guide

๐Ÿ“š Documentation

GTPyhop 1.7.0+ includes comprehensive documentation organized in the docs/ folder:

Core Documentation

Style Guides (1.6.0+, Enhanced in 1.7.0)

  • Domain Style Guide - LibCST-compatible conventions for writing domain files (actions and methods) with proper type hints, docstrings, and comment markers (Version 1.1.0)
  • Problems Style Guide - Conventions for writing problem files with initial states and goal tasks (Version 2.1.0)

Specialized Documentation

IPC 2020 Total Order Domains

MCP Orchestration Examples (1.5.0+, Enhanced in 1.7.0)

External Resources

New Features

Iterative Planning Strategy

This pip branch introduces a new iterative planning strategy that enhances the planner's capabilities for large planning scenarios; it is the default strategy.

  • How it works: Uses an explicit stack data structure
  • Memory usage: More memory-efficient, no call stack buildup
  • Limitations: No recursion limit constraints
  • Backtracking: Explicit stack management for exploring alternatives
  • Use cases:
    • Large planning problems that might exceed recursion limits
    • Memory-constrained environments
    • Production systems requiring reliability

Once gtpyhop is imported, Dana Nau's original recursive strategy can be set by calling:

set_recursive_planning(True)  # Planning strategy now is recursive

Recursive Planning Strategy:

  • How it works: Uses Python's call stack with recursive function calls
  • Memory usage: Each recursive call adds a frame to the call stack
  • Limitations: Limited by Python's recursion limit (default 1000)
  • Backtracking: Natural backtracking through function returns
  • Use cases:
    • Small to medium planning problems
    • When you need to see traditional backtracking behavior
    • Educational purposes or debugging

Of course you can get back to the iterative planning strategy by calling:

set_recursive_planning(False)  # Planning strategy now is iterative

New Functions

Functions Added in 1.4.0 (Robustness, Validation & Benchmarking)

main.py

  • validate_plan_from_goal - Preconditions of each action are successively satisfied from the initial state to eventually produce the goal state.

Benchmarking (benchmarking.py)

  • safe_add_to_path, setup_gtpyhop_imports,
  • load_domain_package,validate_domain_package,load_domain_package,load_domain_package,
  • create_argument_parser, list_available_domains, main,
  • ResourceUsage (data class),BenchmarkResult (data classe),
  • DomainHandler (abstract class),
    • create_multigoal (staticmethod),
  • PlannerBenchmark (class)
    • _get_memory_usage, _calculate_resource_metrics, track_resources (methods)
    • run_single, run_multiple, _calculate_column_widths, print_summary (methods)

Functions Added in 1.3.0 (Thread-Safe Sessions)

Session Management:

  • PlannerSession (class) - Isolated, thread-safe planning context
  • create_session, get_session, destroy_session, list_sessions - Session lifecycle management
  • PlanningTimeoutError (exception) - Timeout handling for session-scoped operations

Session Persistence:

  • SessionSerializer (class), restore_session, restore_all_sessions - Session persistence and recovery
  • set_persistence_directory, get_persistence_directory - Configure auto-save/recovery

Enhanced Planning:

  • session.find_plan() - Per-session planning with timeout and expansion limits
  • session.isolated_execution() - Context manager for safe global state management

Functions Added in 1.2.1 (Iterative Planning & Utilities)

Domain Management:

  • print_domain_names, find_domain_by_name, is_domain_created
  • set_current_domain, get_current_domain

Planning Strategy Control:

  • set_recursive_planning, get_recursive_planning, reset_planning_strategy
  • set_verbose_level, get_verbose_level

Iterative Planning Implementation:

  • seek_plan_iterative and related iterative planning functions
  • refine_multigoal_and_continue_iterative, refine_unigoal_and_continue_iterative
  • refine_task_and_continue_iterative, apply_action_and_continue_iterative

Renaming

_recursive has been added at the end of the identifiers of the original functions involved in seeking for a plan:

  • seek_plan โ†’ seek_plan_recursive
  • _apply_action_and_continue โ†’ apply_action_and_continue_recursive
  • _refine_multigoal_and_continue โ†’ refine_multigoal_and_continue_recursive
  • _refine_unigoal_and_continue โ†’ refine_unigoal_and_continue_recursive
  • _refine_task_and_continue โ†’ refine_task_and_continue_recursive

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

gtpyhop-1.7.0.tar.gz (203.7 kB view details)

Uploaded Source

Built Distribution

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

gtpyhop-1.7.0-py3-none-any.whl (224.1 kB view details)

Uploaded Python 3

File details

Details for the file gtpyhop-1.7.0.tar.gz.

File metadata

  • Download URL: gtpyhop-1.7.0.tar.gz
  • Upload date:
  • Size: 203.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for gtpyhop-1.7.0.tar.gz
Algorithm Hash digest
SHA256 a7a9fb4256f78c37982bce792d5bc676d44edd4a0c091d163005b3f5234d83a8
MD5 d80e322a00092e602870a4cd9e3a6ff4
BLAKE2b-256 0d112f77271cee01a09e87a4ba67c646b71bde61cb3d244201ab079b318a8305

See more details on using hashes here.

Provenance

The following attestation bundles were made for gtpyhop-1.7.0.tar.gz:

Publisher: publish-new-GTPyhop-to-pypi.yml on PCfVW/GTPyhop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gtpyhop-1.7.0-py3-none-any.whl.

File metadata

  • Download URL: gtpyhop-1.7.0-py3-none-any.whl
  • Upload date:
  • Size: 224.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for gtpyhop-1.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 783882bd98659aa1e2d2abec39db145df8469ecf9256baa5d8a81f47e74326fa
MD5 a0662553b06775b073e315086167d14f
BLAKE2b-256 ddda055be1ba6dcd22dfa6c18d8152d4c77e58e4826199f1fc94be07aec82369

See more details on using hashes here.

Provenance

The following attestation bundles were made for gtpyhop-1.7.0-py3-none-any.whl:

Publisher: publish-new-GTPyhop-to-pypi.yml on PCfVW/GTPyhop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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