mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-23 16:00:52 +00:00
182 lines
4.8 KiB
Go
182 lines
4.8 KiB
Go
package provider
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
)
|
|
|
|
type VerifyRequest struct {
|
|
Method string
|
|
URL string
|
|
Headers map[string]string
|
|
Body []byte
|
|
}
|
|
|
|
const (
|
|
defaultVerifyTimeout = 30 * time.Second
|
|
)
|
|
|
|
func SkipVerification(key string) bool {
|
|
switch key {
|
|
case "custom", "vllm", "ollama", "kimi-coding":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func VerifyAccount(provider, baseURL, apiKey string) error {
|
|
req := BuildVerifyRequest(provider, baseURL, apiKey)
|
|
var body *bytes.Buffer
|
|
if len(req.Body) > 0 {
|
|
body = bytes.NewBuffer(req.Body)
|
|
} else {
|
|
body = bytes.NewBuffer(nil)
|
|
}
|
|
httpReq, err := http.NewRequest(req.Method, req.URL, body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for key, value := range req.Headers {
|
|
httpReq.Header.Set(key, value)
|
|
}
|
|
resp, err := (&http.Client{Timeout: verifyTimeout()}).Do(httpReq)
|
|
if err != nil {
|
|
return buserr.WithErr("ErrAgentAccountUnavailable", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode >= 400 {
|
|
return buserr.WithErr("ErrAgentAccountUnavailable", fmt.Errorf("verify failed: %s", resp.Status))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func verifyTimeout() time.Duration {
|
|
return defaultVerifyTimeout
|
|
}
|
|
|
|
func BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {
|
|
base := strings.TrimRight(strings.TrimSpace(baseURL), "/")
|
|
headers := map[string]string{}
|
|
request := VerifyRequest{Method: http.MethodGet, Headers: headers}
|
|
|
|
switch provider {
|
|
case "anthropic", "kimi-coding":
|
|
headers["x-api-key"] = apiKey
|
|
headers["anthropic-version"] = "2023-06-01"
|
|
if strings.Contains(base, "/v1") {
|
|
request.URL = base + "/models"
|
|
} else {
|
|
request.URL = base + "/v1/models"
|
|
}
|
|
case "gemini":
|
|
request.Method = http.MethodPost
|
|
if strings.Contains(base, "/v1beta") {
|
|
request.URL = base + "/models/gemini-3-flash-preview:generateContent"
|
|
} else {
|
|
request.URL = base + "/v1beta/models/gemini-3-flash-preview:generateContent"
|
|
}
|
|
headers["x-goog-api-key"] = apiKey
|
|
headers["Content-Type"] = "application/json"
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"contents": []map[string]interface{}{{
|
|
"parts": []map[string]string{{
|
|
"text": "Explain how AI works in a few words",
|
|
}},
|
|
}},
|
|
})
|
|
case "zai":
|
|
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
request.URL = base + "/models"
|
|
case "bailian-coding-plan":
|
|
request.Method = http.MethodPost
|
|
if !strings.Contains(base, "/v1") {
|
|
base = base + "/v1"
|
|
}
|
|
request.URL = base + "/chat/completions"
|
|
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
headers["Content-Type"] = "application/json"
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"model": "qwen3.5-plus",
|
|
"messages": []map[string]string{{"role": "user", "content": "test"}},
|
|
"max_tokens": 1,
|
|
})
|
|
case "ark-coding-plan":
|
|
request.Method = http.MethodPost
|
|
if !strings.Contains(base, "/api/coding/v3") {
|
|
base = "https://ark.cn-beijing.volces.com/api/coding/v3"
|
|
}
|
|
request.URL = base + "/chat/completions"
|
|
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
headers["Content-Type"] = "application/json"
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"model": "ark-code-latest",
|
|
"messages": []map[string]string{{"role": "user", "content": "test"}},
|
|
"max_tokens": 1,
|
|
})
|
|
case "minimax":
|
|
request.Method = http.MethodPost
|
|
headers["x-api-key"] = apiKey
|
|
headers["anthropic-version"] = "2023-06-01"
|
|
headers["Content-Type"] = "application/json"
|
|
if strings.Contains(base, "/v1") {
|
|
request.URL = base + "/messages"
|
|
} else {
|
|
request.URL = base + "/v1/messages"
|
|
}
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"model": "MiniMax-M2.5",
|
|
"max_tokens": 1,
|
|
"messages": []map[string]interface{}{{
|
|
"role": "user",
|
|
"content": []map[string]string{{
|
|
"type": "text",
|
|
"text": "test",
|
|
}},
|
|
}},
|
|
})
|
|
case "xiaomi":
|
|
request.Method = http.MethodPost
|
|
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
headers["Content-Type"] = "application/json"
|
|
if !strings.Contains(base, "/v1") {
|
|
base = base + "/v1"
|
|
}
|
|
request.URL = base + "/chat/completions"
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"model": "mimo-v2-flash",
|
|
"max_tokens": 1,
|
|
"messages": []map[string]string{{"role": "user", "content": "test"}},
|
|
})
|
|
case "openrouter":
|
|
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
if strings.Contains(base, "/v1") {
|
|
request.URL = base + "/key"
|
|
} else {
|
|
request.URL = base + "/v1/key"
|
|
}
|
|
default:
|
|
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
|
if strings.Contains(base, "/v1") {
|
|
request.URL = base + "/models"
|
|
} else {
|
|
request.URL = base + "/v1/models"
|
|
}
|
|
}
|
|
return request
|
|
}
|
|
|
|
func mustJSON(value interface{}) []byte {
|
|
payload, err := json.Marshal(value)
|
|
if err != nil {
|
|
return []byte("{}")
|
|
}
|
|
return payload
|
|
}
|