I previously covered how to run your own ChatGPT script with Python and Golang. But what if you want to create a script that automatically runs the ChatGPT generated code? That is what I will cover in this post. The idea is really simple:
Create a script that asks for user input
Pass the input to ChatGPT
Run the ChatGPT generated code
NOTE: Please read the caveats at the end of the post before using this!
Calling The OpenAI API
First step is simple, just call the OpenAI API with the user input. If "-v" is given as the first argument, print the raw answer and usage statistics as well.
#!/usr/bin/python3
import openai
import sys
openai.api_key = 'sk-xxx'
verbose = sys.argv[1] == '-v'
prompt = ' '.join(sys.argv[2 if verbose else 1:])
resp = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are Python code generator. Answer with just the Python code."},
{"role": "user", "content": prompt},
]
)
data = resp['choices'][0]['message']['content']
if verbose: print(data, 'Usage was:', resp['usage'], sep='\n')
Parsing the Code From Response
A rather simplistic implementation looks for set of start tokens and end tokens and returns the code between them. This is not perfect, but it works as long as the ChatGPT response does not contain multiple code blocks.
# Extract lines of code from the response tagged with ``` or ```python
lines = []
ok_start = ['```', '```python', '```py', '```python3', '```py3']
started = False
for line in data.splitlines():
if not started:
if line.strip() in ok_start:
started = True
else:
if line.strip() == '```': break
lines.append(line)
Running The Code
The code is then written to a temporary file and executed. After running, the file is deleted.
import os
import tempfile
# Create a temporary file
with tempfile.NamedTemporaryFile(mode='w', delete=False) as tf:
tf.write("#!/usr/bin/env python3\n")
# Write lines to the file
for line in lines:
tf.write(line + '\n')
# Make the file executable
os.chmod(tf.name, 0o755)
# Run the file with python3
os.system(tf.name)
# Delete the file
os.unlink(tf.name)
Trying It Out
The whole script is available here for your convenience. If you are on Linux, you can chmod +x the script and run it directly. Otherwise, you can run it with python3 pyai.py:
Omitting the -v argument will only run the generated code:
$ pyai print out 10 first primes
2
3
5
7
11
13
17
19
23
29
$ pyai calculate sha1 checksum of file pyai
SHA1 checksum of pyai: db5ab334ca2af7508460d5ade8dac7fedd48bcf0
$ sha1sum pyai
db5ab334ca2af7508460d5ade8dac7fedd48bcf0 pyai
Python has pretty powerful libraries, so you can access the web as well:
$ pyai print out longest h1 tag content text from https://codeandlife.com
Using a Soundcard as an Oscilloscope to Decode 433 MHz Smart Power Plug Remote Control Signals
$ # If you want to know what the code (and usage) was, use -v in the beginning
$ pyai -v print out longest h1 tag content text from https://codeandlife.com
```python
import requests
from bs4 import BeautifulSoup
url = "https://codeandlife.com"
response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")
h1_list = soup.find_all("h1")
longest_h1 = max(h1_list, key=lambda x: len(x.text))
print(longest_h1.text.strip())
```
Usage was:
{
"completion_tokens": 76,
"prompt_tokens": 41,
"total_tokens": 117
}
Using a Soundcard as an Oscilloscope to Decode 433 MHz Smart Power Plug Remote Control Signals
Yes you can read files too:
$ pyai print out contents of /etc/passwd
# And the contents of /etc/passwd come here :D
$ pyai read /etc/passwd, split by colon and print the largest 3rd entry value
65534
Conclusions and Caveats
While the code is simple and has many limitations, it is staggeringly powerful. You can do pretty much anything you can do with Python with this. But the downside is it's like the "I'm feeling lucky" version of scripting. You don't know what the code is going to do. You can't even see the code before running it. So, use this with caution! Potential drawbacks may include, but are not limited to:
Racking up your OpenAI API usage and costing you money
ChatGPT getting it wrong and the code not working
Code working but doing something you didn't expect
Overwriting (important) files
Getting really malicious code that does something bad
Currently code injection to LLMs is pretty hard, but it is not unthinkable that someone will find a way to do it. So, please be careful when using this. I am not responsible for any damage caused by this script!
So, I started writing a file database and toolset called
fileson to take advantage of
AWS S3 Glacier Deep Archive
(let's just call it GDA from now on). With 1 €/mo/TB storage cost, it
is essentially a dirt cheap option to store very infrequently accessed data
like offsite backups.
Why not just use rclone? Well, I disliked the
fact that all tools do a ton of (paid) queries against S3 when syncing.
I thought a simple JSON file database should work to keep track what to
copy and delete. Well, that work is progressing, but as a part of that...
Encrypting on the fly with Python and Pycrypto(dome)
I started thinking that client side encryption would be
useful as well. AES is tried and tested, and it's easy to find sample
code
to do it. But it seems wasteful to first create encrypted files on your
hard drive, then upload them to AWS and finally delete everything.
Luckily, the Python AWS SDK boto3
has a great example on how to upload a file to
S3
with upload_fileobj that accepts "a readable file-like object". What does that
mean? Let's find out!
Continuing from my Project Euler Problem
1,
here are the next two Euler problems and some reflection!
Project Euler Problem 2
The second problem asks to find the sum
of even Fibonacci numbers
numbers below four million. For procedural languages, this is quite walk in the
park, but for me a concise method to produce the series in Clojure proved a bit
of a brain nugget!
Python 3
For Python, I decided to make a simple generator that yields the Fibonacci
numbers below a given treshold N. Rest is just list comprehension:
def fib(N):
i, j = 0, 1
while j < N:
yield(j)
i, j = j, i+j
print(sum(i for i in fib(4e6) if i%2==0))
C++
In C++ it's natural to just loop with a conditional addition. This
time I decided to drop using namespace std; to save a few characters.
Without i, j = j, i+j syntax, the temporary variable ij is a bit ugly.
#include <iostream>
int main() {
int i=0, j=1, s=0;
while(j<4000000) {
if(j%2==0) s += j;
int ij = i + j; i = j; j = ij;
}
std::cout << s << std::endl;
}
}
I've long thought about learning
Lisp. Some time
ago I ran across Clojure and the map, set and
vector implementations felt like a modern addition. It's running on top of
JVM which is nice in
a sense, but a major pain in the ass when installing. Well, I swallowed my
wow to never install JDK again and got Clojure running quite painlessly.
To learn the ropes, I thought it would be a fun idea to rewrite Project
Euler problems 1-10 with Clojure to see how
it would compare against my language of choice for these types of algorithmic
problems, Python.
Just to make it more fun, I decided to implement them in my second favorite
language, Javascript as well. Since ECMAScript
9,
it's actually a pretty powerful and concise language, and coupling it with
Node.js unleashes some serious usage options outside
just web pages. Npm dependency hell sucks, but that's a topic for another post.
For more bare metal, I'm also doing C++ (basically sticking to C functionality
until maps and sets make themselves useful). Let's see how the languages stack up!
Project Euler Problem 1
The first problem in Project Euler is simple, add up numbers below 1000 that
are divisible with either 3 or 5 (or both). Modulo operator % can be used
to get the remainder of a number when divided by 3 and 5, and basically we
add all numbers that have either modulo as zero.
Python 3
With Python, the most concise implementation is to use the sum(iterable) and
a list comprehension, netting us a nice one-liner:
print(sum(i for i in range(1000) if i%3==0 or i%5==0))
Having just spent 4 hours trying to get a Python pseudocode version of PBKDF2 to match with hashlib.pbkdf2_hmac() output, I thought I'll post Yet Another Example how to do it. I thought I could just use hashlib.sha256 to calculate the steps, but turns out HMAC is not just a concatenation of password, salt and counter.
So, without further ado, here's a 256 bit key generation with password and salt:
import hashlib, hmac
def pbkdf2(pwd, salt, iter):
h = hmac.new(pwd, digestmod=hashlib.sha256) # create HMAC using SHA256
m = h.copy() # calculate PRF(Password, Salt+INT_32_BE(1))
m.update(salt)
m.update(b'\x00\x00\x00\x01')
U = m.digest()
T = bytes(U) # copy
for _ in range(1, iter):
m = h.copy() # new instance of hmac(key)
m.update(U) # PRF(Password, U-1)
U = m.digest()
T = bytes(a^b for a,b in zip(U,T))
return T
pwd = b'password'
salt = b'salt'
# both should print 120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b
print(pbkdf2(pwd, salt, 1).hex())
print(hashlib.pbkdf2_hmac('sha256', pwd, salt, 1).hex())
# both should print c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a
print(pbkdf2(pwd, salt, 4096).hex())
print(hashlib.pbkdf2_hmac('sha256', pwd, salt, 4096).hex())
Getting from pseudocode to actual working example was surprisingly hard, especially since most implementations on the web are on lower level languages, and Python results are mostly just using a library.
Simplifying the pseudo code further
If you want to avoid the new...update...digest and skip the hmac library altogether,
the code becomes even simpler. HMAC is quite simple
to implement with Python. Here's gethmac function hard-coded to SHA256 and an even shorter pbkdf2:
WebSocket is a protocol that allows for real-time, bidirectional communication between a client and a server. It is often used in web applications to enable features such as chat, live updates, and multiplayer games.
In this tutorial, I will show you how to create a minimalistic WebSocket server using Go and the nhooyr websocket library, and a JavaScript client to test it out. You will learn how to handle WebSocket connections, send and receive messages, and close the connection when necessary.
By the end of this tutorial, you will have a working WebSocket server and client that you can use as a starting point for your own WebSocket-based applications.
Setting up the project
You should first set up a simple "Hello world" go project, something along the lines of this tutorial. After you have a project going, let's install nhooyr.io/websocket WebSocket library (Go's own seems deprecated and Gorilla development has ceased some years ago):
$ go get nhooyr.io/websocket
The whole system will consist of main.go that will contain a simple net/http server that will:
Serve a simple WebSocket echo server at /echo
Serve static files from static subfolder – essentially other addresses including / will try content from there. We'll
put index.html under that subfolder.
Basic webserver stuff:
func main() {
address := "localhost:1234"
http.HandleFunc("/echo", echoHandler)
log.Printf("Starting server, go to http://%s/ to try it out!", address)
http.Handle("/", http.FileServer(http.Dir("static")))
err := http.ListenAndServe(address, nil)
log.Fatal(err)
}
Now the echoHandler will do a few essential items:
Upgrade the connection into a WebSocket one with websocket.Accept
Log errors and defer connection close in case of errors
Loop forever (or actually 10 minutes in this sample), reading messages from
the socket and writing them back.
Note that I've used InsecureSkipVerify to accept connections from any
origin, you might want to modify the code for a tighter policy: