mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-21 00:24:12 +08:00
* feat: Optimize application upgrade logic * feat: Optimize application upgrade logic * feat: Optimize application upgrade logic
179 lines
5.8 KiB
Go
179 lines
5.8 KiB
Go
package provider
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
)
|
|
|
|
type VerifyRequest struct {
|
|
Method string
|
|
URL string
|
|
Headers map[string]string
|
|
Body []byte
|
|
}
|
|
|
|
type verifyErrorResponse struct {
|
|
Error struct {
|
|
Message string `json:"message"`
|
|
} `json:"error"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
const defaultVerifyTimeout = 30 * time.Second
|
|
|
|
func SkipVerification(provider string) bool {
|
|
switch provider {
|
|
case "vllm", "ollama", "kimi-coding":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func VerifyAccount(provider, apiType, authMode, baseURL, apiKey, model string) error {
|
|
req := BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model)
|
|
httpReq, err := http.NewRequest(req.Method, req.URL, bytes.NewReader(req.Body))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for key, value := range req.Headers {
|
|
httpReq.Header.Set(key, value)
|
|
}
|
|
httpReq.Header.Set("Accept", "application/json")
|
|
resp, err := (&http.Client{Timeout: defaultVerifyTimeout}).Do(httpReq)
|
|
if err != nil {
|
|
return buserr.WithErr("ErrAgentAccountUnavailable", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
|
|
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 1024*1024))
|
|
if readErr != nil {
|
|
return buserr.WithErr("ErrAgentAccountUnavailable", readErr)
|
|
}
|
|
return buserr.WithErr("ErrAgentAccountUnavailable", errors.New(verifyHTTPError(resp.StatusCode, body)))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model string) VerifyRequest {
|
|
baseURL = strings.TrimSpace(baseURL)
|
|
if provider != "custom" || !IsImageAPIType(apiType) {
|
|
baseURL = strings.TrimRight(baseURL, "/")
|
|
}
|
|
headers := map[string]string{"Content-Type": "application/json"}
|
|
request := VerifyRequest{Method: http.MethodPost, Headers: headers}
|
|
|
|
if provider == "gemini" {
|
|
request.URL = baseURL + "/v1beta/models/" + strings.TrimSpace(model) + ":generateContent"
|
|
headers["x-goog-api-key"] = apiKey
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"contents": []map[string]interface{}{{"parts": []map[string]string{{"text": "test"}}}},
|
|
})
|
|
return request
|
|
}
|
|
|
|
switch apiType {
|
|
case "openai-embeddings":
|
|
request.URL = embeddingVerifyURL(baseURL)
|
|
headers["Authorization"] = "Bearer " + apiKey
|
|
request.Body = mustJSON(map[string]interface{}{"model": model, "input": "ping"})
|
|
case "openai-images":
|
|
request.URL = imageVerifyURL(provider, baseURL, "/images/generations")
|
|
headers["Authorization"] = "Bearer " + apiKey
|
|
request.Body = mustJSON(map[string]interface{}{"model": model, "prompt": "test", "n": 1, "response_format": "url"})
|
|
case "dashscope-images":
|
|
request.URL = imageVerifyURL(provider, baseURL, "/api/v1/services/aigc/multimodal-generation/generation")
|
|
headers["Authorization"] = "Bearer " + apiKey
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"model": model,
|
|
"input": map[string]interface{}{"messages": []map[string]interface{}{
|
|
{"role": "user", "content": []map[string]string{{"text": "test"}}},
|
|
}},
|
|
"parameters": map[string]interface{}{"n": 1},
|
|
})
|
|
case "minimax-images":
|
|
request.URL = imageVerifyURL(provider, baseURL, "/v1/image_generation")
|
|
headers["Authorization"] = "Bearer " + apiKey
|
|
request.Body = mustJSON(map[string]interface{}{"model": model, "prompt": "test", "n": 1, "response_format": "url"})
|
|
case "openrouter-images":
|
|
request.URL = imageVerifyURL(provider, baseURL, "/api/v1/images")
|
|
headers["Authorization"] = "Bearer " + apiKey
|
|
request.Body = mustJSON(map[string]interface{}{"model": model, "prompt": "test", "n": 1, "response_format": "url"})
|
|
case "anthropic-messages":
|
|
request.URL = baseURL + "/v1/messages"
|
|
if authMode == AuthModeBearer {
|
|
headers["Authorization"] = "Bearer " + apiKey
|
|
} else {
|
|
headers["x-api-key"] = apiKey
|
|
}
|
|
headers["anthropic-version"] = "2023-06-01"
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"model": model, "max_tokens": 1, "stream": false,
|
|
"messages": []map[string]interface{}{{"role": "user", "content": []map[string]string{{"type": "text", "text": "test"}}}},
|
|
})
|
|
case "openai-responses":
|
|
request.URL = baseURL + "/responses"
|
|
headers["Authorization"] = "Bearer " + apiKey
|
|
request.Body = mustJSON(map[string]interface{}{"model": model, "input": "test", "max_output_tokens": 1, "stream": false})
|
|
default:
|
|
request.URL = baseURL + "/chat/completions"
|
|
if provider != "ollama" || strings.TrimSpace(apiKey) != "" {
|
|
headers["Authorization"] = "Bearer " + apiKey
|
|
}
|
|
request.Body = mustJSON(map[string]interface{}{
|
|
"model": model, "messages": []map[string]string{{"role": "user", "content": "test"}}, "max_tokens": 1, "stream": false,
|
|
})
|
|
}
|
|
return request
|
|
}
|
|
|
|
func embeddingVerifyURL(baseURL string) string {
|
|
lowerBaseURL := strings.ToLower(baseURL)
|
|
if strings.HasSuffix(lowerBaseURL, "/embeddings") {
|
|
return baseURL
|
|
}
|
|
if strings.HasSuffix(lowerBaseURL, "/v1") {
|
|
return baseURL + "/embeddings"
|
|
}
|
|
return baseURL + "/v1/embeddings"
|
|
}
|
|
|
|
func imageVerifyURL(provider, baseURL, endpoint string) string {
|
|
if provider == "custom" || strings.HasSuffix(strings.ToLower(baseURL), endpoint) {
|
|
return baseURL
|
|
}
|
|
return baseURL + endpoint
|
|
}
|
|
|
|
func verifyHTTPError(statusCode int, body []byte) string {
|
|
message := strings.TrimSpace(string(body))
|
|
var payload verifyErrorResponse
|
|
if err := json.Unmarshal(body, &payload); err == nil {
|
|
if value := strings.TrimSpace(payload.Error.Message); value != "" {
|
|
message = value
|
|
} else if value := strings.TrimSpace(payload.Message); value != "" {
|
|
message = value
|
|
}
|
|
}
|
|
if message == "" {
|
|
return fmt.Sprintf("validation request returned status %d", statusCode)
|
|
}
|
|
return message
|
|
}
|
|
|
|
func mustJSON(value interface{}) []byte {
|
|
payload, err := json.Marshal(value)
|
|
if err != nil {
|
|
return []byte("{}")
|
|
}
|
|
return payload
|
|
}
|