aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2023-09-26 06:18:56 -0400
committerXe Iaso <me@xeiaso.net>2023-09-26 06:18:56 -0400
commite260e2b17bbecd34afb49c50cd7dbf4d3b43fed3 (patch)
tree64f9ab425c6037509ae13b9f1a7762837150c5c4
parent2654099b2645423152de4e790ea658ff0ab632e4 (diff)
downloadx-e260e2b17bbecd34afb49c50cd7dbf4d3b43fed3.tar.xz
x-e260e2b17bbecd34afb49c50cd7dbf4d3b43fed3.zip
cmd/sapientwindex: add reddit bot
Signed-off-by: Xe Iaso <me@xeiaso.net>
-rw-r--r--cmd/sapientwindex/foo.json11
-rw-r--r--cmd/sapientwindex/llama.go112
-rw-r--r--cmd/sapientwindex/main.go163
-rw-r--r--cmd/sapientwindex/prompts/helper.txt11
-rw-r--r--cmd/sapientwindex/prompts/moderation.txt7
-rw-r--r--go.mod3
-rw-r--r--go.sum8
-rw-r--r--gomod2nix.toml9
8 files changed, 324 insertions, 0 deletions
diff --git a/cmd/sapientwindex/foo.json b/cmd/sapientwindex/foo.json
new file mode 100644
index 0000000..3b01251
--- /dev/null
+++ b/cmd/sapientwindex/foo.json
@@ -0,0 +1,11 @@
+{
+ "temperature": 0.8,
+ "top_k": 40,
+ "top_p": 0.9,
+ "stream": false,
+ "prompt": "<s>[INST] <<SYS>>\nYou are an expert in creating tulpas, also known as tulpamancy. When you are given questions from users, you will answer questions in one paragraph like a redditor with casual language. ONLY reply in plain text. DO NOT return anything but your response. DO NOT use emoji.\n\nBegin your answer with ANSWER:\n<</SYS>>\nAnswer this question:\n\nHow I can understand that tulpa is really answering me and I'm not imagining his answer?\n\nSometimes I'm really not sure with this. Maybe someone can help me?\n[/INST]",
+ "repeat_penalty": 1.15,
+ "repeat_last_n": 512,
+ "mirostat": 2,
+ "n_predict": 2048
+}
diff --git a/cmd/sapientwindex/llama.go b/cmd/sapientwindex/llama.go
new file mode 100644
index 0000000..7895832
--- /dev/null
+++ b/cmd/sapientwindex/llama.go
@@ -0,0 +1,112 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "flag"
+ "io"
+ "net/http"
+
+ "within.website/x/web"
+)
+
+var (
+ llamaServer = flag.String("llama-server", "http://kos-mos:8080/completion", "API server for LLAMA 2")
+)
+
+func Predict(opts *LLAMAOpts) (*LLAMAResponse, error) {
+ jsonData, err := json.Marshal(opts)
+ if err != nil {
+ return nil, err
+ }
+ // Make a POST request to the server
+ resp, err := http.Post(*llamaServer, "application/json", bytes.NewBuffer(jsonData))
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ // Check the response status code
+ if resp.StatusCode != http.StatusOK {
+ return nil, web.NewError(http.StatusOK, resp)
+ }
+
+ data, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var result LLAMAResponse
+
+ if err := json.Unmarshal(data, &result); err != nil {
+ return nil, err
+ }
+
+ return &result, nil
+}
+
+type LLAMAOpts struct {
+ Temperature float64 `json:"temperature"`
+ TopK int `json:"top_k"`
+ TopP float64 `json:"top_p"`
+ Stream bool `json:"stream"`
+ Prompt string `json:"prompt"`
+ RepeatPenalty float64 `json:"repeat_penalty"`
+ RepeatLastN int `json:"repeat_last_n"`
+ Mirostat int `json:"mirostat"`
+ NPredict int `json:"n_predict"`
+}
+
+type LLAMAResponse struct {
+ Content string `json:"content"`
+ GenerationSettings GenerationSettings `json:"generation_settings"`
+ Model string `json:"model"`
+ Prompt string `json:"prompt"`
+ Stop bool `json:"stop"`
+ StoppedEos bool `json:"stopped_eos"`
+ StoppedLimit bool `json:"stopped_limit"`
+ StoppedWord bool `json:"stopped_word"`
+ StoppingWord string `json:"stopping_word"`
+ Timings Timings `json:"timings"`
+ TokensCached int `json:"tokens_cached"`
+ TokensEvaluated int `json:"tokens_evaluated"`
+ TokensPredicted int `json:"tokens_predicted"`
+ Truncated bool `json:"truncated"`
+}
+
+type GenerationSettings struct {
+ FrequencyPenalty float64 `json:"frequency_penalty"`
+ Grammar string `json:"grammar"`
+ IgnoreEos bool `json:"ignore_eos"`
+ LogitBias []any `json:"logit_bias"`
+ Mirostat int `json:"mirostat"`
+ MirostatEta float64 `json:"mirostat_eta"`
+ MirostatTau float64 `json:"mirostat_tau"`
+ Model string `json:"model"`
+ NCtx int `json:"n_ctx"`
+ NKeep int `json:"n_keep"`
+ NPredict int `json:"n_predict"`
+ NProbs int `json:"n_probs"`
+ PenalizeNl bool `json:"penalize_nl"`
+ PresencePenalty float64 `json:"presence_penalty"`
+ RepeatLastN int `json:"repeat_last_n"`
+ RepeatPenalty float64 `json:"repeat_penalty"`
+ Seed int64 `json:"seed"`
+ Stop []any `json:"stop"`
+ Stream bool `json:"stream"`
+ Temp float64 `json:"temp"`
+ TfsZ float64 `json:"tfs_z"`
+ TopK int `json:"top_k"`
+ TopP float64 `json:"top_p"`
+ TypicalP float64 `json:"typical_p"`
+}
+
+type Timings struct {
+ PredictedMs float64 `json:"predicted_ms"`
+ PredictedN int `json:"predicted_n"`
+ PredictedPerSecond float64 `json:"predicted_per_second"`
+ PredictedPerTokenMs float64 `json:"predicted_per_token_ms"`
+ PromptMs float64 `json:"prompt_ms"`
+ PromptN int `json:"prompt_n"`
+ PromptPerSecond float64 `json:"prompt_per_second"`
+ PromptPerTokenMs float64 `json:"prompt_per_token_ms"`
+}
diff --git a/cmd/sapientwindex/main.go b/cmd/sapientwindex/main.go
new file mode 100644
index 0000000..dc825ba
--- /dev/null
+++ b/cmd/sapientwindex/main.go
@@ -0,0 +1,163 @@
+package main
+
+import (
+ "bytes"
+ "embed"
+ "flag"
+ "fmt"
+ "log"
+ "log/slog"
+ "strings"
+ "text/template"
+ "time"
+
+ "github.com/Marcel-ICMC/graw"
+ "github.com/Marcel-ICMC/graw/reddit"
+ "within.website/x/internal"
+)
+
+var (
+ redditUsername = flag.String("reddit-username", "", "reddit username")
+ redditPassword = flag.String("reddit-password", "", "reddit password")
+ redditAppID = flag.String("reddit-app-id", "", "reddit app id")
+ redditAppSecret = flag.String("reddit-app-secret", "", "reddit app secret")
+ subreddit = flag.String("subreddit", "shadowh511", "subreddit to post to")
+ scanDuration = flag.Duration("scan-duration", 30*time.Second, "how long to scan for")
+
+ //go:embed prompts/*.txt
+ prompts embed.FS
+)
+
+func main() {
+ internal.HandleStartup()
+
+ slog.Info("starting up", "username", *redditUsername, "subreddit", *subreddit, "scan_duration", (*scanDuration).String())
+
+ cfg := reddit.BotConfig{
+ Agent: "graw:sapientwindex:0.0.1 by /u/shadowh511",
+ App: reddit.App{
+ ID: *redditAppID,
+ Secret: *redditAppSecret,
+ Username: *redditUsername,
+ Password: *redditPassword,
+ },
+ }
+
+ bot, err := reddit.NewBot(cfg)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ handle, err := reddit.NewScript(cfg.Agent, *scanDuration)
+ if err != nil {
+ log.Fatal(err)
+ }
+ announce := &announcer{bot: bot}
+
+ scriptCfg := graw.Config{Subreddits: []string{*subreddit, "shadowh511"}}
+
+ stop, wait, err := graw.Scan(announce, handle, scriptCfg)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ defer stop()
+
+ wait()
+}
+
+type announcer struct {
+ bot reddit.Bot
+}
+
+func makePrompt(kind, title, body string) (string, error) {
+ data, err := prompts.ReadFile("prompts/" + kind + ".txt")
+ if err != nil {
+ return "", fmt.Errorf("read prompt: %w", err)
+ }
+
+ tmpl, err := template.New("prompt").Parse(string(data))
+ if err != nil {
+ return "", fmt.Errorf("parse prompts: %w", err)
+ }
+
+ var prompt bytes.Buffer
+ err = tmpl.Execute(&prompt, struct {
+ Title string
+ Body string
+ }{
+ Title: title,
+ Body: body,
+ })
+ if err != nil {
+ return "", fmt.Errorf("execute template: %w", err)
+ }
+
+ return prompt.String(), nil
+}
+
+func (a *announcer) Post(post *reddit.Post) error {
+ if post.LinkFlairText == "Personal" {
+ return nil
+ }
+
+ slog.Info("got post", "title", post.Title, "body", post.SelfText)
+
+ prompt, err := makePrompt("moderation", post.Title, post.SelfText)
+ if err != nil {
+ slog.Error("make prompt", "err", err)
+ return nil
+ }
+
+ opts := &LLAMAOpts{
+ Temperature: 0.8,
+ TopK: 40,
+ TopP: 0.9,
+ Stream: false,
+ Prompt: prompt,
+ RepeatPenalty: 1.15,
+ RepeatLastN: 512,
+ Mirostat: 2,
+ NPredict: 2048,
+ }
+
+ resp, err := Predict(opts)
+ if err != nil {
+ slog.Error("predict", "err", err)
+ return nil
+ }
+
+ if !strings.HasPrefix(strings.ToUpper(strings.TrimSpace(resp.Content)), "YES") {
+ slog.Info("not a question, skipping", "title", post.Title, "body", post.SelfText, "response", resp.Content)
+ return nil
+ }
+
+ prompt, err = makePrompt("helper", post.Title, post.SelfText)
+ if err != nil {
+ slog.Error("make prompt", "err", err)
+ return nil
+ }
+
+ opts.Prompt = prompt
+
+ resp, err = Predict(opts)
+ if err != nil {
+ slog.Error("predict", "err", err)
+ return nil
+ }
+
+ body := massageAnswer(resp.Content)
+
+ if err := a.bot.Reply(post.Name, body); err != nil {
+ slog.Error("reply", "err", err)
+ return nil
+ }
+
+ return nil
+}
+
+func massageAnswer(answer string) string {
+ answer = strings.TrimSpace(answer)
+ answer = strings.TrimPrefix(answer, "ANSWER: ")
+ return answer
+}
diff --git a/cmd/sapientwindex/prompts/helper.txt b/cmd/sapientwindex/prompts/helper.txt
new file mode 100644
index 0000000..4fe2a47
--- /dev/null
+++ b/cmd/sapientwindex/prompts/helper.txt
@@ -0,0 +1,11 @@
+<s>[INST] <<SYS>>
+You are an expert in creating tulpas, also known as tulpamancy. When you are given questions from users, you will answer questions in one paragraph like a redditor with casual language. ONLY reply in plain text. DO NOT return anything but your response. DO NOT use emoji.
+
+Begin your answer with ANSWER:
+<</SYS>>
+Answer this question:
+
+{{.Title}}
+
+{{.Body}}
+[/INST]
diff --git a/cmd/sapientwindex/prompts/moderation.txt b/cmd/sapientwindex/prompts/moderation.txt
new file mode 100644
index 0000000..e6e9620
--- /dev/null
+++ b/cmd/sapientwindex/prompts/moderation.txt
@@ -0,0 +1,7 @@
+<s>[INST] <<SYS>>
+You are the content moderator for a subreddit. Does this look like a question about tulpas or tulpamancy? Respond YES or NO.
+<</SYS>>
+{{.Title}}
+
+{{.Body}}
+[/INST] \ No newline at end of file
diff --git a/go.mod b/go.mod
index 3c81e36..9124b56 100644
--- a/go.mod
+++ b/go.mod
@@ -50,6 +50,7 @@ require (
)
require (
+ github.com/Marcel-ICMC/graw v0.0.0-20230411090719-e24cd8592d25 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/antonmedv/expr v1.15.0 // indirect
@@ -65,8 +66,10 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/miekg/dns v1.1.55 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
+ github.com/turnage/redditproto v0.0.0-20151223012412-afedf1b6eddb // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.48.0 // indirect
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1 // indirect
diff --git a/go.sum b/go.sum
index 3f815c5..31e1784 100644
--- a/go.sum
+++ b/go.sum
@@ -64,6 +64,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/Marcel-ICMC/graw v0.0.0-20230411090719-e24cd8592d25 h1:2y0Jf51U3toefyF80qgCNfd8qZEjkmWSvBX/DNhM6Mw=
+github.com/Marcel-ICMC/graw v0.0.0-20230411090719-e24cd8592d25/go.mod h1:Tc1Bv6CivnFGhW5kjO2ZN9/PMlGJ6O4cVFYkaTNWSY8=
github.com/McKael/madon/v2 v2.4.0 h1:u5bwEs7r3ek2L1KFOZ27wzHY1vY9bnG4oQDzcrbJyK4=
github.com/McKael/madon/v2 v2.4.0/go.mod h1:fSjqeQzvbKPjWQOv0VV3SPKvPCmj6Y+meOqkvQbXEnU=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
@@ -414,6 +416,7 @@ 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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@@ -440,6 +443,9 @@ github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mndrix/golog v0.0.0-20170330170653-a28e2a269775 h1:KPqf9x/eMg3ZnHATLXcM1OgQMNVkPUv1QcGv6zTRMRg=
github.com/mndrix/golog v0.0.0-20170330170653-a28e2a269775/go.mod h1:Q4YHYl483MNk6wwg3g8YsINpKe5S2UzUJCRSRlFaSU0=
github.com/mndrix/ps v0.0.0-20170330174427-18e65badd6ab h1:fPrYMvMnWuED0MLhLyrny1fLaHhtiXK30pNyBGrk9Gs=
@@ -566,6 +572,8 @@ github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64 h1:l/T7dYuJEQZOwV
github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64/go.mod h1:Q1NAJOuRdQCqN/VIWdnaaEhV8LpeO2rtlBP7/iDJNII=
github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef h1:7D6Nm4D6f0ci9yttWaKjM1TMAXrH5Su72dojqYGntFY=
github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef/go.mod h1:WLFStEdnJXpjK8kd4qKLwQKX/1vrDzp5BcDyiZJBHJM=
+github.com/turnage/redditproto v0.0.0-20151223012412-afedf1b6eddb h1:qR56NGRvs2hTUbkn6QF8bEJzxPIoMw3Np3UigBeJO5A=
+github.com/turnage/redditproto v0.0.0-20151223012412-afedf1b6eddb/go.mod h1:GyqJdEoZSNoxKDb7Z2Lu/bX63jtFukwpaTP9ZIS5Ei0=
github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A=
github.com/u-root/u-root v0.11.0 h1:6gCZLOeRyevw7gbTwMj3fKxnr9+yHFlgF3N7udUVNO8=
github.com/u-root/u-root v0.11.0/go.mod h1:DBkDtiZyONk9hzVEdB/PWI9B4TxDkElWlVTHseglrZY=
diff --git a/gomod2nix.toml b/gomod2nix.toml
index 612fe51..cd01831 100644
--- a/gomod2nix.toml
+++ b/gomod2nix.toml
@@ -7,6 +7,9 @@ schema = 3
[mod."filippo.io/edwards25519"]
version = "v1.0.0"
hash = "sha256-APnPAcmItvtJ5Zsy863lzR2TjEBF9Y66TY1e4M1ap98="
+ [mod."github.com/Marcel-ICMC/graw"]
+ version = "v0.0.0-20230411090719-e24cd8592d25"
+ hash = "sha256-oKeE+BQHXpFEU3g21Z41PeSFJicABcRYsGEs807oo40="
[mod."github.com/McKael/madon/v2"]
version = "v2.4.0"
hash = "sha256-bEkpnDMyJtzU5epFNrjLhgJW+cp5Tf4YL7lk99q0ymM="
@@ -262,6 +265,9 @@ schema = 3
[mod."github.com/mitchellh/go-ps"]
version = "v1.0.0"
hash = "sha256-HzxVHNLHZpnsBuPcub0G+9jjDcDOsxM/6wifbsxf7EY="
+ [mod."github.com/mitchellh/mapstructure"]
+ version = "v1.5.0"
+ hash = "sha256-ztVhGQXs67MF8UadVvG72G3ly0ypQW0IRDdOOkjYwoE="
[mod."github.com/mndrix/golog"]
version = "v0.0.0-20170330170653-a28e2a269775"
hash = "sha256-JD5kY0Krc1yHXT+0b3XzkwqFKpIvY3VFSok4/yHGOP8="
@@ -373,6 +379,9 @@ schema = 3
[mod."github.com/tmc/scp"]
version = "v0.0.0-20170824174625-f7b48647feef"
hash = "sha256-qHwQb3JA43VVhJdB18/2zXbxqW/bgw1yvr8YhqFyd74="
+ [mod."github.com/turnage/redditproto"]
+ version = "v0.0.0-20151223012412-afedf1b6eddb"
+ hash = "sha256-ageZnAhwB2i8VP9Qa3ChUQG2a1cJu/89e6tVFdM6qyM="
[mod."github.com/u-root/uio"]
version = "v0.0.0-20230305220412-3e8cd9d6bf63"
hash = "sha256-y0VT9PLROozi6wNMgnX706ifumQxlMc8y4/XZDhdfMY="