fetch_models_for_opencode.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package main
  2. import (
  3. "encoding/json"
  4. "flag"
  5. "fmt"
  6. "io"
  7. "log"
  8. "net/http"
  9. "os"
  10. "strings"
  11. )
  12. type ModelsResponse struct {
  13. Object string `json:"object"`
  14. Data []Model `json:"data"`
  15. }
  16. type Model struct {
  17. ID string `json:"id"`
  18. Object string `json:"object"`
  19. Created int64 `json:"created,omitempty"`
  20. OwnedBy string `json:"owned_by,omitempty"`
  21. Metadata map[string]interface{} `json:"metadata,omitempty"`
  22. }
  23. type Limit struct {
  24. Context int `json:"context"`
  25. Output int `json:"output"`
  26. }
  27. type OpencodeModel struct {
  28. Name string `json:"name"`
  29. Limit Limit `json:"limit"`
  30. }
  31. type DynamicProvider struct {
  32. Npm string `json:"npm"`
  33. Name string `json:"name"`
  34. Options map[string]string `json:"options"`
  35. Models map[string]OpencodeModel `json:"models"`
  36. }
  37. type OpencodeConfig struct {
  38. Schema string `json:"$schema"`
  39. Provider map[string]DynamicProvider `json:"provider"`
  40. }
  41. func getIntFromMetadata(md map[string]interface{}, key string, defaultVal int) int {
  42. if md == nil {
  43. return defaultVal
  44. }
  45. if v, ok := md[key]; ok {
  46. if i, ok := v.(float64); ok {
  47. return int(i)
  48. }
  49. }
  50. return defaultVal
  51. }
  52. func main() {
  53. var providerName, baseURL, apiURL, outputFile, token string
  54. flag.StringVar(&providerName, "provider", "Myprovider", "Name of the provider (default: Myprovider)")
  55. flag.StringVar(&baseURL, "baseurl", "", "Base URL for the provider (required, e.g., http://localhost:8080/v1)")
  56. flag.StringVar(&outputFile, "output", "opencode.jsonc", "Output file name (default: opencode.jsonc)")
  57. flag.StringVar(&token, "token", "", "API token sent as Authorization: Bearer (optional)")
  58. flag.Usage = func() {
  59. fmt.Fprintf(os.Stderr, "Usage: %s [options]\n\n", os.Args[0])
  60. fmt.Fprintf(os.Stderr, "Example:\n")
  61. fmt.Fprintf(os.Stderr, " %s --provider Myprovider --baseurl http://localhost:8080/v1\n\n", os.Args[0])
  62. flag.PrintDefaults()
  63. }
  64. flag.Parse()
  65. if baseURL == "" {
  66. log.Fatal("--baseurl is required")
  67. }
  68. apiURL = strings.TrimSuffix(baseURL, "/") + "/models"
  69. log.Println("apiurl:", apiURL)
  70. req, err := http.NewRequest("GET", apiURL, nil)
  71. if err != nil {
  72. log.Fatalf("Failed to create request: %v", err)
  73. }
  74. if token != "" {
  75. req.Header.Set("Authorization", "Bearer "+token)
  76. }
  77. resp, err := http.DefaultClient.Do(req)
  78. if err != nil {
  79. log.Fatalf("Failed to fetch %s: %v", apiURL, err)
  80. }
  81. defer resp.Body.Close()
  82. if resp.StatusCode != http.StatusOK {
  83. log.Fatalf("Unexpected status code: %d", resp.StatusCode)
  84. }
  85. body, err := io.ReadAll(resp.Body)
  86. if err != nil {
  87. log.Fatalf("Failed to read response: %v", err)
  88. }
  89. var modelsResp ModelsResponse
  90. if err := json.Unmarshal(body, &modelsResp); err != nil {
  91. log.Fatalf("Failed to parse models response: %v", err)
  92. }
  93. var apiKey = "noneneeded"
  94. if token != "" {
  95. apiKey = token
  96. }
  97. config := OpencodeConfig{
  98. Schema: "https://opencode.ai/config.json",
  99. Provider: map[string]DynamicProvider{
  100. providerName: {
  101. Npm: "@ai-sdk/openai-compatible",
  102. Name: providerName,
  103. Options: map[string]string{
  104. "baseURL": baseURL,
  105. "apiKey": apiKey,
  106. },
  107. Models: make(map[string]OpencodeModel),
  108. },
  109. },
  110. }
  111. for _, m := range modelsResp.Data {
  112. ctx := getIntFromMetadata(m.Metadata, "context_window", 131072)
  113. out := getIntFromMetadata(m.Metadata, "max_output_tokens", 8192)
  114. config.Provider[providerName].Models[m.ID] = OpencodeModel{
  115. Name: m.ID,
  116. Limit: Limit{
  117. Context: ctx,
  118. Output: out,
  119. },
  120. }
  121. }
  122. out, err := json.MarshalIndent(config, "", " ")
  123. if err != nil {
  124. log.Fatalf("Failed to marshal config: %v", err)
  125. }
  126. if err := os.WriteFile(outputFile, out, 0644); err != nil {
  127. log.Fatalf("Failed to write %s: %v", outputFile, err)
  128. }
  129. fmt.Printf("Successfully wrote %s with %d models for provider %s\n", outputFile, len(config.Provider[providerName].Models), providerName)
  130. }