Direct openai client, clean up deps and errors

This commit is contained in:
Ian Gulliver
2024-11-26 14:37:24 -06:00
parent c40777b689
commit 0b6e729706
5 changed files with 113 additions and 47 deletions

13
go.mod
View File

@@ -1,16 +1,3 @@
module github.com/gopatchy/p
go 1.22
require (
github.com/openai/openai-go v0.1.0-alpha.38
github.com/samber/lo v1.47.0
)
require (
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
golang.org/x/text v0.16.0 // indirect
)

16
go.sum
View File

@@ -1,16 +0,0 @@
github.com/openai/openai-go v0.1.0-alpha.38 h1:j/rL0aEIHWnWaPgA8/AXYKCI79ZoW44NTIpn7qfMEXQ=
github.com/openai/openai-go v0.1.0-alpha.38/go.mod h1:3SdE6BffOX9HPEQv8IL/fi3LYZ5TUpRYaqGQZbyk11A=
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=

27
main.go
View File

@@ -7,13 +7,12 @@ import (
"net/http"
"os"
"strings"
"github.com/openai/openai-go"
)
type PHandler struct {
tmpl *template.Template
pd *pdClient
oai *oaiClient
mux *http.ServeMux
}
@@ -29,9 +28,15 @@ func NewPHandler(routingKey string) (*PHandler, error) {
return nil, fmt.Errorf("static/index.html: %w", err)
}
oai, err := newOAIClientFromEnv()
if err != nil {
return nil, fmt.Errorf("newOAIClientFromEnv: %w", err)
}
ph := &PHandler{
tmpl: tmpl,
pd: newPDClient(routingKey),
oai: oai,
mux: http.NewServeMux(),
}
@@ -67,7 +72,7 @@ func (ph *PHandler) serveRoot(w http.ResponseWriter, r *http.Request) {
err = ph.pd.sendAlert(m)
if err != nil {
sendError(w, http.StatusInternalServerError, "Send PD alert: %s", err)
sendError(w, http.StatusInternalServerError, "Error sending to PagerDuty: %s", err)
return
}
sendResponse(w, "Page sent")
@@ -88,22 +93,16 @@ func (ph *PHandler) serveSuggest(w http.ResponseWriter, r *http.Request) {
return
}
c := openai.NewClient()
comp, err := c.Chat.Completions.New(r.Context(), openai.ChatCompletionNewParams{
Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
openai.SystemMessage(`You are an assistant helping users to write good text to include in an urgent page sent to a person. Good page text contains a very brief description of the problem (e.g. "down" or "slow"), the systems it affects (acronyms for system names are fine), the identity of the sender (first names are fine), and how to contact them (e.g. a phone number or incident Slack channel). The request will consist of just the user's proposed page text. Respond with just a very brief message suggesting improvements that the sender might make or saying "Looks good -- send it!". Remember that the user is likely in an urgent, stressful situation, so make your response brief and err on the side of assuming that the message is sufficient if the text might be OK. Assume that the recipient already knows the message is urgent so the sender doesn't have to specify urgency.`),
openai.UserMessage(m),
}),
Model: openai.F(openai.ChatModelGPT4o),
})
comp, err := ph.oai.completeChat(
`You are an assistant helping users to write good text to include in an urgent page sent to a person. Good page text contains a very brief description of the problem (e.g. "down" or "slow"), the systems it affects (acronyms for system names are fine), the identity of the sender (first names are fine), and how to contact them (e.g. a phone number or incident Slack channel). The request will consist of just the user's proposed page text. Respond with just a very brief message suggesting improvements that the sender might make or saying "Looks good -- send it!". Remember that the user is likely in an urgent, stressful situation, so make your response brief and err on the side of assuming that the message is sufficient if the text might be OK. Assume that the recipient already knows the message is urgent so the sender doesn't have to specify urgency.`,
m,
)
if err != nil {
sendError(w, http.StatusInternalServerError, "OpenAI error: %s", err)
return
}
sendSuggestResponse(w, comp.Choices[0].Message.Content)
sendSuggestResponse(w, comp)
}
var allowedEnvs = []string{

94
openai.go Normal file
View File

@@ -0,0 +1,94 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type oaiClient struct {
c *http.Client
apiKey string
}
type oaiRequest struct {
Model string `json:"model"`
Messages []oaiMessage `json:"messages"`
}
type oaiMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
type oaiResponse struct {
Choices []oaiChoice `json:"choices"`
}
type oaiChoice struct {
Message oaiMessage `json:"message"`
}
func newOAIClient(apiKey string) *oaiClient {
return &oaiClient{
c: &http.Client{},
apiKey: apiKey,
}
}
func newOAIClientFromEnv() (*oaiClient, error) {
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
return nil, fmt.Errorf("OPENAI_API_KEY is not set")
}
return newOAIClient(apiKey), nil
}
func (oai *oaiClient) completeChat(system, user string) (string, error) {
buf := &bytes.Buffer{}
err := json.NewEncoder(buf).Encode(&oaiRequest{
Model: "gpt-4o",
Messages: []oaiMessage{
{Role: "system", Content: system},
{Role: "user", Content: user},
},
})
if err != nil {
return "", err
}
req, err := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", buf)
if err != nil {
return "", err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", oai.apiKey))
req.Header.Set("Content-Type", "application/json")
resp, err := oai.c.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("%s", string(body))
}
dec := json.NewDecoder(resp.Body)
var res oaiResponse
err = dec.Decode(&res)
if err != nil {
return "", err
}
return res.Choices[0].Message.Content, nil
}

View File

@@ -45,24 +45,26 @@ func (pd *pdClient) sendAlert(msg string) error {
})
if err != nil {
return fmt.Errorf("Encode PD alert: %s", err)
return err
}
req, err := http.NewRequest("POST", "https://events.pagerduty.com/v2/enqueue", buf)
if err != nil {
return fmt.Errorf("Create HTTP request: %s", err)
return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := pd.c.Do(req)
if err != nil {
return fmt.Errorf("Call PagerDuty: %s", err)
return err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 202 {
return fmt.Errorf("PagerDuty error: %s", string(body))
return fmt.Errorf("%s", string(body))
}
return nil