diff --git a/frontend/src/assets/github.svg b/frontend/src/assets/github.svg
new file mode 100644
index 0000000..f0ea898
--- /dev/null
+++ b/frontend/src/assets/github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/views/dashboard/KeyNew.vue b/frontend/src/views/dashboard/KeyNew.vue
index f94e892..af84eff 100644
--- a/frontend/src/views/dashboard/KeyNew.vue
+++ b/frontend/src/views/dashboard/KeyNew.vue
@@ -67,6 +67,12 @@
+
+
+
+
+
+
diff --git a/internal/controller/apikey.go b/internal/controller/apikey.go
index 82961ee..af30019 100644
--- a/internal/controller/apikey.go
+++ b/internal/controller/apikey.go
@@ -1,10 +1,13 @@
package controller
import (
+ "bytes"
+ "encoding/json"
"net/http"
"opencatd-open/internal/consts"
"opencatd-open/internal/dto"
"opencatd-open/internal/model"
+ "opencatd-open/internal/utils"
"strconv"
"strings"
@@ -18,13 +21,22 @@ func (a Api) CreateApiKey(c *gin.Context) {
dto.Fail(c, 403, "Permission denied")
return
}
- req := new(model.ApiKey)
- err := c.ShouldBind(&req)
+ newkey := new(model.ApiKey)
+ err := c.ShouldBind(newkey)
if err != nil {
dto.Fail(c, 400, err.Error())
}
+ if slice.Contain([]string{"openai", "azure", "claude"}, *newkey.ApiType) {
+ sma, err := utils.FetchKeyModel(a.db, newkey)
+ if err == nil {
+ newkey.SupportModelsArray = sma
+ var buf = new(bytes.Buffer)
+ json.NewEncoder(buf).Encode(sma) //nolint:errcheck
+ newkey.SupportModels = utils.ToPtr(buf.String())
+ }
+ }
- err = a.keyService.CreateApiKey(c, req)
+ err = a.keyService.CreateApiKey(c, newkey)
if err != nil {
dto.Fail(c, 400, err.Error())
} else {
@@ -79,6 +91,10 @@ func (a Api) ListApiKey(c *gin.Context) {
}
str = str[:slen]
key.ApiKey = &str
+
+ var sma []string
+ json.NewDecoder(strings.NewReader(*key.SupportModels)).Decode(&sma) //nolint:errcheck
+ key.SupportModelsArray = sma
}
dto.Success(c, gin.H{
"total": total,
diff --git a/internal/controller/proxy/proxy.go b/internal/controller/proxy/proxy.go
index fb0c36d..063c750 100644
--- a/internal/controller/proxy/proxy.go
+++ b/internal/controller/proxy/proxy.go
@@ -186,7 +186,7 @@ func (p *Proxy) SelectApiKey(model string) error {
akpikeys, err := p.apiKeyDao.FindApiKeysBySupportModel(p.db, model)
if err != nil || len(akpikeys) == 0 {
- if strings.HasPrefix(model, "gpt") || strings.HasPrefix(model, "o1") || strings.HasPrefix(model, "o3") {
+ if strings.HasPrefix(model, "gpt") || strings.HasPrefix(model, "o1") || strings.HasPrefix(model, "o3") || strings.HasPrefix(model, "o4") {
keys, err := p.apiKeyDao.FindKeys(map[string]any{"apitype = ?": "openai"})
if err != nil {
return err
@@ -227,7 +227,7 @@ func (p *Proxy) SelectApiKey(model string) error {
func (p *Proxy) updateSupportModel() {
- keys, err := p.apiKeyDao.FindKeys(map[string]interface{}{"apitype in ?": "openai,azure,claude"})
+ keys, err := p.apiKeyDao.FindKeys(map[string]interface{}{"apitype in ?": []string{"openai", "azure", "claude"}})
if err != nil {
return
}
diff --git a/internal/utils/fetchKeyModel.go b/internal/utils/fetchKeyModel.go
new file mode 100644
index 0000000..d4ed65f
--- /dev/null
+++ b/internal/utils/fetchKeyModel.go
@@ -0,0 +1,100 @@
+package utils
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "opencatd-open/internal/model"
+ "os"
+ "strings"
+
+ "github.com/tidwall/gjson"
+ "gorm.io/gorm"
+)
+
+var client = &http.Client{}
+
+func init() {
+ if os.Getenv("LOCAL_PROXY") != "" {
+ if proxyUrl, err := url.Parse(os.Getenv("LOCAL_PROXY")); err == nil {
+ client.Transport = &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
+ }
+ }
+}
+
+func FetchKeyModel(db *gorm.DB, key *model.ApiKey) ([]string, error) {
+
+ var supportModels []string
+ var err error
+ if *key.ApiType == "openai" || *key.ApiType == "azure" {
+ supportModels, err = FetchOpenAISupportModels(db, key)
+ if err != nil {
+ fmt.Println(err)
+ }
+ }
+ if *key.ApiType == "claude" {
+ supportModels, err = FetchClaudeSupportModels(db, key)
+ }
+ return supportModels, err
+}
+
+func FetchOpenAISupportModels(db *gorm.DB, apikey *model.ApiKey) ([]string, error) {
+ openaiModelsUrl := "https://api.openai.com/v1/models"
+ // https://learn.microsoft.com/zh-cn/rest/api/azureopenai/models/list?view=rest-azureopenai-2025-02-01-preview&tabs=HTTP
+ azureModelsUrl := "/openai/deployments?api-version=2022-12-01"
+
+ var supportModels []string
+ var req *http.Request
+ if *apikey.ApiType == "azure" {
+ req, _ = http.NewRequest("GET", *apikey.Endpoint+azureModelsUrl, nil)
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("api-key", *apikey.ApiKey)
+ } else {
+ req, _ = http.NewRequest("GET", openaiModelsUrl, nil)
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("Authorization", "Bearer "+*apikey.ApiKey)
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode == http.StatusOK {
+ bytesbody, _ := io.ReadAll(resp.Body)
+ result := gjson.GetBytes(bytesbody, "data.#.id").Array()
+ for _, v := range result {
+ model := v.Str
+ model = strings.Replace(model, "-35-", "-3.5-", -1)
+ model = strings.Replace(model, "-41-", "-4.1-", -1)
+ supportModels = append(supportModels, model)
+ }
+ }
+ return supportModels, nil
+}
+
+func FetchClaudeSupportModels(db *gorm.DB, apikey *model.ApiKey) ([]string, error) {
+ // https://docs.anthropic.com/en/api/models-list
+ claudemodelsUrl := "https://api.anthropic.com/v1/models"
+ var supportModels []string
+
+ req, _ := http.NewRequest("GET", claudemodelsUrl, nil)
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("x-api-key", *apikey.ApiKey)
+ req.Header.Set("anthropic-version", "2023-06-01")
+
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode == http.StatusOK {
+ bytesbody, _ := io.ReadAll(resp.Body)
+ result := gjson.GetBytes(bytesbody, "data.#.id").Array()
+ for _, v := range result {
+ supportModels = append(supportModels, v.Str)
+ }
+ }
+ return supportModels, nil
+}