mirror of
https://github.com/githubhjs/CLIProxyAPIPlus.git
synced 2026-07-13 18:15:24 +00:00
Merge branch 'router-for-me:main' into main
This commit is contained in:
@@ -1,16 +1,28 @@
|
|||||||
package management
|
package management
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
|
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
latestReleaseURL = "https://api.github.com/repos/router-for-me/CLIProxyAPI/releases/latest"
|
||||||
|
latestReleaseUserAgent = "CLIProxyAPI"
|
||||||
|
)
|
||||||
|
|
||||||
func (h *Handler) GetConfig(c *gin.Context) {
|
func (h *Handler) GetConfig(c *gin.Context) {
|
||||||
if h == nil || h.cfg == nil {
|
if h == nil || h.cfg == nil {
|
||||||
c.JSON(200, gin.H{})
|
c.JSON(200, gin.H{})
|
||||||
@@ -20,6 +32,66 @@ func (h *Handler) GetConfig(c *gin.Context) {
|
|||||||
c.JSON(200, &cfgCopy)
|
c.JSON(200, &cfgCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type releaseInfo struct {
|
||||||
|
TagName string `json:"tag_name"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestVersion returns the latest release version from GitHub without downloading assets.
|
||||||
|
func (h *Handler) GetLatestVersion(c *gin.Context) {
|
||||||
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
|
proxyURL := ""
|
||||||
|
if h != nil && h.cfg != nil {
|
||||||
|
proxyURL = strings.TrimSpace(h.cfg.ProxyURL)
|
||||||
|
}
|
||||||
|
if proxyURL != "" {
|
||||||
|
sdkCfg := &sdkconfig.SDKConfig{ProxyURL: proxyURL}
|
||||||
|
util.SetProxy(sdkCfg, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(c.Request.Context(), http.MethodGet, latestReleaseURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "request_create_failed", "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", "application/vnd.github+json")
|
||||||
|
req.Header.Set("User-Agent", latestReleaseUserAgent)
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadGateway, gin.H{"error": "request_failed", "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if errClose := resp.Body.Close(); errClose != nil {
|
||||||
|
log.WithError(errClose).Debug("failed to close latest version response body")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
|
||||||
|
c.JSON(http.StatusBadGateway, gin.H{"error": "unexpected_status", "message": fmt.Sprintf("status %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var info releaseInfo
|
||||||
|
if errDecode := json.NewDecoder(resp.Body).Decode(&info); errDecode != nil {
|
||||||
|
c.JSON(http.StatusBadGateway, gin.H{"error": "decode_failed", "message": errDecode.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
version := strings.TrimSpace(info.TagName)
|
||||||
|
if version == "" {
|
||||||
|
version = strings.TrimSpace(info.Name)
|
||||||
|
}
|
||||||
|
if version == "" {
|
||||||
|
c.JSON(http.StatusBadGateway, gin.H{"error": "invalid_response", "message": "missing release version"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"latest-version": version})
|
||||||
|
}
|
||||||
|
|
||||||
func WriteConfig(path string, data []byte) error {
|
func WriteConfig(path string, data []byte) error {
|
||||||
data = config.NormalizeCommentIndentation(data)
|
data = config.NormalizeCommentIndentation(data)
|
||||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
|||||||
@@ -472,6 +472,7 @@ func (s *Server) registerManagementRoutes() {
|
|||||||
mgmt.GET("/config", s.mgmt.GetConfig)
|
mgmt.GET("/config", s.mgmt.GetConfig)
|
||||||
mgmt.GET("/config.yaml", s.mgmt.GetConfigYAML)
|
mgmt.GET("/config.yaml", s.mgmt.GetConfigYAML)
|
||||||
mgmt.PUT("/config.yaml", s.mgmt.PutConfigYAML)
|
mgmt.PUT("/config.yaml", s.mgmt.PutConfigYAML)
|
||||||
|
mgmt.GET("/latest-version", s.mgmt.GetLatestVersion)
|
||||||
|
|
||||||
mgmt.GET("/debug", s.mgmt.GetDebug)
|
mgmt.GET("/debug", s.mgmt.GetDebug)
|
||||||
mgmt.PUT("/debug", s.mgmt.PutDebug)
|
mgmt.PUT("/debug", s.mgmt.PutDebug)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
||||||
@@ -510,6 +511,24 @@ func (e *AntigravityExecutor) buildRequest(ctx context.Context, auth *cliproxyau
|
|||||||
|
|
||||||
payload = geminiToAntigravity(modelName, payload)
|
payload = geminiToAntigravity(modelName, payload)
|
||||||
payload, _ = sjson.SetBytes(payload, "model", alias2ModelName(modelName))
|
payload, _ = sjson.SetBytes(payload, "model", alias2ModelName(modelName))
|
||||||
|
|
||||||
|
if strings.Contains(modelName, "claude") {
|
||||||
|
strJSON := string(payload)
|
||||||
|
paths := make([]string, 0)
|
||||||
|
util.Walk(gjson.ParseBytes(payload), "", "parametersJsonSchema", &paths)
|
||||||
|
for _, p := range paths {
|
||||||
|
strJSON, _ = util.RenameKey(strJSON, p, p[:len(p)-len("parametersJsonSchema")]+"parameters")
|
||||||
|
}
|
||||||
|
|
||||||
|
strJSON = util.DeleteKey(strJSON, "$schema")
|
||||||
|
strJSON = util.DeleteKey(strJSON, "maxItems")
|
||||||
|
strJSON = util.DeleteKey(strJSON, "minItems")
|
||||||
|
strJSON = util.DeleteKey(strJSON, "minLength")
|
||||||
|
strJSON = util.DeleteKey(strJSON, "maxLength")
|
||||||
|
|
||||||
|
payload = []byte(strJSON)
|
||||||
|
}
|
||||||
|
|
||||||
httpReq, errReq := http.NewRequestWithContext(ctx, http.MethodPost, requestURL.String(), bytes.NewReader(payload))
|
httpReq, errReq := http.NewRequestWithContext(ctx, http.MethodPost, requestURL.String(), bytes.NewReader(payload))
|
||||||
if errReq != nil {
|
if errReq != nil {
|
||||||
return nil, errReq
|
return nil, errReq
|
||||||
|
|||||||
@@ -79,6 +79,15 @@ func RenameKey(jsonStr, oldKeyPath, newKeyPath string) (string, error) {
|
|||||||
return finalJson, nil
|
return finalJson, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeleteKey(jsonStr, keyName string) string {
|
||||||
|
paths := make([]string, 0)
|
||||||
|
Walk(gjson.Parse(jsonStr), "", keyName, &paths)
|
||||||
|
for _, p := range paths {
|
||||||
|
jsonStr, _ = sjson.Delete(jsonStr, p)
|
||||||
|
}
|
||||||
|
return jsonStr
|
||||||
|
}
|
||||||
|
|
||||||
// FixJSON converts non-standard JSON that uses single quotes for strings into
|
// FixJSON converts non-standard JSON that uses single quotes for strings into
|
||||||
// RFC 8259-compliant JSON by converting those single-quoted strings to
|
// RFC 8259-compliant JSON by converting those single-quoted strings to
|
||||||
// double-quoted strings with proper escaping.
|
// double-quoted strings with proper escaping.
|
||||||
|
|||||||
Reference in New Issue
Block a user