If you don’t know what ChatGPT is you can sign up for free at https://chat.openai.com/. Signing up does require a valid phone number that can receive SMS messages for confirmation.
The introductory documentation for ChatGPT plugins can be found at https://platform.openai.com/docs/plugins/introduction
For a plugin to work it needs to communicate between two endpoints, with one endpoint being ChatGPT. To create a programming language independent API, OpenAPI is used. OpenAPI uses a specification file.
ChatGPT uses the OpenAPI specification file to understand how to create HTTP(S) request to send to the plugin and what expected responses exist.
The plugin is created using any programming language that can run an HTTP(S) server and communicate using REST.
Three additional files needed to create a ChatGPT plugin are
- A logo file for displaying an image along with a textual name of the plugin.
- A manifest file that contains some basic fields including the location the OpenAPI specification for the plugin.
- The code to run the HTTP(S) server.
The basic file layout of many plugins is
C:.
| logo.png
| main.py
| openapi.yaml
\---.well-known
ai-plugin.json
This example will use SWI-Prolog and have just one POST command to receive SWI-Prolog code which it will compile and then return the results.
The manifest file, named ai-plugin.json
, is typical for most introductory plugins.
For the ChatGPT plugin - SWI-Prolog REPL this is ai-plugin.json
{
"schema_version": "v1",
"name_for_human": "SWI-Prolog REPL",
"name_for_model": "SWI_Prolog_REPL",
"description_for_human": "Compile SWI-Prolog code and return results, success or errors.",
"description_for_model": "Compile SWI-Prolog code and return results, success or errors.",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "http://localhost:5003/openapi.yaml",
"is_user_authenticated": false
},
"logo_url": "http://localhost:5003/logo.png",
"contact_email": "support@example.com",
"legal_info_url": "https://example.com/legal"
}
The next file is the OpenAPI spec. It is often easier to create by just giving ChatGPT a basic description and it will often create one with little to no errors, sometimes a few changes are needed to be validated by OpenAI when installing the ChatGPT plugin.
Prompt
ChatGPT May 24 Version
Create an OpenAPI specification that will post SWI-Prolog code and return the results of compiling the code, either success or the errors.
Completion
Sure! Here’s an example of an OpenAPI specification (using the OpenAPI 3.0 syntax) for an endpoint that accepts SWI-Prolog code as input and returns the results of compiling the code, including success or any errors encountered.
openapi: 3.0.3
info:
title: SWI-Prolog Compiler API
version: 1.0.0
servers:
- url: http://api.example.com/prolog
paths:
/compile:
post:
summary: Compile SWI-Prolog code
requestBody:
content:
application/json:
schema:
type: object
properties:
code:
type: string
description: SWI-Prolog code to compile
required:
- code
responses:
'200':
description: Compilation result
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
description: Indicates whether the compilation was successful
errors:
type: array
items:
type: string
description: Compilation errors (if any)
'400':
description: Invalid request
'500':
description: Internal server error
This OpenAPI specification defines a single endpoint, /compile
, which accepts a POST request with a JSON payload containing the SWI-Prolog code to be compiled. The response will include a success
field indicating whether the compilation was successful and an errors
field containing any compilation errors encountered.
You can use this specification to generate API documentation, client SDKs, or server stubs using tools like Swagger UI or OpenAPI Generator.
The ChatGPT generated OpenAPI specification was close to the working version, here is the working version.
openapi: 3.1.0
info:
title: SWI-Prolog Compiler API
description: A plugin to compile SWI-Prolog and return success or errors.
version: 0.0.0.1
servers:
- url: http://localhost:5003
paths:
/compile:
post:
operationId: compileCode
summary: Compile SWI-Prolog code
requestBody:
content:
application/json:
schema:
type: object
properties:
code:
type: string
description: SWI-Prolog code to compile
required:
- code
responses:
'200':
description: Compilation result
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
description: Indicates whether the compilation was successful
errors:
type: array
items:
type: string
description: Compilation errors (if any)
'400':
description: Invalid request
'500':
description: Internal server error
The logo is created as PNG file and uses the SWI-Prolog owl which can be found in most installation directories of SWI-Prolog.
For this example SWI-Prolog MQI will be used so that the HTTP server with CORS can be created using Python. Tired to create the HTTP server with CORS for this using SWI-Prolog but could never get it to work. Seems HTTP servers with CORS created with Python just work as expected with ChatGPT plugins.
To use SWI-Prolog MQI with this code ran
pip install swiplserver
which created files in C:\Users\Eric\AppData\Local\Programs\Python
While I did not try to create a single ChatGPT prompt that would create the Python code using the OpenAPI specification and some other constraints such as using CORS, adding routes for the three static files and including SWI-Prolog MQI much of the Python code was created and refined using ChatGPT prompts.
As this example is a proof of concept rather than production quality code there are many places that optimizations can be made.
For the HTTP server with CORS created with Python to work, some additional Prolog files needed to be created, since the Prolog code is being passed as a string and the quickest way to compile it was to place the Prolog code from ChatGPT into a file, then compile it. Also when using load_files/2 the error messages need to be captured from message_hook/3 which was better hidden in a Prolog file, thus compile.pl
was created to simplify the Python code.
Note: catch_messages/2 is from https://github.com/SWI-Prolog/packages-http/blob/010d4a0d0eb76b8f22f42ce4db7d75f7b555a8d1/test_proxy.pl#L672-L699. Thanks Jan W., I knew the overall idea but once I saw this code decided not to roll my own.
FIle: compile.pl
:- module(compile,[
compile/1
]).
% ----------------------------------------------------------------------------
:- meta_predicate
catch_messages(0, -).
catch_messages(Goal, Messages) :-
nb_setval(messages, []),
thread_self(Me),
setup_call_cleanup(assert((user:message_hook(Term, Kind, Lines) :-
catch_message(Me, message(Term,Kind,Lines))),
Ref),
once(Goal),
collect_messages(Messages, Ref)).
catch_message(Me, message(Term,Kind,Lines)) :-
Kind \= silent,
thread_self(Me),
!,
nb_getval(messages, L0),
message_to_string(Term, String),
( source_location(File, Line)
-> true
; File = "",
Line = 0
),
nb_linkval(messages, [message(Term,Kind,Lines,File,Line,String)|L0]).
collect_messages(Messages, Ref) :-
erase(Ref),
nb_getval(messages, L),
nb_delete(messages),
reverse(L, Messages).
% ----------------------------------------------------------------------------
compile(Errors) :-
catch_messages(
load_files(['code.pl'],[]),
Messages
),
maplist(format_message,Messages,Errors).
format_message(message(_Term,_Kind,_Lines,_File,Line,Error),error(Line,Error)).
Lastly the Python HTTP server with CORS.
FIle: main.py
from flask import Flask, jsonify, request, send_from_directory
from flask_cors import CORS
from werkzeug.exceptions import BadRequest, InternalServerError
from swiplserver import PrologMQI, PrologThread
import json
app = Flask(__name__)
CORS(app)
# Serve static files from the root directory
@app.route('/logo.png', methods=['GET'])
def serve_files():
return send_from_directory('.', 'logo.png')
@app.route('/.well-known/ai-plugin.json', methods=['GET'])
def serve_ai_plugin():
return send_from_directory('.well-known', 'ai-plugin.json')
@app.route('/openapi.yaml', methods=['GET'])
def serve_openapi():
return send_from_directory('.', 'openapi.yaml')
@app.route('/compile', methods=['POST'])
def compile_code():
try:
json_reqeust = request.get_json(force=True)
print("Request:", request.method, request.path, json_reqeust) # Print the request to the console
# Extract the 'code' from the request JSON
code = json_reqeust['code']
print("Code: ", code)
# Save code to file so it can be compiled.
with open("code.pl", "w") as file:
# Write the string to the file
file.write(code)
with PrologMQI() as mqi:
with mqi.create_thread() as prolog_thread:
result = prolog_thread.query('[compile]')
print(result)
errors = prolog_thread.query('compile:compile(Errors)')
print(errors)
error_message = []
try:
error_message = errors[0]['Errors'][0]['args'][1]
except (IndexError, KeyError):
pass
# Placeholder response for demonstration purposes
response = {
'success': True,
'Errors': error_message
}
return jsonify(response), 200
except BadRequest:
return jsonify({'error': 'Invalid request'}), 400
except Exception as e:
return jsonify({'error': str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5003)
How to install the ChatGPT plugin - SWI-Prolog REPL.
First the HTTP server needs to be running.
From a Windows command prompt
- Change directory to directory with
main.py
- python main.py
C:\Users\Groot\SWI-Prolog REPL>python main.py
* Serving Flask app 'main'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5003
* Running on http://192.168.1.143:5003
Press CTRL+C to quit
Navigate to https://chat.openai.com/
Note: Many developers prefer to use Chrome at this point as it is the one OpenAI supports and fixes the bugs fastest.
Disable caching for the browser.
Click dropdown arrow
Click Plugins
Click
Plugin store
In lower right click Develop your own plugin
For Domain
enter: localhost:5003
(change port number to one server is using)
Click Find manifest file
You can click the dropdowns to see the manifest file and OpenAPI specification file found, this is handy to make sure the correct files are accessed.
Click: Install localhost plugin
One nice debugging tool for ChatGPT is Open plugin devtools
. This can be enabled through the ChatGPT settings.
Notice that in the right of the ChatGPT page the Plugin devtools
panel opened.
The only thing new here is Prompt for ChatGPT
which is TypeScript
// Compile SWI-Prolog code and return results, success or errors.
namespace SWI_Prolog_REPL {
// Compile SWI-Prolog code
type compileCode = (_: {
// SWI-Prolog code to compile
code: string,
}) => any;
} // namespace SWI_Prolog_REPL
My take is that this is the API used by the ChatGPT web pages to communicate with the plugin. As can be seen it is very simple.
To use the plugin make sure it is activated, has a check mark in a green circle. It also helps during development if only one plugin at a time is activated.
The first prompt I run is to see if ChatGPT understands what the plugin does.
Prompt
ChatGPT May 24 Version
What can SWI-Prolog REPL plugin do?
Completion
The SWI-Prolog REPL (Read-Eval-Print Loop) plugin is a tool that allows you to compile and run SWI-Prolog code. Prolog is a logic programming language associated with artificial intelligence and computational linguistics.
Here’s what you can do with the SWI-Prolog REPL plugin:
-
Compile and Run Prolog Code: You can write Prolog code and have it compiled and run within the chat. This is useful for testing small snippets of code or for demonstrating how Prolog works.
-
Debug Prolog Code: If there are errors in your Prolog code, the REPL will provide error messages that can help you debug your code.
-
Interactive Programming: The REPL nature of the plugin allows for interactive programming. You can run a piece of code, see the output, then modify and rerun the code based on the output.
-
Teaching and Learning: The plugin can be a useful tool for teaching and learning Prolog. You can demonstrate Prolog concepts and students can practice writing Prolog code.
Remember that the plugin is a tool for running Prolog code and does not provide any Prolog-specific features beyond what is available in the SWI-Prolog system. It also does not provide features like code completion or syntax highlighting that you might find in a dedicated Prolog development environment.
The next prompt is to create some example prompts to try with the plugin, sometimes ChatGPT comes up with some interesting prompts.
Prompt
ChatGPT May 24 Version
Create some simple prompts to test out SWI-Prolog REPL plugin. Do not create API calls but prompts a real person would create.
Completion
Sure, here are some prompts that a real person might create to test out the SWI-Prolog REPL plugin:
-
Fact and Rule Creation: “Can you write a Prolog program that defines a parent-child relationship? Let’s say that John is the parent of Jim, and Jim is the parent of Tim. Then, define a rule to determine if someone is a grandparent.”
-
List Manipulation: “Can you write a Prolog program that defines a predicate to find the last element of a list?”
-
Recursion: “Can you write a Prolog program that uses recursion to calculate the factorial of a number?”
-
Path Finding: “Can you write a Prolog program that defines a simple graph with nodes and edges, and then finds a path between two nodes?”
-
Pattern Matching: “Can you write a Prolog program that uses pattern matching to split a list into its head and tail?”
-
Sorting: “Can you write a Prolog program that sorts a list of numbers in ascending order?”
-
Predicate Logic: “Can you write a Prolog program that defines a predicate to determine if a number is even?”
Remember, these are prompts for testing the SWI-Prolog REPL plugin. The actual Prolog code would be written in response to these prompts.
Now to test the ChatGPT plugin - SWI-Prolog REPL.
Prompt
ChatGPT May 24 Version
Can you write a Prolog program that defines a parent-child relationship? Let’s say that John is the parent of Jim, and Jim is the parent of Tim. Then, define a rule to determine if someone is a grandparent.
Completion
Notes: The first time the code passed to the SWI-Prolog plugin is not formatted correctly and returns an error. Once ChatGPT sees the error it corrects the formatting of the code and sends it to the plugin again. The second time the code does compile clean.
Next we give some invalid code.
Prompt
ChatGPT May 24 Version
Create some Prolog code with a syntax error to test the SWI-Prolog REPL plugin.
Completion
Notes: The first time ChatGPT tried to create Prolog code with a syntax error it failed but it recognized that the error was not a syntax error and tried again, this time getting it correct. The use of Used unknown plugin
is common among many plugins, still have not seen a minimal working example that can reproduce it so that OpenAI can fix the problem.
Here ia a partial listing of the Python HTTP server console.
127.0.0.1 - - [10/Jun/2023 15:35:42] "OPTIONS /logo.png HTTP/1.1" 200 -
127.0.0.1 - - [10/Jun/2023 15:35:42] "GET /logo.png HTTP/1.1" 200 -
127.0.0.1 - - [10/Jun/2023 15:40:02] "OPTIONS /compile HTTP/1.1" 200 -
Request: POST /compile {'code': 'parent(john jim).\nparent(jim, tim).\ngrandparent(X, Z) :- parent(X, Y), parent(Y, Z).'}
Code: parent(john jim).
parent(jim, tim).
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
True
[{'Errors': [{'args': [1, 'c:/users/eric/projects/chatgpt plugin/python based/eric/swi-prolog repl/code.pl:1:12: Syntax error: Operator expected'], 'functor': 'error'}]}]
127.0.0.1 - - [10/Jun/2023 15:40:03] "POST /compile HTTP/1.1" 200 -
127.0.0.1 - - [10/Jun/2023 15:40:20] "OPTIONS /logo.png HTTP/1.1" 200 -
127.0.0.1 - - [10/Jun/2023 15:40:20] "GET /logo.png HTTP/1.1" 200 -
127.0.0.1 - - [10/Jun/2023 15:40:22] "GET /logo.png HTTP/1.1" 200 -
127.0.0.1 - - [10/Jun/2023 15:40:49] "OPTIONS /compile HTTP/1.1" 200 -
Request: POST /compile {'code': 'This is not valid Prolog code.'}
Code: This is not valid Prolog code.
True
[{'Errors': [{'args': [1, 'c:/users/eric/projects/chatgpt plugin/python based/eric/swi-prolog repl/code.pl:1:12: Syntax error: Operator expected'], 'functor': 'error'}]}]
127.0.0.1 - - [10/Jun/2023 15:40:50] "POST /compile HTTP/1.1" 200 -
127.0.0.1 - - [10/Jun/2023 15:41:37] "OPTIONS /compile HTTP/1.1" 200 -
Request: POST /compile {'code': 'father(john, jim).\nfather(john, ann).\nbrother(X, Y) :- father(Z, X), father(Z, Y), X \\= Y.'}
Code: father(john, jim).
father(john, ann).
brother(X, Y) :- father(Z, X), father(Z, Y), X \= Y.
True
[{'Errors': []}]
127.0.0.1 - - [10/Jun/2023 15:41:38] "POST /compile HTTP/1.1" 200 -
127.0.0.1 - - [10/Jun/2023 15:41:51] "OPTIONS /compile HTTP/1.1" 200 -
Request: POST /compile {'code': 'father(john, jim)\nfather(john, ann).\nbrother(X, Y) :- father(Z, X), father(Z, Y), X \\= Y.'}
Code: father(john, jim)
father(john, ann).
brother(X, Y) :- father(Z, X), father(Z, Y), X \= Y.
True
[{'Errors': [{'args': [1, 'c:/users/eric/projects/chatgpt plugin/python based/eric/swi-prolog repl/code.pl:1:18: Syntax error: Operator expected'], 'functor': 'error'}]}]
127.0.0.1 - - [10/Jun/2023 15:41:52] "POST /compile HTTP/1.1" 200 -
So there is much work to be done with this ChatGPT plugin - SWI-Prolog REPL but it does show that as a proof of concept it can be done.