AI-Planning / planutils

General library for setting up linux-based environments for developing, running, and evaluating planners.
MIT License
108 stars 31 forks source link

Server Example #135

Open delislice opened 11 months ago

delislice commented 11 months ago

Could anyone please provide an example of how to use the planutils server endpoints? I am having difficulty figuring out how to format client requests using the manifest.json files. If I had an example demonstrating something like what the client call for the lama planner looks like, that would go a million miles for my simple brain. Cheers, and great work on this awesome project!

haz commented 11 months ago

Yep!

https://github.com/AI-Planning/planutils/tree/main/environments%2Fserver

But also worth pointing out that it's hosted online too so you don't have to.

delislice commented 11 months ago

Thank you! I used the same parameters in that link to set up my planutils server, but I'm still a little confused on what the server endpoints are. Ideally, I'd like to create a simple python client app that can load a domain.pddl and problem.pddl along with other necessary parameters into a JSON dictionary and send a POST or GET to the planutils server, but I have no idea what the server API is. Is there an explanation or example of using the server API anywhere? Thanks a lot!

haz commented 11 months ago

Will hook you up later tonight when I get back to a laptop...

delislice commented 11 months ago

Thank you for your help. You pointed out in the VS Code thread that I should be trying the planner as a service instead, so I will give that a try.

delislice commented 11 months ago

So, I got the planutils as a service container running and successfully executed the sample Python domain/problem script using the lama planner. I would like to use the popf planner instead; however, I found that the manifest.json file for popf does not describe any server endpoints, so I copied the ones used for lama:

    "name": "POPF: Partial Order Planning Forwards",
    "description": "POPF is forwards-chaining temporal planner with linear continuous effects, inspited by partial-order planning. This version (POPF2) introduces any-time search, allowing it to optimise solution quality.",
    "homepage": "https://github.com/roveri-marco/popf",
    "install-size": "297MB",
    "dependencies": [],
    "endpoint": {
        "services": {
            "solve": {
                "args": [
                    {
                        "description": "domain file",
                        "name": "domain",
                        "type": "file"
                    },
                    {
                        "description": "problem file",
                        "name": "problem",
                        "type": "file"
                    }
                ],
                "call": "popf {domain} {problem}",
                "return": {
                    "files": "*plan*",
                    "type": "generic"
                }
            }
        }
    }
}

I'm not sure what additional hooks need to be added to the planutils server to support popf, but I was unable to get this to work using the sample Python app. Are there other files in the server that need to be touched to add popf support? Thanks!

haz commented 11 months ago

Great! So the popf planner works in a fairly different manner than lama. Most importantly (and frustratingly), there are no proper plan files produced. You can see the manifest for a very similar planner (optic) over here: https://github.com/AI-Planning/planutils/blob/main/planutils/packages/optic/manifest.json#L7

Note that the call string just pipes it all to a plan file, and then that's the file that is returned.

delislice commented 11 months ago

Thank you for the replay. I am trying to wrap my head around the mechanics of how a planner's output is routed to the result of a POST request here, and what those mechanics mean for POPF compatibility:

# Send job request to solve endpoint
solve_request_url=requests.post("http://localhost:5001/package/popf/solve", json=req_body).json()
print(solve_request_url)

# Query the result in the job
celery_result=requests.post('http://localhost:5001' + solve_request_url['result'])

print('Computing...')
while celery_result.json().get("status","")== 'PENDING':

    # Query the result every 0.5 seconds while the job is executing
    celery_result=requests.post('http://localhost:5001' + solve_request_url['result'])
    time.sleep(0.5)

pprint(celery_result.json())

My new POPF manifest.json is:

