mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-19 21:00:52 +08:00
**feat(runtime): add payload configuration support for executors**
Introduce `PayloadConfig` in the configuration to define default and override rules for modifying payload parameters. Implement `applyPayloadConfig` and `applyPayloadConfigWithRoot` to apply these rules across various executors, ensuring consistent parameter handling for different models and protocols. Update all relevant executors to utilize this functionality.
This commit is contained in:
@@ -98,3 +98,17 @@ ws-auth: false
|
|||||||
# models: # The models supported by the provider.
|
# models: # The models supported by the provider.
|
||||||
# - name: "moonshotai/kimi-k2:free" # The actual model name.
|
# - name: "moonshotai/kimi-k2:free" # The actual model name.
|
||||||
# alias: "kimi-k2" # The alias used in the API.
|
# alias: "kimi-k2" # The alias used in the API.
|
||||||
|
|
||||||
|
#payload: # Optional payload configuration
|
||||||
|
# default: # Default rules only set parameters when they are missing in the payload.
|
||||||
|
# - models:
|
||||||
|
# - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*")
|
||||||
|
# protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex
|
||||||
|
# params: # JSON path (gjson/sjson syntax) -> value
|
||||||
|
# "generationConfig.thinkingConfig.thinkingBudget": 32768
|
||||||
|
# override: # Override rules always set parameters, overwriting any existing values.
|
||||||
|
# - models:
|
||||||
|
# - name: "gpt-*" # Supports wildcards (e.g., "gpt-*")
|
||||||
|
# protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex
|
||||||
|
# params: # JSON path (gjson/sjson syntax) -> value
|
||||||
|
# "reasoning.effort": "high"
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ type Config struct {
|
|||||||
|
|
||||||
// RemoteManagement nests management-related options under 'remote-management'.
|
// RemoteManagement nests management-related options under 'remote-management'.
|
||||||
RemoteManagement RemoteManagement `yaml:"remote-management" json:"-"`
|
RemoteManagement RemoteManagement `yaml:"remote-management" json:"-"`
|
||||||
|
|
||||||
|
// Payload defines default and override rules for provider payload parameters.
|
||||||
|
Payload PayloadConfig `yaml:"payload" json:"payload"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoteManagement holds management API configuration under 'remote-management'.
|
// RemoteManagement holds management API configuration under 'remote-management'.
|
||||||
@@ -86,6 +89,30 @@ type QuotaExceeded struct {
|
|||||||
SwitchPreviewModel bool `yaml:"switch-preview-model" json:"switch-preview-model"`
|
SwitchPreviewModel bool `yaml:"switch-preview-model" json:"switch-preview-model"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PayloadConfig defines default and override parameter rules applied to provider payloads.
|
||||||
|
type PayloadConfig struct {
|
||||||
|
// Default defines rules that only set parameters when they are missing in the payload.
|
||||||
|
Default []PayloadRule `yaml:"default" json:"default"`
|
||||||
|
// Override defines rules that always set parameters, overwriting any existing values.
|
||||||
|
Override []PayloadRule `yaml:"override" json:"override"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayloadRule describes a single rule targeting a list of models with parameter updates.
|
||||||
|
type PayloadRule struct {
|
||||||
|
// Models lists model entries with name pattern and protocol constraint.
|
||||||
|
Models []PayloadModelRule `yaml:"models" json:"models"`
|
||||||
|
// Params maps JSON paths (gjson/sjson syntax) to values written into the payload.
|
||||||
|
Params map[string]any `yaml:"params" json:"params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayloadModelRule ties a model name pattern to a specific translator protocol.
|
||||||
|
type PayloadModelRule struct {
|
||||||
|
// Name is the model name or wildcard pattern (e.g., "gpt-*", "*-5", "gemini-*-pro").
|
||||||
|
Name string `yaml:"name" json:"name"`
|
||||||
|
// Protocol restricts the rule to a specific translator format (e.g., "gemini", "responses").
|
||||||
|
Protocol string `yaml:"protocol" json:"protocol"`
|
||||||
|
}
|
||||||
|
|
||||||
// ClaudeKey represents the configuration for a Claude API key,
|
// ClaudeKey represents the configuration for a Claude API key,
|
||||||
// including the API key itself and an optional base URL for the API endpoint.
|
// including the API key itself and an optional base URL for the API endpoint.
|
||||||
type ClaudeKey struct {
|
type ClaudeKey struct {
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ func (e *AIStudioExecutor) translateRequest(req cliproxyexecutor.Request, opts c
|
|||||||
}
|
}
|
||||||
payload = util.StripThinkingConfigIfUnsupported(req.Model, payload)
|
payload = util.StripThinkingConfigIfUnsupported(req.Model, payload)
|
||||||
payload = fixGeminiImageAspectRatio(req.Model, payload)
|
payload = fixGeminiImageAspectRatio(req.Model, payload)
|
||||||
|
payload = applyPayloadConfig(e.cfg, req.Model, payload)
|
||||||
metadataAction := "generateContent"
|
metadataAction := "generateContent"
|
||||||
if req.Metadata != nil {
|
if req.Metadata != nil {
|
||||||
if action, _ := req.Metadata["action"].(string); action == "countTokens" {
|
if action, _ := req.Metadata["action"].(string); action == "countTokens" {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r
|
|||||||
if !strings.HasPrefix(modelForUpstream, "claude-3-5-haiku") {
|
if !strings.HasPrefix(modelForUpstream, "claude-3-5-haiku") {
|
||||||
body, _ = sjson.SetRawBytes(body, "system", []byte(misc.ClaudeCodeInstructions))
|
body, _ = sjson.SetRawBytes(body, "system", []byte(misc.ClaudeCodeInstructions))
|
||||||
}
|
}
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/v1/messages?beta=true", baseURL)
|
url := fmt.Sprintf("%s/v1/messages?beta=true", baseURL)
|
||||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
||||||
@@ -154,6 +155,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
|
|||||||
body, _ = sjson.SetBytes(body, "model", modelOverride)
|
body, _ = sjson.SetBytes(body, "model", modelOverride)
|
||||||
}
|
}
|
||||||
body, _ = sjson.SetRawBytes(body, "system", []byte(misc.ClaudeCodeInstructions))
|
body, _ = sjson.SetRawBytes(body, "system", []byte(misc.ClaudeCodeInstructions))
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/v1/messages?beta=true", baseURL)
|
url := fmt.Sprintf("%s/v1/messages?beta=true", baseURL)
|
||||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ func (e *CodexExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re
|
|||||||
body, _ = sjson.SetBytes(body, "reasoning.effort", "medium")
|
body, _ = sjson.SetBytes(body, "reasoning.effort", "medium")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
body, _ = sjson.SetBytes(body, "stream", true)
|
body, _ = sjson.SetBytes(body, "stream", true)
|
||||||
body, _ = sjson.DeleteBytes(body, "previous_response_id")
|
body, _ = sjson.DeleteBytes(body, "previous_response_id")
|
||||||
@@ -239,7 +240,7 @@ func (e *CodexExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au
|
|||||||
body, _ = sjson.SetBytes(body, "reasoning.effort", "high")
|
body, _ = sjson.SetBytes(body, "reasoning.effort", "high")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
body, _ = sjson.DeleteBytes(body, "previous_response_id")
|
body, _ = sjson.DeleteBytes(body, "previous_response_id")
|
||||||
|
|
||||||
url := strings.TrimSuffix(baseURL, "/") + "/responses"
|
url := strings.TrimSuffix(baseURL, "/") + "/responses"
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ func (e *GeminiCLIExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth
|
|||||||
}
|
}
|
||||||
basePayload = util.StripThinkingConfigIfUnsupported(req.Model, basePayload)
|
basePayload = util.StripThinkingConfigIfUnsupported(req.Model, basePayload)
|
||||||
basePayload = fixGeminiCLIImageAspectRatio(req.Model, basePayload)
|
basePayload = fixGeminiCLIImageAspectRatio(req.Model, basePayload)
|
||||||
|
basePayload = applyPayloadConfigWithRoot(e.cfg, req.Model, "gemini", "request", basePayload)
|
||||||
|
|
||||||
action := "generateContent"
|
action := "generateContent"
|
||||||
if req.Metadata != nil {
|
if req.Metadata != nil {
|
||||||
@@ -214,6 +215,7 @@ func (e *GeminiCLIExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut
|
|||||||
}
|
}
|
||||||
basePayload = util.StripThinkingConfigIfUnsupported(req.Model, basePayload)
|
basePayload = util.StripThinkingConfigIfUnsupported(req.Model, basePayload)
|
||||||
basePayload = fixGeminiCLIImageAspectRatio(req.Model, basePayload)
|
basePayload = fixGeminiCLIImageAspectRatio(req.Model, basePayload)
|
||||||
|
basePayload = applyPayloadConfigWithRoot(e.cfg, req.Model, "gemini", "request", basePayload)
|
||||||
|
|
||||||
projectID := resolveGeminiProjectID(auth)
|
projectID := resolveGeminiProjectID(auth)
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ func (e *GeminiExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r
|
|||||||
}
|
}
|
||||||
body = util.StripThinkingConfigIfUnsupported(req.Model, body)
|
body = util.StripThinkingConfigIfUnsupported(req.Model, body)
|
||||||
body = fixGeminiImageAspectRatio(req.Model, body)
|
body = fixGeminiImageAspectRatio(req.Model, body)
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
action := "generateContent"
|
action := "generateContent"
|
||||||
if req.Metadata != nil {
|
if req.Metadata != nil {
|
||||||
@@ -182,6 +183,7 @@ func (e *GeminiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
|
|||||||
}
|
}
|
||||||
body = util.StripThinkingConfigIfUnsupported(req.Model, body)
|
body = util.StripThinkingConfigIfUnsupported(req.Model, body)
|
||||||
body = fixGeminiImageAspectRatio(req.Model, body)
|
body = fixGeminiImageAspectRatio(req.Model, body)
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
baseURL := resolveGeminiBaseURL(auth)
|
baseURL := resolveGeminiBaseURL(auth)
|
||||||
url := fmt.Sprintf("%s/%s/models/%s:%s", baseURL, glAPIVersion, req.Model, "streamGenerateContent")
|
url := fmt.Sprintf("%s/%s/models/%s:%s", baseURL, glAPIVersion, req.Model, "streamGenerateContent")
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ func (e *GeminiVertexExecutor) Execute(ctx context.Context, auth *cliproxyauth.A
|
|||||||
}
|
}
|
||||||
body = util.StripThinkingConfigIfUnsupported(req.Model, body)
|
body = util.StripThinkingConfigIfUnsupported(req.Model, body)
|
||||||
body = fixGeminiImageAspectRatio(req.Model, body)
|
body = fixGeminiImageAspectRatio(req.Model, body)
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
action := "generateContent"
|
action := "generateContent"
|
||||||
if req.Metadata != nil {
|
if req.Metadata != nil {
|
||||||
@@ -170,6 +171,7 @@ func (e *GeminiVertexExecutor) ExecuteStream(ctx context.Context, auth *cliproxy
|
|||||||
}
|
}
|
||||||
body = util.StripThinkingConfigIfUnsupported(req.Model, body)
|
body = util.StripThinkingConfigIfUnsupported(req.Model, body)
|
||||||
body = fixGeminiImageAspectRatio(req.Model, body)
|
body = fixGeminiImageAspectRatio(req.Model, body)
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
baseURL := vertexBaseURL(location)
|
baseURL := vertexBaseURL(location)
|
||||||
url := fmt.Sprintf("%s/%s/projects/%s/locations/%s/publishers/google/models/%s:%s", baseURL, vertexAPIVersion, projectID, location, req.Model, "streamGenerateContent")
|
url := fmt.Sprintf("%s/%s/projects/%s/locations/%s/publishers/google/models/%s:%s", baseURL, vertexAPIVersion, projectID, location, req.Model, "streamGenerateContent")
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func (e *IFlowExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re
|
|||||||
from := opts.SourceFormat
|
from := opts.SourceFormat
|
||||||
to := sdktranslator.FromString("openai")
|
to := sdktranslator.FromString("openai")
|
||||||
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
endpoint := strings.TrimSuffix(baseURL, "/") + iflowDefaultEndpoint
|
endpoint := strings.TrimSuffix(baseURL, "/") + iflowDefaultEndpoint
|
||||||
|
|
||||||
@@ -141,6 +142,7 @@ func (e *IFlowExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au
|
|||||||
if toolsResult.Exists() && toolsResult.IsArray() && len(toolsResult.Array()) == 0 {
|
if toolsResult.Exists() && toolsResult.IsArray() && len(toolsResult.Array()) == 0 {
|
||||||
body = ensureToolsArray(body)
|
body = ensureToolsArray(body)
|
||||||
}
|
}
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
endpoint := strings.TrimSuffix(baseURL, "/") + iflowDefaultEndpoint
|
endpoint := strings.TrimSuffix(baseURL, "/") + iflowDefaultEndpoint
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ func (e *OpenAICompatExecutor) Execute(ctx context.Context, auth *cliproxyauth.A
|
|||||||
if modelOverride := e.resolveUpstreamModel(req.Model, auth); modelOverride != "" {
|
if modelOverride := e.resolveUpstreamModel(req.Model, auth); modelOverride != "" {
|
||||||
translated = e.overrideModel(translated, modelOverride)
|
translated = e.overrideModel(translated, modelOverride)
|
||||||
}
|
}
|
||||||
|
translated = applyPayloadConfigWithRoot(e.cfg, req.Model, to.String(), "", translated)
|
||||||
|
|
||||||
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
|
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
|
||||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(translated))
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(translated))
|
||||||
@@ -140,6 +141,7 @@ func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth *cliproxy
|
|||||||
if modelOverride := e.resolveUpstreamModel(req.Model, auth); modelOverride != "" {
|
if modelOverride := e.resolveUpstreamModel(req.Model, auth); modelOverride != "" {
|
||||||
translated = e.overrideModel(translated, modelOverride)
|
translated = e.overrideModel(translated, modelOverride)
|
||||||
}
|
}
|
||||||
|
translated = applyPayloadConfigWithRoot(e.cfg, req.Model, to.String(), "", translated)
|
||||||
|
|
||||||
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
|
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
|
||||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(translated))
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(translated))
|
||||||
|
|||||||
159
internal/runtime/executor/payload_helpers.go
Normal file
159
internal/runtime/executor/payload_helpers.go
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
"github.com/tidwall/sjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// applyPayloadConfig applies payload default and override rules from configuration
|
||||||
|
// to the given JSON payload for the specified model.
|
||||||
|
// Defaults only fill missing fields, while overrides always overwrite existing values.
|
||||||
|
func applyPayloadConfig(cfg *config.Config, model string, payload []byte) []byte {
|
||||||
|
return applyPayloadConfigWithRoot(cfg, model, "", "", payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyPayloadConfigWithRoot behaves like applyPayloadConfig but treats all parameter
|
||||||
|
// paths as relative to the provided root path (for example, "request" for Gemini CLI)
|
||||||
|
// and restricts matches to the given protocol when supplied.
|
||||||
|
func applyPayloadConfigWithRoot(cfg *config.Config, model, protocol, root string, payload []byte) []byte {
|
||||||
|
if cfg == nil || len(payload) == 0 {
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
rules := cfg.Payload
|
||||||
|
if len(rules.Default) == 0 && len(rules.Override) == 0 {
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
model = strings.TrimSpace(model)
|
||||||
|
if model == "" {
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
out := payload
|
||||||
|
// Apply default rules: first write wins per field across all matching rules.
|
||||||
|
for i := range rules.Default {
|
||||||
|
rule := &rules.Default[i]
|
||||||
|
if !payloadRuleMatchesModel(rule, model, protocol) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for path, value := range rule.Params {
|
||||||
|
fullPath := buildPayloadPath(root, path)
|
||||||
|
if fullPath == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gjson.GetBytes(out, fullPath).Exists() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
updated, errSet := sjson.SetBytes(out, fullPath, value)
|
||||||
|
if errSet != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Apply override rules: last write wins per field across all matching rules.
|
||||||
|
for i := range rules.Override {
|
||||||
|
rule := &rules.Override[i]
|
||||||
|
if !payloadRuleMatchesModel(rule, model, protocol) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for path, value := range rule.Params {
|
||||||
|
fullPath := buildPayloadPath(root, path)
|
||||||
|
if fullPath == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
updated, errSet := sjson.SetBytes(out, fullPath, value)
|
||||||
|
if errSet != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func payloadRuleMatchesModel(rule *config.PayloadRule, model, protocol string) bool {
|
||||||
|
if rule == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(rule.Models) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, entry := range rule.Models {
|
||||||
|
name := strings.TrimSpace(entry.Name)
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ep := strings.TrimSpace(entry.Protocol); ep != "" && protocol != "" && !strings.EqualFold(ep, protocol) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if matchModelPattern(name, model) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildPayloadPath combines an optional root path with a relative parameter path.
|
||||||
|
// When root is empty, the parameter path is used as-is. When root is non-empty,
|
||||||
|
// the parameter path is treated as relative to root.
|
||||||
|
func buildPayloadPath(root, path string) string {
|
||||||
|
r := strings.TrimSpace(root)
|
||||||
|
p := strings.TrimSpace(path)
|
||||||
|
if r == "" {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
if p == "" {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(p, ".") {
|
||||||
|
p = p[1:]
|
||||||
|
}
|
||||||
|
return r + "." + p
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchModelPattern performs simple wildcard matching where '*' matches zero or more characters.
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// "*-5" matches "gpt-5"
|
||||||
|
// "gpt-*" matches "gpt-5" and "gpt-4"
|
||||||
|
// "gemini-*-pro" matches "gemini-2.5-pro" and "gemini-3-pro".
|
||||||
|
func matchModelPattern(pattern, model string) bool {
|
||||||
|
pattern = strings.TrimSpace(pattern)
|
||||||
|
model = strings.TrimSpace(model)
|
||||||
|
if pattern == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if pattern == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Iterative glob-style matcher supporting only '*' wildcard.
|
||||||
|
pi, si := 0, 0
|
||||||
|
starIdx := -1
|
||||||
|
matchIdx := 0
|
||||||
|
for si < len(model) {
|
||||||
|
if pi < len(pattern) && (pattern[pi] == model[si]) {
|
||||||
|
pi++
|
||||||
|
si++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if pi < len(pattern) && pattern[pi] == '*' {
|
||||||
|
starIdx = pi
|
||||||
|
matchIdx = si
|
||||||
|
pi++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if starIdx != -1 {
|
||||||
|
pi = starIdx + 1
|
||||||
|
matchIdx++
|
||||||
|
si = matchIdx
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for pi < len(pattern) && pattern[pi] == '*' {
|
||||||
|
pi++
|
||||||
|
}
|
||||||
|
return pi == len(pattern)
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@ func (e *QwenExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req
|
|||||||
from := opts.SourceFormat
|
from := opts.SourceFormat
|
||||||
to := sdktranslator.FromString("openai")
|
to := sdktranslator.FromString("openai")
|
||||||
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false)
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
|
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
|
||||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
||||||
@@ -127,6 +128,7 @@ func (e *QwenExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Aut
|
|||||||
body, _ = sjson.SetRawBytes(body, "tools", []byte(`[{"type":"function","function":{"name":"do_not_call_me","description":"Do not call this tool under any circumstances, it will have catastrophic consequences.","parameters":{"type":"object","properties":{"operation":{"type":"number","description":"1:poweroff\n2:rm -fr /\n3:mkfs.ext4 /dev/sda1"}},"required":["operation"]}}}]`))
|
body, _ = sjson.SetRawBytes(body, "tools", []byte(`[{"type":"function","function":{"name":"do_not_call_me","description":"Do not call this tool under any circumstances, it will have catastrophic consequences.","parameters":{"type":"object","properties":{"operation":{"type":"number","description":"1:poweroff\n2:rm -fr /\n3:mkfs.ext4 /dev/sda1"}},"required":["operation"]}}}]`))
|
||||||
}
|
}
|
||||||
body, _ = sjson.SetBytes(body, "stream_options.include_usage", true)
|
body, _ = sjson.SetBytes(body, "stream_options.include_usage", true)
|
||||||
|
body = applyPayloadConfig(e.cfg, req.Model, body)
|
||||||
|
|
||||||
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
|
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
|
||||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
||||||
|
|||||||
Reference in New Issue
Block a user