From 87b14d53fe5d4850c55d93031f5a4b7f7e36f60b Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Fri, 27 Mar 2026 15:28:08 +0100 Subject: [PATCH 1/8] chore: per-provider endpoint --- main.go | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/main.go b/main.go index 5bdb8496..06d4b15d 100644 --- a/main.go +++ b/main.go @@ -4,11 +4,14 @@ package main import ( "encoding/json" + "fmt" "log" "net/http" + "strings" "time" "charm.land/catwalk/internal/providers" + "charm.land/catwalk/pkg/catwalk" "github.com/charmbracelet/x/etag" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -69,6 +72,79 @@ func providersHandler(w http.ResponseWriter, r *http.Request) { } } +func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Extract provider ID from the URL path + path := strings.TrimPrefix(r.URL.Path, "/v2/providers/") + providerID := strings.TrimSuffix(path, "/") + + if providerID == "" { + http.Error(w, "Provider ID is required", http.StatusBadRequest) + return + } + + // Check if pretty printing is requested + pretty := r.URL.Query().Get("pretty") == "true" + + // Get all providers + allProviders := providers.GetAll() + + // Find the specific provider by ID + var foundProvider *catwalk.Provider + for _, provider := range allProviders { + if string(provider.ID) == providerID { + foundProvider = &provider + break + } + } + + if foundProvider == nil { + http.Error(w, "Provider not found", http.StatusNotFound) + return + } + + if pretty { + // Return as markdown table + w.Header().Set("Content-Type", "text/markdown") + renderProviderMarkdown(w, *foundProvider) + } else { + // Return as JSON + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(foundProvider); err != nil { + log.Printf("Error encoding provider response: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } +} + +func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider) { + // Header + fmt.Fprintf(w, "# Provider: %s\n\n", provider.Name) + + // Basic info table + fmt.Fprintf(w, "| Field | Value |\n") + fmt.Fprintf(w, "|-------|-------|\n") + fmt.Fprintf(w, "| ID | `%s` |\n", provider.ID) + fmt.Fprintf(w, "| Type | `%s` |\n", provider.Type) + fmt.Fprintf(w, "| API Endpoint | `%s` |\n", provider.APIEndpoint) + fmt.Fprintf(w, "| Default Large Model ID | `%s` |\n", provider.DefaultLargeModelID) + fmt.Fprintf(w, "| Default Small Model ID | `%s` |\n", provider.DefaultSmallModelID) + + if len(provider.Models) > 0 { + fmt.Fprintf(w, "\n## Models\n\n") + fmt.Fprintf(w, "| Model ID | Name | Context Window | Default Max Tokens |\n") + fmt.Fprintf(w, "|----------|------|----------------|------------------|\n") + for _, model := range provider.Models { + fmt.Fprintf(w, "| `%s` | %s | %d | %d |\n", model.ID, model.Name, model.ContextWindow, model.DefaultMaxTokens) + } + } +} + func providersHandlerDeprecated(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -81,6 +157,7 @@ func providersHandlerDeprecated(w http.ResponseWriter, _ *http.Request) { func main() { mux := http.NewServeMux() mux.HandleFunc("/v2/providers", providersHandler) + mux.HandleFunc("/v2/providers/", providersSpecificHandler) mux.HandleFunc("/providers", providersHandlerDeprecated) mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) From 913d00e1a40c83c3675dba8b7213b2aa4d87a729 Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Fri, 27 Mar 2026 15:39:38 +0100 Subject: [PATCH 2/8] chore: show token costs, context --- main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 06d4b15d..dd2fe0b0 100644 --- a/main.go +++ b/main.go @@ -137,10 +137,10 @@ func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider) { if len(provider.Models) > 0 { fmt.Fprintf(w, "\n## Models\n\n") - fmt.Fprintf(w, "| Model ID | Name | Context Window | Default Max Tokens |\n") - fmt.Fprintf(w, "|----------|------|----------------|------------------|\n") + fmt.Fprintf(w, "| Model ID | Name | Context Window | Default Max Tokens | Input Cost ($/M) | Output Cost ($/M) |\n") + fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|------------------|\n") for _, model := range provider.Models { - fmt.Fprintf(w, "| `%s` | %s | %d | %d |\n", model.ID, model.Name, model.ContextWindow, model.DefaultMaxTokens) + fmt.Fprintf(w, "| `%s` | %s | %d | %d | %.6f | %.6f |\n", model.ID, model.Name, model.ContextWindow, model.DefaultMaxTokens, model.CostPer1MIn, model.CostPer1MOut) } } } From 2cfc099356f7c81af0306c21900e6ca758b627b3 Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Fri, 27 Mar 2026 15:43:10 +0100 Subject: [PATCH 3/8] chore: sorting --- main.go | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index dd2fe0b0..751dc61f 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "fmt" "log" "net/http" + "sort" "strings" "time" @@ -110,7 +111,7 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { if pretty { // Return as markdown table w.Header().Set("Content-Type", "text/markdown") - renderProviderMarkdown(w, *foundProvider) + renderProviderMarkdown(w, *foundProvider, r) } else { // Return as JSON w.Header().Set("Content-Type", "application/json") @@ -122,7 +123,7 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { } } -func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider) { +func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r *http.Request) { // Header fmt.Fprintf(w, "# Provider: %s\n\n", provider.Name) @@ -137,10 +138,33 @@ func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider) { if len(provider.Models) > 0 { fmt.Fprintf(w, "\n## Models\n\n") - fmt.Fprintf(w, "| Model ID | Name | Context Window | Default Max Tokens | Input Cost ($/M) | Output Cost ($/M) |\n") - fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|------------------|\n") - for _, model := range provider.Models { - fmt.Fprintf(w, "| `%s` | %s | %d | %d | %.6f | %.6f |\n", model.ID, model.Name, model.ContextWindow, model.DefaultMaxTokens, model.CostPer1MIn, model.CostPer1MOut) + + // Handle sorting + models := provider.Models + sortParam := r.URL.Query().Get("sort") + + switch sortParam { + case "output": + // Sort by output cost (ascending) + sort.Slice(models, func(i, j int) bool { + return models[i].CostPer1MOut < models[j].CostPer1MOut + }) + case "context": + // Sort by context window (descending) + sort.Slice(models, func(i, j int) bool { + return models[i].ContextWindow > models[j].ContextWindow + }) + default: + // Default sort by input cost (ascending) + sort.Slice(models, func(i, j int) bool { + return models[i].CostPer1MIn < models[j].CostPer1MIn + }) + } + + fmt.Fprintf(w, "| Model ID | Name | Context Window | Input Cost ($/M) | Output Cost ($/M) |\n") + fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|\n") + for _, model := range models { + fmt.Fprintf(w, "| `%s` | %s | %d | %.6f | %.6f |\n", model.ID, model.Name, model.ContextWindow, model.CostPer1MIn, model.CostPer1MOut) } } } From 77f388eeda5e43f9e9327f45a4d80bae1bf5960e Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Fri, 27 Mar 2026 16:00:18 +0100 Subject: [PATCH 4/8] fix: gofmt --- main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 751dc61f..1f6b423d 100644 --- a/main.go +++ b/main.go @@ -93,7 +93,7 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { // Get all providers allProviders := providers.GetAll() - + // Find the specific provider by ID var foundProvider *catwalk.Provider for _, provider := range allProviders { @@ -126,7 +126,7 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r *http.Request) { // Header fmt.Fprintf(w, "# Provider: %s\n\n", provider.Name) - + // Basic info table fmt.Fprintf(w, "| Field | Value |\n") fmt.Fprintf(w, "|-------|-------|\n") @@ -135,14 +135,14 @@ func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r fmt.Fprintf(w, "| API Endpoint | `%s` |\n", provider.APIEndpoint) fmt.Fprintf(w, "| Default Large Model ID | `%s` |\n", provider.DefaultLargeModelID) fmt.Fprintf(w, "| Default Small Model ID | `%s` |\n", provider.DefaultSmallModelID) - + if len(provider.Models) > 0 { fmt.Fprintf(w, "\n## Models\n\n") - + // Handle sorting models := provider.Models sortParam := r.URL.Query().Get("sort") - + switch sortParam { case "output": // Sort by output cost (ascending) @@ -160,7 +160,7 @@ func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r return models[i].CostPer1MIn < models[j].CostPer1MIn }) } - + fmt.Fprintf(w, "| Model ID | Name | Context Window | Input Cost ($/M) | Output Cost ($/M) |\n") fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|\n") for _, model := range models { From ce967566d1ee8957626e20ce72f1e21ac17f1aee Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Fri, 27 Mar 2026 16:07:21 +0100 Subject: [PATCH 5/8] chore: show reasoning yes/no --- main.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 1f6b423d..e9378ff1 100644 --- a/main.go +++ b/main.go @@ -123,6 +123,13 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { } } +func yesNo(b bool) string { + if b { + return "yes" + } + return "no" +} + func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r *http.Request) { // Header fmt.Fprintf(w, "# Provider: %s\n\n", provider.Name) @@ -137,7 +144,7 @@ func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r fmt.Fprintf(w, "| Default Small Model ID | `%s` |\n", provider.DefaultSmallModelID) if len(provider.Models) > 0 { - fmt.Fprintf(w, "\n## Models\n\n") + fmt.Fprintf(w, "\n## Available Models\n\n") // Handle sorting models := provider.Models @@ -161,10 +168,10 @@ func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r }) } - fmt.Fprintf(w, "| Model ID | Name | Context Window | Input Cost ($/M) | Output Cost ($/M) |\n") - fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|\n") + fmt.Fprintf(w, "| Name | ID | Context Window | Input Cost ($/M) | Output Cost ($/M) | Reasoning |\n") + fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|----------|\n") for _, model := range models { - fmt.Fprintf(w, "| `%s` | %s | %d | %.6f | %.6f |\n", model.ID, model.Name, model.ContextWindow, model.CostPer1MIn, model.CostPer1MOut) + fmt.Fprintf(w, "| %s | `%s` | %d | %.6f | %.6f | %s |\n", model.Name, model.ID, model.ContextWindow, model.CostPer1MIn, model.CostPer1MOut, yesNo(model.CanReason)) } } } From 715d671e25b4aef419a445e9838bf786469f0ab9 Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Fri, 27 Mar 2026 21:54:55 +0100 Subject: [PATCH 6/8] chore: just provider --- main.go | 75 ++++----------------------------------------------------- 1 file changed, 5 insertions(+), 70 deletions(-) diff --git a/main.go b/main.go index e9378ff1..43fa9921 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,8 @@ package main import ( "encoding/json" - "fmt" "log" "net/http" - "sort" "strings" "time" @@ -88,9 +86,6 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { return } - // Check if pretty printing is requested - pretty := r.URL.Query().Get("pretty") == "true" - // Get all providers allProviders := providers.GetAll() @@ -108,71 +103,11 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { return } - if pretty { - // Return as markdown table - w.Header().Set("Content-Type", "text/markdown") - renderProviderMarkdown(w, *foundProvider, r) - } else { - // Return as JSON - w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(foundProvider); err != nil { - log.Printf("Error encoding provider response: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - } -} - -func yesNo(b bool) string { - if b { - return "yes" - } - return "no" -} - -func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r *http.Request) { - // Header - fmt.Fprintf(w, "# Provider: %s\n\n", provider.Name) - - // Basic info table - fmt.Fprintf(w, "| Field | Value |\n") - fmt.Fprintf(w, "|-------|-------|\n") - fmt.Fprintf(w, "| ID | `%s` |\n", provider.ID) - fmt.Fprintf(w, "| Type | `%s` |\n", provider.Type) - fmt.Fprintf(w, "| API Endpoint | `%s` |\n", provider.APIEndpoint) - fmt.Fprintf(w, "| Default Large Model ID | `%s` |\n", provider.DefaultLargeModelID) - fmt.Fprintf(w, "| Default Small Model ID | `%s` |\n", provider.DefaultSmallModelID) - - if len(provider.Models) > 0 { - fmt.Fprintf(w, "\n## Available Models\n\n") - - // Handle sorting - models := provider.Models - sortParam := r.URL.Query().Get("sort") - - switch sortParam { - case "output": - // Sort by output cost (ascending) - sort.Slice(models, func(i, j int) bool { - return models[i].CostPer1MOut < models[j].CostPer1MOut - }) - case "context": - // Sort by context window (descending) - sort.Slice(models, func(i, j int) bool { - return models[i].ContextWindow > models[j].ContextWindow - }) - default: - // Default sort by input cost (ascending) - sort.Slice(models, func(i, j int) bool { - return models[i].CostPer1MIn < models[j].CostPer1MIn - }) - } - - fmt.Fprintf(w, "| Name | ID | Context Window | Input Cost ($/M) | Output Cost ($/M) | Reasoning |\n") - fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|----------|\n") - for _, model := range models { - fmt.Fprintf(w, "| %s | `%s` | %d | %.6f | %.6f | %s |\n", model.Name, model.ID, model.ContextWindow, model.CostPer1MIn, model.CostPer1MOut, yesNo(model.CanReason)) - } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(foundProvider); err != nil { + log.Printf("Error encoding provider response: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return } } From 7b3ac0b7efac2aa66d9dcc4165f34687b58c7125 Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Fri, 27 Mar 2026 22:06:12 +0100 Subject: [PATCH 7/8] chore: output markdown on /provider.md --- main.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 43fa9921..529f2723 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,10 @@ package main import ( "encoding/json" + "fmt" "log" "net/http" + "sort" "strings" "time" @@ -77,19 +79,19 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { return } - // Extract provider ID from the URL path path := strings.TrimPrefix(r.URL.Path, "/v2/providers/") - providerID := strings.TrimSuffix(path, "/") + providerID := strings.TrimSuffix(path, ".md") + providerID = strings.TrimSuffix(providerID, "/") if providerID == "" { http.Error(w, "Provider ID is required", http.StatusBadRequest) return } - // Get all providers + isMarkdown := strings.HasSuffix(r.URL.Path, ".md") + allProviders := providers.GetAll() - // Find the specific provider by ID var foundProvider *catwalk.Provider for _, provider := range allProviders { if string(provider.ID) == providerID { @@ -103,11 +105,68 @@ func providersSpecificHandler(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(foundProvider); err != nil { - log.Printf("Error encoding provider response: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return + if isMarkdown { + // Return as markdown table + w.Header().Set("Content-Type", "text/markdown") + renderProviderMarkdown(w, *foundProvider, r) + } else { + // Return as JSON + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(foundProvider); err != nil { + log.Printf("Error encoding provider response: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } +} + +func yesNo(b bool) string { + if b { + return "yes" + } + return "no" +} + +func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r *http.Request) { + fmt.Fprintf(w, "# Provider: %s\n\n", provider.Name) + + fmt.Fprintf(w, "| Field | Value |\n") + fmt.Fprintf(w, "|-------|-------|\n") + fmt.Fprintf(w, "| ID | `%s` |\n", provider.ID) + fmt.Fprintf(w, "| Type | `%s` |\n", provider.Type) + fmt.Fprintf(w, "| API Endpoint | `%s` |\n", provider.APIEndpoint) + fmt.Fprintf(w, "| Default Large Model ID | `%s` |\n", provider.DefaultLargeModelID) + fmt.Fprintf(w, "| Default Small Model ID | `%s` |\n", provider.DefaultSmallModelID) + + if len(provider.Models) > 0 { + fmt.Fprintf(w, "\n## Available Models\n\n") + + models := provider.Models + sortParam := r.URL.Query().Get("sort") + + switch sortParam { + case "output": + // Sort by output cost (ascending) + sort.Slice(models, func(i, j int) bool { + return models[i].CostPer1MOut < models[j].CostPer1MOut + }) + case "context": + // Sort by context window (descending) + sort.Slice(models, func(i, j int) bool { + return models[i].ContextWindow > models[j].ContextWindow + }) + default: + // Default sort by input cost (ascending) + sort.Slice(models, func(i, j int) bool { + return models[i].CostPer1MIn < models[j].CostPer1MIn + }) + } + + fmt.Fprintf(w, "| Name | ID | Context Window | Input Cost ($/M) | Output Cost ($/M) | Reasoning |\n") + fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|----------|\n") + for _, model := range models { + fmt.Fprintf(w, "| %s | `%s` | %d | %.6f | %.6f | %s |\n", model.Name, model.ID, model.ContextWindow, model.CostPer1MIn, model.CostPer1MOut, yesNo(model.CanReason)) + } } } From 1dcd1ffac0230b609090911fa255f0b93a0a728f Mon Sep 17 00:00:00 2001 From: Harm Weites Date: Mon, 30 Mar 2026 11:08:53 +0200 Subject: [PATCH 8/8] fix: use strings.Builder --- main.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/main.go b/main.go index 529f2723..66a0c0cb 100644 --- a/main.go +++ b/main.go @@ -128,18 +128,19 @@ func yesNo(b bool) string { } func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r *http.Request) { - fmt.Fprintf(w, "# Provider: %s\n\n", provider.Name) + var buf strings.Builder - fmt.Fprintf(w, "| Field | Value |\n") - fmt.Fprintf(w, "|-------|-------|\n") - fmt.Fprintf(w, "| ID | `%s` |\n", provider.ID) - fmt.Fprintf(w, "| Type | `%s` |\n", provider.Type) - fmt.Fprintf(w, "| API Endpoint | `%s` |\n", provider.APIEndpoint) - fmt.Fprintf(w, "| Default Large Model ID | `%s` |\n", provider.DefaultLargeModelID) - fmt.Fprintf(w, "| Default Small Model ID | `%s` |\n", provider.DefaultSmallModelID) + fmt.Fprintf(&buf, "# Provider: %s\n\n", provider.Name) + fmt.Fprintf(&buf, "| Field | Value |\n") + fmt.Fprintf(&buf, "|-------|-------|\n") + fmt.Fprintf(&buf, "| ID | `%s` |\n", provider.ID) + fmt.Fprintf(&buf, "| Type | `%s` |\n", provider.Type) + fmt.Fprintf(&buf, "| API Endpoint | `%s` |\n", provider.APIEndpoint) + fmt.Fprintf(&buf, "| Default Large Model ID | `%s` |\n", provider.DefaultLargeModelID) + fmt.Fprintf(&buf, "| Default Small Model ID | `%s` |\n", provider.DefaultSmallModelID) if len(provider.Models) > 0 { - fmt.Fprintf(w, "\n## Available Models\n\n") + fmt.Fprintf(&buf, "\n## Available Models\n\n") models := provider.Models sortParam := r.URL.Query().Get("sort") @@ -162,12 +163,17 @@ func renderProviderMarkdown(w http.ResponseWriter, provider catwalk.Provider, r }) } - fmt.Fprintf(w, "| Name | ID | Context Window | Input Cost ($/M) | Output Cost ($/M) | Reasoning |\n") - fmt.Fprintf(w, "|----------|------|----------------|------------------|------------------|----------|\n") + fmt.Fprintf(&buf, "| Name | ID | Context Window | Input Cost ($/M) | Output Cost ($/M) | Reasoning |\n") + fmt.Fprintf(&buf, "|----------|------|----------------|------------------|------------------|----------|\n") for _, model := range models { - fmt.Fprintf(w, "| %s | `%s` | %d | %.6f | %.6f | %s |\n", model.Name, model.ID, model.ContextWindow, model.CostPer1MIn, model.CostPer1MOut, yesNo(model.CanReason)) + fmt.Fprintf(&buf, "| %s | `%s` | %d | %.6f | %.6f | %s |\n", model.Name, model.ID, model.ContextWindow, model.CostPer1MIn, model.CostPer1MOut, yesNo(model.CanReason)) } } + + if _, err := w.Write([]byte(buf.String())); err != nil { + log.Printf("Error writing markdown response: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + } } func providersHandlerDeprecated(w http.ResponseWriter, _ *http.Request) {