{
    "name": "POPF: Partial Order Planning Forwards",
    "description": "POPF is forwards-chaining temporal planner with linear continuous effects, inspited by partial-order planning. This version (POPF2) introduces any-time search, allowing it to optimise solution quality.",
    "homepage": "https://github.com/roveri-marco/popf",
    "install-size": "297MB",
    "dependencies": [],
    "endpoint": {
        "services": {
          "solve": {
            "template": "planner",
            "call":"popf {domain} {problem} >> plan",
            "return": {
              "type": "log",
              "files": "*plan*"
            }
          }
        }
      }    
}

When I run the Python sample client, even with a known POPF compatible set of domain/problem files, I get a seg fault in the result:

Computing...
{'result': {'call': 'popf {domain} {problem} >> plan',
            'output': {'plan': ''},
            'output_type': 'log',
            'stderr': 'Segmentation fault (core dumped)\n',
            'stdout': ''},
 'status': 'ok'}

I am likely doing something horribly wrong but do not have the insight to understand what. Would you happen to have any idea what may be happening?

When I run the planutils POPF command line utility on my domain and problem files inside the docker container, I successfully get a plan:

(planutils) root@ubuntu:~/pddl$ popf domain.pddl problem.pddl 
Number of literals: 78
Constructing lookup tables: [10%] [20%] [30%] [40%] [50%] [60%] [70%] [80%] [90%] [100%]
Post filtering unreachable actions:  [10%] [20%] [30%] [40%] [50%] [60%] [70%] [80%] [90%] [100%]
No analytic limits found, not considering limit effects of goal-only operators
All the ground actions in this problem are compression-safe
Initial heuristic = 9.000
b (8.000 | 5.000)b (7.000 | 5.000)b (6.000 | 5.000)b (5.000 | 10.001)b (3.000 | 10.001)b (2.000 | 25.004)b (1.000 | 40.007);;;; Solution Found
; States evaluated: 620
; Cost: 45.008
; Time 1.29
0.000: (transport r2d2 wheel_1 wheels_zone assembly_zone)  [5.000]
0.000: (move c3po wheels_zone steering_wheels_zone)  [5.000]
0.000: (move bb8 wheels_zone body_car_zone)  [5.000]
5.001: (transport c3po steering_wheel_1 steering_wheels_zone assembly_zone)  [5.000]
5.001: (transport bb8 body_car_1 body_car_zone assembly_zone)  [5.000]
10.002: (assemble r2d2 assembly_zone wheel_1 body_car_1 steering_wheel_1 car_3)  [5.000]
10.002: (move bb8 assembly_zone wheels_zone)  [5.000]
10.002: (move c3po assembly_zone steering_wheels_zone)  [5.000]
15.003: (transport bb8 wheel_2 wheels_zone assembly_zone)  [5.000]
15.003: (transport c3po steering_wheel_2 steering_wheels_zone assembly_zone)  [5.000]
15.003: (move r2d2 assembly_zone body_car_zone)  [5.000]
20.004: (transport r2d2 body_car_2 body_car_zone assembly_zone)  [5.000]
20.004: (move c3po assembly_zone wheels_zone)  [5.000]
25.005: (assemble bb8 assembly_zone wheel_2 body_car_2 steering_wheel_2 car_2)  [5.000]
25.005: (transport c3po wheel_3 wheels_zone assembly_zone)  [5.000]
25.005: (move r2d2 assembly_zone steering_wheels_zone)  [5.000]
30.006: (transport r2d2 steering_wheel_3 steering_wheels_zone assembly_zone)  [5.000]
30.006: (move bb8 assembly_zone body_car_zone)  [5.000]
30.006: (move c3po assembly_zone body_car_zone)  [5.000]
35.007: (transport bb8 body_car_3 body_car_zone assembly_zone)  [5.000]
40.008: (assemble r2d2 assembly_zone wheel_3 body_car_3 steering_wheel_3 car_1)  [5.000]
haz commented 11 months ago

Hrmz...maybe something funky with the req_body? What's that look like? You can try sending it to optic too (should accept the same inputs) and see if that works. Also, since you control the the call string, you can just cat the domain or problem file to make sure it's working.