From 2fbc950b7da60087930c8ec3cd667ef70c889f2c Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Tue, 14 Jan 2025 18:22:11 -0500 Subject: uber fix Signed-off-by: Xe Iaso --- cmd/nomadicdemo/main.go | 112 +++++++++++++++++++++++++++++++++ cmd/nomadicdemo/main.templ | 34 ++++++++++ cmd/nomadicdemo/main_templ.go | 142 ++++++++++++++++++++++++++++++++++++++++++ cmd/orodyagzou/.gitignore | 3 +- cmd/orodyagzou/main.go | 31 ++++----- 5 files changed, 307 insertions(+), 15 deletions(-) create mode 100644 cmd/nomadicdemo/main.go create mode 100644 cmd/nomadicdemo/main.templ create mode 100644 cmd/nomadicdemo/main_templ.go (limited to 'cmd') diff --git a/cmd/nomadicdemo/main.go b/cmd/nomadicdemo/main.go new file mode 100644 index 0000000..cd56018 --- /dev/null +++ b/cmd/nomadicdemo/main.go @@ -0,0 +1,112 @@ +package main + +import ( + "bytes" + "context" + "encoding/json" + "flag" + "fmt" + "log" + "log/slog" + "net/http" + "time" + + "github.com/a-h/templ" + "within.website/x/htmx" + "within.website/x/internal" + "within.website/x/web" + "within.website/x/xess" +) + +//go:generate go run github.com/a-h/templ/cmd/templ@latest generate + +var ( + apiURL = flag.String("api-url", "https://waifuwave.fly.dev/generate", "API backend URL") + bind = flag.String("bind", ":3924", "TCP address to bind to") +) + +func main() { + internal.HandleStartup() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + _ = ctx + + mux := http.NewServeMux() + htmx.Mount(mux) + xess.Mount(mux) + + mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { + prompt := r.FormValue("prompt") + negPrompt := r.FormValue("negative_prompt") + + var imageURL string + var howLong time.Duration + + if prompt != "" && negPrompt != "" { + t0 := time.Now() + output, err := getImage(r.Context(), *apiURL, prompt, negPrompt) + if err != nil { + slog.Error("can't make image", "err", err) + templ.Handler( + xess.Simple("Scale to 0 demo", ohNoes(err.Error())), + templ.WithStatus(http.StatusInternalServerError), + ).ServeHTTP(w, r) + return + } + howLong = time.Now().Sub(t0) + + imageURL = output.URL + } + + if prompt == "" { + prompt = "1girl, solo, flower, long hair, outdoors, green hair, running, smiling, parted lips, blue eyes, cute, sakura blossoms, spring, sun, blue sky, depth of field, cat ears, kimono, bow, onsen, pool, warm lighting, safe" + } + + if negPrompt == "" { + negPrompt = "crop, bad hands, worst hands, worst quality" + } + + templ.Handler( + xess.Simple("Scale to 0 demo", index(prompt, negPrompt, imageURL, howLong)), + ).ServeHTTP(w, r) + }) + + slog.Info("listening", "bind", *bind) + log.Fatal(http.ListenAndServe(*bind, mux)) +} + +func getImage(ctx context.Context, apiURL, prompt, negPrompt string) (*Output, error) { + buf := bytes.Buffer{} + + if err := json.NewEncoder(&buf).Encode(Input{negPrompt, prompt}); err != nil { + return nil, fmt.Errorf("can't encode: %w", err) + } + + resp, err := http.Post(apiURL, "application/json", &buf) + if err != nil { + return nil, fmt.Errorf("can't request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, web.NewError(http.StatusOK, resp) + } + + var result Output + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, fmt.Errorf("can't read result: %w", err) + } + + return &result, nil +} + +type Input struct { + NegativePrompt string `json:"negative_prompt"` + Prompt string `json:"prompt"` +} + +type Output struct { + Fname []string `json:"fname"` + URL string `json:"url"` +} diff --git a/cmd/nomadicdemo/main.templ b/cmd/nomadicdemo/main.templ new file mode 100644 index 0000000..52e6a8b --- /dev/null +++ b/cmd/nomadicdemo/main.templ @@ -0,0 +1,34 @@ +package main + +import "time" + +templ index(prompt, negPrompt, imageURL string, howLong time.Duration) { + +
+ +
+ +
+ +
+ if imageURL != "" { +
+ +

Generated in { howLong.String() }

+
+ } +} + +templ ohNoes(why string) { + Oh noes! +

{ why }

+

Audience: please laugh.

+} diff --git a/cmd/nomadicdemo/main_templ.go b/cmd/nomadicdemo/main_templ.go new file mode 100644 index 0000000..922def4 --- /dev/null +++ b/cmd/nomadicdemo/main_templ.go @@ -0,0 +1,142 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.819 +package main + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import "time" + +func index(prompt, negPrompt, imageURL string, howLong time.Duration) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "


") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if imageURL != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

Generated in ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(howLong.String()) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `main.templ`, Line: 25, Col: 37} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + return nil + }) +} + +func ohNoes(why string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var6 := templ.GetChildren(ctx) + if templ_7745c5c3_Var6 == nil { + templ_7745c5c3_Var6 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "Oh noes!

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(why) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `main.templ`, Line: 32, Col: 9} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "

Audience: please laugh.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/cmd/orodyagzou/.gitignore b/cmd/orodyagzou/.gitignore index c588cf3..b28ea26 100644 --- a/cmd/orodyagzou/.gitignore +++ b/cmd/orodyagzou/.gitignore @@ -1,2 +1,3 @@ *.env -gpu_names_cache.json \ No newline at end of file +gpu_names_cache.json +venv \ No newline at end of file diff --git a/cmd/orodyagzou/main.go b/cmd/orodyagzou/main.go index 5267103..bc4d19f 100644 --- a/cmd/orodyagzou/main.go +++ b/cmd/orodyagzou/main.go @@ -14,7 +14,6 @@ import ( "sync" "time" - "github.com/google/uuid" "github.com/joho/godotenv" "within.website/x/internal" "within.website/x/web/vastai/vastaicli" @@ -23,10 +22,12 @@ import ( var ( bind = flag.String("bind", ":3238", "HTTP port to bind to") diskSizeGB = flag.Int("vastai-disk-size-gb", 32, "amount of disk we need from vast.ai") - dockerImage = flag.String("docker-image", "reg.xeiaso.net/runner/sdxl-tigris:latest", "docker image to start") - onstartCmd = flag.String("onstart-cmd", "python -m cog.server.http", "onstart command to run in vast.ai") - vastaiPort = flag.Int("vastai-port", 5000, "port that the guest will use in vast.ai") - vastaiFilters = flag.String("vastai-filters", "verified=False cuda_max_good>=12.1 gpu_ram>=12 num_gpus=1 inet_down>=850", "vast.ai search filters") + dockerImage = flag.String("docker-image", "reg.xeiaso.net/xeserv/waifuwave:latest", "docker image to start") + onstartCmd = flag.String("onstart-cmd", "/opt/comfyui/startup.sh", "onstart command to run in vast.ai") + vastaiPort = flag.Int("vastai-port", 8080, "port that the guest will use in vast.ai") + vastaiFilters = flag.String("vastai-filters", "verified=True cuda_max_good>=12.1 gpu_ram>=24 num_gpus=1 inet_down>=2000", "vast.ai search filters") + + idleTimeout = flag.Duration("idle-timeout", 5*time.Minute, "how long the instance should be considered \"idle\" before it is slain") ) func main() { @@ -63,7 +64,7 @@ func main() { go images.slayLoop(ctx) mux := http.NewServeMux() - mux.Handle("/v1/images", images) + mux.Handle("/", images) fmt.Printf("http://localhost%s\n", *bind) log.Fatal(http.ListenAndServe(*bind, mux)) @@ -81,7 +82,7 @@ type ScaleToZeroProxy struct { } func (s *ScaleToZeroProxy) slayLoop(ctx context.Context) { - t := time.NewTicker(time.Minute) + t := time.NewTicker(time.Second) defer t.Stop() for { @@ -99,7 +100,7 @@ func (s *ScaleToZeroProxy) slayLoop(ctx context.Context) { continue } - if lastUsed.Add(5 * time.Minute).Before(time.Now()) { + if lastUsed.Add(*idleTimeout).Before(time.Now()) { if err := s.slay(ctx); err != nil { slog.Error("can't slay instance", "err", err) } @@ -133,11 +134,6 @@ func (s *ScaleToZeroProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { } next := httputil.NewSingleHostReverseProxy(u) - od := next.Director - next.Director = func(r *http.Request) { - od(r) - r.URL.Path = "/predictions/" + uuid.NewString() - } next.ServeHTTP(w, r) s.lock.Lock() @@ -221,14 +217,21 @@ func (s *ScaleToZeroProxy) delayUntilReady(ctx context.Context, endpointURL stri t := time.NewTicker(time.Second) defer t.Stop() + failCount := 0 + for { select { case <-ctx.Done(): return ctx.Err() case <-t.C: + if failCount >= 100 { + return fmt.Errorf("healthcheck failed %d times", failCount+1) + } + resp, err := http.Get(u.String()) if err != nil { - return fmt.Errorf("can't fetch health check: %w", err) + slog.Error("health check failed", "err", err) + continue } var status cogHealthCheck -- cgit v1.2.3