aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2025-01-14 18:22:11 -0500
committerXe Iaso <me@xeiaso.net>2025-01-14 18:22:19 -0500
commit2fbc950b7da60087930c8ec3cd667ef70c889f2c (patch)
treebf69ab8af9214838aed0a689eb16cb5a7a143ccb /cmd
parent432b1128b43d7ad34b89866f6ce8ab93b6c2e7d3 (diff)
downloadx-2fbc950b7da60087930c8ec3cd667ef70c889f2c.tar.xz
x-2fbc950b7da60087930c8ec3cd667ef70c889f2c.zip
uber fix
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/nomadicdemo/main.go112
-rw-r--r--cmd/nomadicdemo/main.templ34
-rw-r--r--cmd/nomadicdemo/main_templ.go142
-rw-r--r--cmd/orodyagzou/.gitignore3
-rw-r--r--cmd/orodyagzou/main.go31
5 files changed, 307 insertions, 15 deletions
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) {
+ <style>
+ .big-input {
+ padding: 0.5rem;
+ width: 100%;
+ }
+</style>
+ <form>
+ <input class="big-input" id="prompt" name="prompt" type="text" value={ prompt }/>
+ <br/>
+ <input class="big-input" id="negative_prompt" name="negative_prompt" type="text" value={ negPrompt }/>
+ <br/>
+ <button>Submit</button>
+ </form>
+ if imageURL != "" {
+ <div id="image">
+ <img
+ width="100%"
+ src={ imageURL }
+ />
+ <p>Generated in { howLong.String() }</p>
+ </div>
+ }
+}
+
+templ ohNoes(why string) {
+ <big>Oh noes!</big>
+ <p>{ why }</p>
+ <p>Audience: please laugh.</p>
+}
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, "<style>\n\t.big-input {\n \tpadding: 0.5rem;\n \twidth: 100%;\n\t}\n</style><form><input class=\"big-input\" id=\"prompt\" name=\"prompt\" type=\"text\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var2 string
+ templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(prompt)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `main.templ`, Line: 13, Col: 79}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><br><input class=\"big-input\" id=\"negative_prompt\" name=\"negative_prompt\" type=\"text\" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var3 string
+ templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(negPrompt)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `main.templ`, Line: 15, Col: 100}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\"><br><button>Submit</button></form>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if imageURL != "" {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div id=\"image\"><img width=\"100%\" src=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var4 string
+ templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(imageURL)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `main.templ`, Line: 23, Col: 18}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"><p>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, "</p></div>")
+ 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, "<big>Oh noes!</big><p>")
+ 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, "</p><p>Audience: please laugh.</p>")
+ 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