|
| 1 | +# Daemon Tools (Advanced) |
| 2 | + |
| 3 | +One advanced use case that GPTScript supports is daemon tools. |
| 4 | +A daemon tool is a tool that starts a long-running HTTP server in the background, that will continue running until GPTScript is done executing. |
| 5 | +Other tools can easily send HTTP POST requests to the daemon tool. |
| 6 | + |
| 7 | +## Example |
| 8 | + |
| 9 | +Here is an example of a daemon tool with a simple echo server written in an inline Node.js script: |
| 10 | + |
| 11 | +``` |
| 12 | +Tools: my-daemon |
| 13 | +Param: first: the first parameter |
| 14 | +Param: second: the second parameter |
| 15 | +
|
| 16 | +#!http://my-daemon.daemon.gptscript.local/myPath |
| 17 | +
|
| 18 | +--- |
| 19 | +Name: my-daemon |
| 20 | +
|
| 21 | +#!sys.daemon node |
| 22 | +
|
| 23 | +const http = require('http'); |
| 24 | +
|
| 25 | +const server = http.createServer((req, res) => { |
| 26 | + if (req.method === 'GET' || req.method === 'POST') { |
| 27 | + // Extract the path from the request URL |
| 28 | + const path = req.url; |
| 29 | +
|
| 30 | + let body = ''; |
| 31 | +
|
| 32 | + req.on('data', chunk => { |
| 33 | + body += chunk.toString(); |
| 34 | + }) |
| 35 | +
|
| 36 | + // Respond with the path and body |
| 37 | + req.on('end', () => { |
| 38 | + res.writeHead(200, { 'Content-Type': 'text/plain' }); |
| 39 | + res.write(`Body: ${body}\n`); |
| 40 | + res.end(`Path: ${path}`); |
| 41 | + }) |
| 42 | + } else { |
| 43 | + res.writeHead(405, { 'Content-Type': 'text/plain' }); |
| 44 | + res.end('Method Not Allowed'); |
| 45 | + } |
| 46 | +}); |
| 47 | +
|
| 48 | +const PORT = process.env.PORT || 3000; |
| 49 | +server.listen(PORT, () => { |
| 50 | + console.log(`Server is listening on port ${PORT}`); |
| 51 | +}); |
| 52 | +``` |
| 53 | + |
| 54 | +Let's talk about the daemon tool, called `my-daemon`, first. |
| 55 | + |
| 56 | +### The Daemon Tool |
| 57 | + |
| 58 | +The body of this tool begins with `#!sys.daemon`. This tells GPTScript to take the rest of the body as a command to be |
| 59 | +run in the background that will listen for HTTP requests. GPTScript will run this command (in this case, a Node script). |
| 60 | +GPTScript will assign a port number for the server and set the `PORT` environment variable to that number, so the |
| 61 | +server needs to check that variable and listen on the proper port. |
| 62 | + |
| 63 | +After GPTScript runs the daemon, it will send it an HTTP GET request to make sure that it is running properly. |
| 64 | +The daemon needs to respond with a 200 OK to this request. |
| 65 | +By default, the request goes to `/`, but this can be configured with the following syntax: |
| 66 | + |
| 67 | +``` |
| 68 | +#!sys.daemon (path=/api/ready) node |
| 69 | +
|
| 70 | +// (node script here) |
| 71 | +``` |
| 72 | + |
| 73 | +### The Entrypoint Tool |
| 74 | + |
| 75 | +The entrypoint tool at the top of this script sends an HTTP request to the daemon tool. |
| 76 | +There are a few important things to note here: |
| 77 | + |
| 78 | +- The `Tools: my-daemon` directive is needed to show that this tool requires the `my-daemon` tool to already be running. |
| 79 | + - When the entrypoint tool runs, GPTScript will check if `my-daemon` is already running. If it is not, GPTScript will start it. |
| 80 | +- The `#!http://my-daemon.daemon.gptscript.local/myPath` in the body tells GPTScript to send an HTTP request to the daemon tool. |
| 81 | + - The request will be a POST request, with the body of the request being a JSON string of the parameters passed to the entrypoint tool. |
| 82 | + - For example, if the script is run like `gptscript script.gpt '{"first":"hello","second":"world"}'`, then the body of the request will be `{"first":"hello","second":"world"}`. |
| 83 | + - The path of the request will be `/myPath`. |
| 84 | + - The hostname is `my-daemon.daemon.gptscript.local`. When sending a request to a daemon tool, the hostname must always start with the daemon tool's name, followed by `.daemon.gptscript.local`. |
| 85 | + - GPTScript recognizes this hostname and determines the correct port number to send the request to, on localhost. |
| 86 | + |
| 87 | +### Running the Example |
| 88 | + |
| 89 | +Now let's try running it: |
| 90 | + |
| 91 | +```bash |
| 92 | +gptscript script.gpt '{"first":"hello","second":"world"}' |
| 93 | +``` |
| 94 | + |
| 95 | +``` |
| 96 | +OUTPUT: |
| 97 | +
|
| 98 | +Body: {"first":"hello","second":"world"} |
| 99 | +Path: /myPath |
| 100 | +``` |
| 101 | + |
| 102 | +This is exactly what we expected. This is a silly, small example just to demonstrate how this feature works. |
| 103 | +A real-world situation would involve several different tools sending different HTTP requests to the daemon tool, |
| 104 | +likely with an LLM determining when to call which tool. |
| 105 | + |
| 106 | +## Real-World Example |
| 107 | + |
| 108 | +To see a real-world example of a daemon tool, check out the [GPTScript Browser tool](https://github.com/gptscript-ai/browser). |
0 commit comments