|
|
@@ -0,0 +1,148 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding/json"
|
|
|
+ "flag"
|
|
|
+ "fmt"
|
|
|
+ "io"
|
|
|
+ "log"
|
|
|
+ "net/http"
|
|
|
+ "os"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+type ModelsResponse struct {
|
|
|
+ Object string `json:"object"`
|
|
|
+ Data []Model `json:"data"`
|
|
|
+}
|
|
|
+
|
|
|
+type Model struct {
|
|
|
+ ID string `json:"id"`
|
|
|
+ Object string `json:"object"`
|
|
|
+ Created int64 `json:"created,omitempty"`
|
|
|
+ OwnedBy string `json:"owned_by,omitempty"`
|
|
|
+ Metadata map[string]interface{} `json:"metadata,omitempty"`
|
|
|
+}
|
|
|
+
|
|
|
+type Limit struct {
|
|
|
+ Context int `json:"context"`
|
|
|
+ Output int `json:"output"`
|
|
|
+}
|
|
|
+
|
|
|
+type OpencodeModel struct {
|
|
|
+ Name string `json:"name"`
|
|
|
+ Limit Limit `json:"limit"`
|
|
|
+}
|
|
|
+
|
|
|
+type DynamicProvider struct {
|
|
|
+ Npm string `json:"npm"`
|
|
|
+ Name string `json:"name"`
|
|
|
+ Options map[string]string `json:"options"`
|
|
|
+ Models map[string]OpencodeModel `json:"models"`
|
|
|
+}
|
|
|
+
|
|
|
+type OpencodeConfig struct {
|
|
|
+ Schema string `json:"$schema"`
|
|
|
+ Provider map[string]DynamicProvider `json:"provider"`
|
|
|
+}
|
|
|
+
|
|
|
+func getIntFromMetadata(md map[string]interface{}, key string, defaultVal int) int {
|
|
|
+ if md == nil {
|
|
|
+ return defaultVal
|
|
|
+ }
|
|
|
+ if v, ok := md[key]; ok {
|
|
|
+ if i, ok := v.(float64); ok {
|
|
|
+ return int(i)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return defaultVal
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ var providerName, baseURL, apiURL, outputFile string
|
|
|
+
|
|
|
+ flag.StringVar(&providerName, "provider", "Myprovider", "Name of the provider (default: Myprovider)")
|
|
|
+ flag.StringVar(&baseURL, "baseurl", "", "Base URL for the provider (required, e.g., http://localhost:8080/v1)")
|
|
|
+ flag.StringVar(&apiURL, "apiurl", "", "API URL to fetch models from (default: <baseurl>/models)")
|
|
|
+ flag.StringVar(&outputFile, "output", "opencode.jsonc", "Output file name (default: opencode.jsonc)")
|
|
|
+
|
|
|
+ flag.Usage = func() {
|
|
|
+ fmt.Fprintf(os.Stderr, "Usage: %s [options]\n\n", os.Args[0])
|
|
|
+ fmt.Fprintf(os.Stderr, "Example:\n")
|
|
|
+ fmt.Fprintf(os.Stderr, " %s --provider Myprovider --baseurl http://localhost:8080/v1\n\n", os.Args[0])
|
|
|
+ flag.PrintDefaults()
|
|
|
+ }
|
|
|
+
|
|
|
+ flag.Parse()
|
|
|
+
|
|
|
+ if baseURL == "" {
|
|
|
+ log.Fatal("--baseurl is required")
|
|
|
+ }
|
|
|
+
|
|
|
+ if apiURL == "" {
|
|
|
+ apiURL = strings.TrimSuffix(baseURL, "/") + "/models"
|
|
|
+ }
|
|
|
+
|
|
|
+ if baseURL == "" || apiURL == "" {
|
|
|
+ log.Fatal("Both --baseurl and --apiurl are required")
|
|
|
+ }
|
|
|
+
|
|
|
+ resp, err := http.Get(apiURL)
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("Failed to fetch %s: %v", apiURL, err)
|
|
|
+ }
|
|
|
+ defer resp.Body.Close()
|
|
|
+
|
|
|
+ if resp.StatusCode != http.StatusOK {
|
|
|
+ log.Fatalf("Unexpected status code: %d", resp.StatusCode)
|
|
|
+ }
|
|
|
+
|
|
|
+ body, err := io.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("Failed to read response: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ var modelsResp ModelsResponse
|
|
|
+ if err := json.Unmarshal(body, &modelsResp); err != nil {
|
|
|
+ log.Fatalf("Failed to parse models response: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ config := OpencodeConfig{
|
|
|
+ Schema: "https://opencode.ai/config.json",
|
|
|
+ Provider: map[string]DynamicProvider{
|
|
|
+ providerName: {
|
|
|
+ Npm: "@ai-sdk/openai-compatible",
|
|
|
+ Name: providerName,
|
|
|
+ Options: map[string]string{
|
|
|
+ "baseURL": baseURL,
|
|
|
+ "apiKey": "noneneeded",
|
|
|
+ },
|
|
|
+ Models: make(map[string]OpencodeModel),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, m := range modelsResp.Data {
|
|
|
+ ctx := getIntFromMetadata(m.Metadata, "context_window", 131072)
|
|
|
+ out := getIntFromMetadata(m.Metadata, "max_output_tokens", 8192)
|
|
|
+
|
|
|
+ config.Provider[providerName].Models[m.ID] = OpencodeModel{
|
|
|
+ Name: m.ID,
|
|
|
+ Limit: Limit{
|
|
|
+ Context: ctx,
|
|
|
+ Output: out,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ out, err := json.MarshalIndent(config, "", " ")
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("Failed to marshal config: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := os.WriteFile(outputFile, out, 0644); err != nil {
|
|
|
+ log.Fatalf("Failed to write %s: %v", outputFile, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt.Printf("Successfully wrote %s with %d models for provider %s\n", outputFile, len(config.Provider[providerName].Models), providerName)
|
|
|
+}
|