llmweb.go (2532B)
1 package main 2 3 import ( 4 "fmt" 5 "html/template" 6 "log" 7 "net" 8 "net/http" 9 "os" 10 "path" 11 12 "olowe.co/x/openai" 13 ) 14 15 type Chat struct { 16 client *openai.Client 17 template *template.Template 18 } 19 20 func (c *Chat) ServeHTTP(w http.ResponseWriter, req *http.Request) { 21 log.Println(req.RemoteAddr, req.Method, req.URL) 22 chat := openai.Chat{ 23 Model: "mistral-small-latest", 24 Messages: []openai.Message{ 25 {openai.RoleSystem, ""}, 26 }, 27 } 28 29 if req.Method == http.MethodGet { 30 if err := c.template.Execute(w, &chat); err != nil { 31 log.Println(err) 32 } 33 return 34 } else if req.Method != http.MethodPost { 35 w.WriteHeader(http.StatusMethodNotAllowed) 36 return 37 } 38 39 if err := req.ParseForm(); err != nil { 40 http.Error(w, err.Error(), http.StatusBadRequest) 41 return 42 } 43 log.Printf("%+v\n", req.PostForm) 44 45 if sys, ok := req.PostForm[openai.RoleSystem]; ok { 46 chat.Messages[0].Content = sys[0] 47 } 48 49 nuser := len(req.PostForm[openai.RoleUser]) 50 nassistant := len(req.PostForm[openai.RoleAssistant]) 51 if nuser != nassistant+1 { 52 e := fmt.Sprintf("expected %d user messages for %d assistant messages, got %d", nassistant+1, nassistant, nuser) 53 http.Error(w, e, http.StatusBadRequest) 54 return 55 } 56 57 for i := 0; i < nassistant; i++ { 58 user := openai.Message{openai.RoleUser, req.PostForm[openai.RoleUser][i]} 59 chat.Messages = append(chat.Messages, user) 60 reply := openai.Message{openai.RoleAssistant, req.PostForm[openai.RoleAssistant][i]} 61 chat.Messages = append(chat.Messages, reply) 62 } 63 latest := openai.Message{openai.RoleUser, req.PostForm[openai.RoleUser][nuser-1]} 64 chat.Messages = append(chat.Messages, latest) 65 66 reply, err := c.client.Complete(&chat) 67 if err != nil { 68 http.Error(w, err.Error(), http.StatusInternalServerError) 69 return 70 } 71 chat.Messages = append(chat.Messages, *reply) 72 c.template.Execute(w, &chat) 73 } 74 75 func servePWA(w http.ResponseWriter, req *http.Request) { 76 log.Println(req.RemoteAddr, req.Method, req.URL) 77 http.ServeFile(w, req, "manifest.json") 78 } 79 80 func main() { 81 confDir, err := os.UserConfigDir() 82 if err != nil { 83 log.Fatal(err) 84 } 85 config, err := readConfig(path.Join(confDir, "openai")) 86 if err != nil { 87 log.Fatalf("read configuration: %v", err) 88 } 89 client := &openai.Client{http.DefaultClient, config.Token, config.BaseURL} 90 91 tmpl, err := template.ParseGlob("*.html") 92 if err != nil { 93 log.Fatal(err) 94 } 95 96 ln, err := net.Listen("tcp", ":8080") 97 if err != nil { 98 log.Fatal(err) 99 } 100 http.Handle("/", &Chat{client, tmpl}) 101 http.HandleFunc("/manifest.json", servePWA) 102 log.Fatal(http.Serve(ln, nil)) 103 }