aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-10-25 14:06:42 -0400
committerXe Iaso <me@xeiaso.net>2024-10-25 14:06:42 -0400
commitafa4bc6c01297af78885bf0562e2dae7ff83605b (patch)
tree97a1149d5646cf9b1c7aa2892d6e849c589219cc /web
parent797eec6d94e193ae684db977179ea4a422b2499f (diff)
downloadx-afa4bc6c01297af78885bf0562e2dae7ff83605b.tar.xz
x-afa4bc6c01297af78885bf0562e2dae7ff83605b.zip
cmd: add amano and stealthmountain
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'web')
-rw-r--r--web/mistral/mistral.go14
-rw-r--r--web/vastai/error.go32
-rw-r--r--web/vastai/search.go101
-rw-r--r--web/vastai/templates.go67
-rw-r--r--web/vastai/users.go52
-rw-r--r--web/vastai/vastai.go119
6 files changed, 371 insertions, 14 deletions
diff --git a/web/mistral/mistral.go b/web/mistral/mistral.go
index 75cf9d3..3f2d9e7 100644
--- a/web/mistral/mistral.go
+++ b/web/mistral/mistral.go
@@ -4,27 +4,13 @@ import (
"bytes"
"context"
"encoding/json"
- "expvar"
"fmt"
"net/http"
- "tailscale.com/metrics"
"within.website/x/llm"
"within.website/x/web"
)
-var (
- promptTokens = metrics.LabelMap{Label: "model"}
- completionTokens = metrics.LabelMap{Label: "model"}
- totalTokens = metrics.LabelMap{Label: "model"}
-)
-
-func init() {
- expvar.Publish("gauge_x_web_mistral_prompt_tokens", &promptTokens)
- expvar.Publish("gauge_x_web_mistral_completion_tokens", &completionTokens)
- expvar.Publish("gauge_x_web_mistral_total_tokens", &totalTokens)
-}
-
type Client struct {
*http.Client
apiKey string
diff --git a/web/vastai/error.go b/web/vastai/error.go
new file mode 100644
index 0000000..89487a7
--- /dev/null
+++ b/web/vastai/error.go
@@ -0,0 +1,32 @@
+package vastai
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+)
+
+type Error struct {
+ Success bool `json:"success"`
+ ErrorKind string `json:"error"`
+ Msg string `json:"msg"`
+ StatusCode int `json:"status_code"`
+}
+
+func (e Error) Error() string {
+ return fmt.Sprintf("%s (%d): %s", e.ErrorKind, e.StatusCode, e.Msg)
+}
+
+func NewError(resp *http.Response) error {
+ var e Error
+
+ dec := json.NewDecoder(resp.Body)
+ defer resp.Body.Close()
+ if err := dec.Decode(&e); err != nil {
+ return fmt.Errorf("vastai: can't decode json while handling error: %w", err)
+ }
+
+ e.StatusCode = resp.StatusCode
+
+ return e
+}
diff --git a/web/vastai/search.go b/web/vastai/search.go
new file mode 100644
index 0000000..4ad301a
--- /dev/null
+++ b/web/vastai/search.go
@@ -0,0 +1,101 @@
+package vastai
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/url"
+)
+
+func (c *Client) SearchByTemplate(ctx context.Context, t Template) ([]Offer, error) {
+ filters := map[string]any{}
+ if err := json.Unmarshal([]byte(t.ExtraFilters), &filters); err != nil {
+ return nil, fmt.Errorf("can't unmarshal extra filters for template %s (%d): %w", t.Name, t.ID, err)
+ }
+ filters["order"] = []string{"dphtotal", "asc"}
+
+ extraFilters, err := json.Marshal(filters)
+ if err != nil {
+ return nil, fmt.Errorf("can't remarshal extra filters for template %s (%d): %w", t.Name, t.ID, err)
+ }
+
+ q := url.Values{}
+ q.Set("q", string(extraFilters))
+
+ resp, err := doJSON[struct {
+ Offers []Offer `json:"offers"`
+ }](
+ ctx, c, http.MethodGet,
+ fmt.Sprintf("/v0/bundles/%s", q.Encode()),
+ http.StatusOK,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("can't find instances with template %s (%d): %w", t.Name, t.ID, err)
+ }
+
+ return resp.Offers, nil
+}
+
+type Offer struct {
+ IsBid bool `json:"is_bid"`
+ InetUpBilled any `json:"inet_up_billed"`
+ InetDownBilled any `json:"inet_down_billed"`
+ External bool `json:"external"`
+ Webpage any `json:"webpage"`
+ Logo string `json:"logo"`
+ Rentable bool `json:"rentable"`
+ ComputeCap int `json:"compute_cap"`
+ DriverVersion string `json:"driver_version"`
+ CudaMaxGood int `json:"cuda_max_good"`
+ MachineID int `json:"machine_id"`
+ HostingType any `json:"hosting_type"`
+ PublicIpaddr string `json:"public_ipaddr"`
+ Geolocation string `json:"geolocation"`
+ FlopsPerDphtotal float64 `json:"flops_per_dphtotal"`
+ DlperfPerDphtotal float64 `json:"dlperf_per_dphtotal"`
+ Reliability2 float64 `json:"reliability2"`
+ HostRunTime int `json:"host_run_time"`
+ HostID int `json:"host_id"`
+ ID int `json:"id"`
+ BundleID int `json:"bundle_id"`
+ NumGpus int `json:"num_gpus"`
+ TotalFlops float64 `json:"total_flops"`
+ MinBid float64 `json:"min_bid"`
+ DphBase float64 `json:"dph_base"`
+ DphTotal float64 `json:"dph_total"`
+ GpuName string `json:"gpu_name"`
+ GpuRAM int `json:"gpu_ram"`
+ GpuDisplayActive bool `json:"gpu_display_active"`
+ GpuMemBw float64 `json:"gpu_mem_bw"`
+ BwNvlink int `json:"bw_nvlink"`
+ DirectPortCount int `json:"direct_port_count"`
+ GpuLanes int `json:"gpu_lanes"`
+ PcieBw float64 `json:"pcie_bw"`
+ PciGen int `json:"pci_gen"`
+ Dlperf float64 `json:"dlperf"`
+ CPUName string `json:"cpu_name"`
+ MoboName string `json:"mobo_name"`
+ CPURAM int `json:"cpu_ram"`
+ CPUCores int `json:"cpu_cores"`
+ CPUCoresEffective int `json:"cpu_cores_effective"`
+ GpuFrac int `json:"gpu_frac"`
+ HasAvx int `json:"has_avx"`
+ DiskSpace float64 `json:"disk_space"`
+ DiskName string `json:"disk_name"`
+ DiskBw float64 `json:"disk_bw"`
+ InetUp float64 `json:"inet_up"`
+ InetDown float64 `json:"inet_down"`
+ StartDate float64 `json:"start_date"`
+ EndDate any `json:"end_date"`
+ Duration any `json:"duration"`
+ StorageCost float64 `json:"storage_cost"`
+ InetUpCost float64 `json:"inet_up_cost"`
+ InetDownCost float64 `json:"inet_down_cost"`
+ StorageTotalCost int `json:"storage_total_cost"`
+ Verification string `json:"verification"`
+ Score float64 `json:"score"`
+ Rented bool `json:"rented"`
+ BundledResults int `json:"bundled_results"`
+ PendingCount int `json:"pending_count"`
+}
diff --git a/web/vastai/templates.go b/web/vastai/templates.go
new file mode 100644
index 0000000..2f5d31e
--- /dev/null
+++ b/web/vastai/templates.go
@@ -0,0 +1,67 @@
+package vastai
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+)
+
+func (c *Client) GetTemplatesForUser(ctx context.Context, userID int) ([]Template, error) {
+ resp, err := doJSON[struct {
+ Success bool `json:"success"`
+ Templates []Template `json:"template"`
+ }](
+ ctx, c, http.MethodGet,
+ fmt.Sprintf("/v0/users/%d/created-templates/", userID),
+ http.StatusOK,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ return resp.Templates, nil
+}
+
+type Template struct {
+ ID int `json:"id"`
+ CreatorID int `json:"creator_id"`
+ ArgsStr string `json:"args_str"`
+ Command any `json:"command"`
+ DefaultTag string `json:"default_tag"`
+ Desc string `json:"desc"`
+ DockerLoginRepo any `json:"docker_login_repo"`
+ Env string `json:"env"`
+ ExtraFilters string `json:"extra_filters"`
+ Href string `json:"href"`
+ Image string `json:"image"`
+ JupyterDir any `json:"jupyter_dir"`
+ JupDirect bool `json:"jup_direct"`
+ JupyterTested bool `json:"jupyter_tested"`
+ JupyterlabTested bool `json:"jupyterlab_tested"`
+ MaxCuda any `json:"max_cuda"`
+ MinCuda any `json:"min_cuda"`
+ Onstart string `json:"onstart"`
+ PythonUtf8 bool `json:"python_utf8"`
+ LangUtf8 bool `json:"lang_utf8"`
+ Repo string `json:"repo"`
+ Runtype string `json:"runtype"`
+ SSHDirect bool `json:"ssh_direct"`
+ Tag string `json:"tag"`
+ UseJupyterLab bool `json:"use_jupyter_lab"`
+ UseSSH bool `json:"use_ssh"`
+ HashID string `json:"hash_id"`
+ Name string `json:"name"`
+ CreatedFrom any `json:"created_from"`
+ CreatedFromID int `json:"created_from_id"`
+ CountCreated int `json:"count_created"`
+ DescCount int `json:"desc_count"`
+ RecentCreateDate float64 `json:"recent_create_date"`
+ CreatedAt float64 `json:"created_at"`
+ Private bool `json:"private"`
+ ReadmeVisible bool `json:"readme_visible"`
+ ReadmeHash any `json:"readme_hash"`
+ RecommendedDiskSpace float64 `json:"recommended_disk_space"`
+ Recommended bool `json:"recommended"`
+ Cached bool `json:"cached"`
+ Autoscaler bool `json:"autoscaler"`
+}
diff --git a/web/vastai/users.go b/web/vastai/users.go
new file mode 100644
index 0000000..c043e60
--- /dev/null
+++ b/web/vastai/users.go
@@ -0,0 +1,52 @@
+package vastai
+
+import (
+ "context"
+ "net/http"
+)
+
+func (c *Client) Whoami(ctx context.Context) (*User, error) {
+ u, err := doJSON[User](ctx, c, http.MethodGet, "/v0/users/current/", http.StatusOK)
+ return &u, err
+}
+
+type User struct {
+ CanPay bool `json:"can_pay"`
+ ID int `json:"id"`
+ APIKey string `json:"api_key"`
+ Username string `json:"username"`
+ SSHKey any `json:"ssh_key"`
+ PhoneNumber any `json:"phone_number"`
+ PaypalEmail any `json:"paypal_email"`
+ WiseEmail any `json:"wise_email"`
+ Fullname any `json:"fullname"`
+ BalanceThreshold float64 `json:"balance_threshold"`
+ BalanceThresholdEnabled bool `json:"balance_threshold_enabled"`
+ AutobillThreshold any `json:"autobill_threshold"`
+ AutobillAmount any `json:"autobill_amount"`
+ BilladdressLine1 any `json:"billaddress_line1"`
+ BilladdressLine2 any `json:"billaddress_line2"`
+ BilladdressCity any `json:"billaddress_city"`
+ BilladdressZip any `json:"billaddress_zip"`
+ BilladdressCountry any `json:"billaddress_country"`
+ BillingCreditonly int `json:"billing_creditonly"`
+ BilladdressTaxinfo any `json:"billaddress_taxinfo"`
+ PasswordResettable any `json:"password_resettable"`
+ Email string `json:"email"`
+ HasBilling bool `json:"has_billing"`
+ HasPayout bool `json:"has_payout"`
+ HostOnly bool `json:"host_only"`
+ HostAgreementAccepted bool `json:"host_agreement_accepted"`
+ EmailVerified bool `json:"email_verified"`
+ Last4 any `json:"last4"`
+ Balance int `json:"balance"`
+ Credit float64 `json:"credit"`
+ GotSignupCredit int `json:"got_signup_credit"`
+ User string `json:"user"`
+ PaidVerified float64 `json:"paid_verified"`
+ PaidExpected float64 `json:"paid_expected"`
+ BilledVerified float64 `json:"billed_verified"`
+ BilledExpected float64 `json:"billed_expected"`
+ Rights any `json:"rights"`
+ Sid string `json:"sid"`
+}
diff --git a/web/vastai/vastai.go b/web/vastai/vastai.go
new file mode 100644
index 0000000..88463ea
--- /dev/null
+++ b/web/vastai/vastai.go
@@ -0,0 +1,119 @@
+package vastai
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "runtime"
+)
+
+type Client struct {
+ apiKey string
+ cli *http.Client
+ apiURL string
+}
+
+func New(apiKey string) *Client {
+ return &Client{
+ apiKey: apiKey,
+ cli: &http.Client{},
+ apiURL: "https://cloud.vast.ai/api",
+ }
+}
+
+func (c *Client) WithClient(cli *http.Client) *Client {
+ c.cli = cli
+ return c
+}
+
+func (c *Client) WithAPIURL(apiURL string) *Client {
+ c.apiURL = apiURL
+ return c
+}
+
+// Do performs a HTTP request with the appropriate authentication and user agent headers.
+func (c *Client) Do(req *http.Request) (*http.Response, error) {
+ req.Header.Set("Authorization", "Bearer "+c.apiKey)
+ req.Header.Set("User-Agent", fmt.Sprintf("go/%s pkg:within.website/x/web/vastai", runtime.Version()))
+
+ return c.cli.Do(req)
+}
+
+func (c *Client) doRequestNoResponse(ctx context.Context, method, path string) error {
+ req, err := http.NewRequestWithContext(ctx, method, c.apiURL+path, nil)
+ if err != nil {
+ return err
+ }
+
+ resp, err := c.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode/100 != 2 {
+ return NewError(resp)
+ }
+
+ return nil
+}
+
+// zilch returns the zero value of a given type.
+func zilch[T any]() T { return *new(T) }
+
+func doJSON[Output any](ctx context.Context, c *Client, method, path string, wantStatusCode int) (Output, error) {
+ req, err := http.NewRequestWithContext(ctx, method, c.apiURL+path, nil)
+ if err != nil {
+ return zilch[Output](), err
+ }
+
+ resp, err := c.Do(req)
+ if err != nil {
+ return zilch[Output](), err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != wantStatusCode {
+ return zilch[Output](), NewError(resp)
+ }
+
+ var output Output
+
+ if err := json.NewDecoder(resp.Body).Decode(&output); err != nil {
+ return zilch[Output](), err
+ }
+
+ return output, nil
+}
+
+func doJSONBody[Input any, Output any](ctx context.Context, c *Client, method, path string, input Input, wantStatusCode int) (Output, error) {
+ buf := new(bytes.Buffer)
+ if err := json.NewEncoder(buf).Encode(input); err != nil {
+ return zilch[Output](), err
+ }
+
+ req, err := http.NewRequestWithContext(ctx, method, c.apiURL+path, buf)
+ if err != nil {
+ return zilch[Output](), err
+ }
+
+ resp, err := c.Do(req)
+ if err != nil {
+ return zilch[Output](), err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != wantStatusCode {
+ return zilch[Output](), NewError(resp)
+ }
+
+ var output Output
+
+ if err := json.NewDecoder(resp.Body).Decode(&output); err != nil {
+ return zilch[Output](), err
+ }
+
+ return output, nil
+}