Initial Commit

main
James Mills 6 months ago
commit 202803b799
Signed by: prologic
GPG Key ID: AC4C014F1440EBD6
  1. 11
      .dockerfiles/entrypoint.sh
  2. 7
      .gitignore
  3. 45
      Dockerfile
  4. 13
      LICENSE
  5. 40
      README.md
  6. 5
      go.mod
  7. 51
      go.sum
  8. 170
      main.go
  9. BIN
      screenshot.png

@ -0,0 +1,11 @@
#!/bin/sh
[ -n "${PUID}" ] && usermod -u "${PUID}" gpt
[ -n "${PGID}" ] && groupmod -g "${PGID}" gpt
printf "Configuring gpt...\n"
[ -z "${DATA}" ] && DATA="/data"
export DATA
printf "Switching UID=%s and GID=%s\n" "${PUID}" "${PGID}"
exec su-exec gpt:gpt "$@"

7
.gitignore vendored

@ -0,0 +1,7 @@
*~
*.bak
**/.envrc
/gpt
/cmd/gpt/gpt

@ -0,0 +1,45 @@
# Build
FROM golang:alpine AS build
RUN apk add --no-cache -U build-base
RUN mkdir -p /src
WORKDIR /src
# Copy go.mod and go.sum and install and cache dependencies
COPY go.mod .
COPY go.sum .
# Install deps
RUN go mod download
# Copy sources
COPY *.go ./
# Build server binary
RUN go build
# Runtime
FROM alpine:latest
RUN apk --no-cache -U add su-exec shadow ca-certificates
ENV PUID=1000
ENV PGID=1000
RUN addgroup -g "${PGID}" gpt && \
adduser -D -H -G gpt -h /var/empty -u "${PUID}" gpt && \
mkdir -p /data && chown -R gpt:gpt /data
VOLUME /data
WORKDIR /
COPY --from=build /src/gpt /usr/local/bin/gpt
COPY .dockerfiles/entrypoint.sh /init
ENTRYPOINT ["/init"]
CMD ["gpt", "-server"]

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

@ -0,0 +1,40 @@
# GPT3 Demo
[Demo](https://ask.mills.io)
A demo of [Open AI](https://beta.openai.com)'s API. This demo implements a
basic "Ask my anything" type web application with a simple interface
(No javascript required).
![](/screenshot1.png)
## Quick Start
```console
$ git clone https://git.mills.io/prologic/gpt.git
$ cd gpt
$ go build .
```
Using the CLI:
```console
$ echo "What is the capital of Australia?" | ./gpt
The capital of Australia is Canberra.
```
Server mode:
```console
$ ./gpt -server
```
Open your web browser to https://localhost:8000/
**NB:** You will need to setup an API key at https://beta.openai.com and
set the environment variable `OPENAI_API_KEY`.
## License
Currently licensed under the terms of the [WTFPL](/LICENSE) license.

@ -0,0 +1,5 @@
module git.mills.io/prologic/gpt
go 1.18
require github.com/PullRequestInc/go-gpt3 v1.1.7

@ -0,0 +1,51 @@
github.com/PullRequestInc/go-gpt3 v1.1.7 h1:1p4tYLLWHxSVD9GMnBIjEMSGHPYHHQzGktvNJRG2tOg=
github.com/PullRequestInc/go-gpt3 v1.1.7/go.mod h1:F9yzAy070LhkqHS2154/IH0HVj5xq5g83gLTj7xzyfw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.3/go.mod h1:1ftk08SazyElaaNvmqAfZWGwJzshjCfBXDLoQtPAMNk=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200301222351-066e0c02454c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -0,0 +1,170 @@
package main
import (
"context"
"flag"
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"github.com/PullRequestInc/go-gpt3"
)
const indexTemplate = `
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>GPT3 Demo</title>
</head>
<body>
<header>
<nav>
<a href="/">Home</a>
<a href="https://git.mills.io/prologic/gpt">Source</a>
</nav>
<h1>GPT3 Demo</h1>
<p>A GPT3 Demo Web Application and API using the <a href="https://beta.openai.com/">Open AI</a> service.</p>
</header>
<main>
<h2>Ask me anything!</h2>
<form action="/" method="POST">
<input type="text" name="prompt" placeholder="Enter your prompt here..." required>
<button type="submit">Go!</button>
</form>
<p>{{ .Response }}</p>
</main>
<footer>
<p>Licensed under the <a href="https://git.mills.io/prologic/gpt/blob/master/LICENSE">WTFPL License</a></p>
</footer>
</body>
</html>
`
func render(name, tmpl string, ctx interface{}, w io.Writer) error {
t, err := template.New(name).Parse(tmpl)
if err != nil {
return err
}
return t.Execute(w, ctx)
}
type gptHandler struct {
cli gpt3.Client
engine string
}
func (h gptHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodHead || r.Method == http.MethodGet && r.URL.Path == "/health" {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "OK\n")
return
}
if r.Method == http.MethodHead || r.Method == http.MethodGet || r.Method == http.MethodPost {
switch r.URL.Path {
case "/":
ctx := struct {
Response string
}{}
if r.Method == http.MethodHead {
return
}
if r.Method == http.MethodPost {
prompt := r.FormValue("prompt")
if prompt == "" {
ctx.Response = "Error: no prompt entered"
} else {
response, err := h.cli.CompletionWithEngine(r.Context(), h.engine, gpt3.CompletionRequest{
Prompt: []string{prompt},
MaxTokens: gpt3.IntPtr(150),
Echo: false,
Temperature: gpt3.Float32Ptr(0.7),
TopP: gpt3.Float32Ptr(0),
FrequencyPenalty: 0,
PresencePenalty: 0,
})
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
for _, choice := range response.Choices {
text := strings.TrimSpace(choice.Text)
if len(text) > 0 {
ctx.Response += text
}
}
}
}
if err := render("index", indexTemplate, ctx, w); err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
return
default:
http.Error(w, "Not Found", http.StatusNotFound)
return
}
}
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func main() {
var (
engine = flag.String("engine", "text-davinci-002", "The engine to use")
server = flag.Bool("server", false, "Run in server mode")
bind = flag.String("bind", "0.0.0.0:8000", "[interface]:<port> to bind to in server mode")
)
flag.Parse()
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatalln("missing openai key")
}
cli := gpt3.NewClient(apiKey)
if !*server {
stdin, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatalln(err)
}
input := strings.TrimSpace(string(stdin))
ctx := context.Background()
response, err := cli.CompletionWithEngine(ctx, *engine, gpt3.CompletionRequest{
Prompt: []string{input},
MaxTokens: gpt3.IntPtr(150),
Echo: false,
Temperature: gpt3.Float32Ptr(0.7),
TopP: gpt3.Float32Ptr(0),
FrequencyPenalty: 0,
PresencePenalty: 0,
})
if err != nil {
log.Fatalln(err)
}
for _, choice := range response.Choices {
text := strings.TrimSpace(choice.Text)
if len(text) > 0 {
fmt.Println(text)
}
}
} else {
log.Fatal(http.ListenAndServe(*bind, gptHandler{cli: cli, engine: *engine}))
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 KiB

Loading…
Cancel
Save