Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
d2332685
authored
Mar 05, 2026
by
Seefs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add wildcard path support and improve param override templates/editor
parent
f53f3269
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
510 additions
and
22 deletions
+510
-22
relay/common/override.go
+182
-15
relay/common/override_test.go
+256
-0
web/src/components/table/channels/modals/ParamOverrideEditorModal.jsx
+72
-7
No files found.
relay/common/override.go
View file @
d2332685
...
...
@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"regexp"
"sort"
"strconv"
"strings"
...
...
@@ -487,15 +488,35 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
}
// 处理路径中的负数索引
opPath
:=
processNegativeIndex
(
result
,
op
.
Path
)
var
opPaths
[]
string
if
isPathBasedOperation
(
op
.
Mode
)
{
opPaths
,
err
=
resolveOperationPaths
(
result
,
opPath
)
if
err
!=
nil
{
return
""
,
err
}
if
len
(
opPaths
)
==
0
{
continue
}
}
switch
op
.
Mode
{
case
"delete"
:
result
,
err
=
sjson
.
Delete
(
result
,
opPath
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
deleteValue
(
result
,
path
)
if
err
!=
nil
{
break
}
}
case
"set"
:
if
op
.
KeepOrigin
&&
gjson
.
Get
(
result
,
opPath
)
.
Exists
()
{
for
_
,
path
:=
range
opPaths
{
if
op
.
KeepOrigin
&&
gjson
.
Get
(
result
,
path
)
.
Exists
()
{
continue
}
result
,
err
=
sjson
.
Set
(
result
,
opPath
,
op
.
Value
)
result
,
err
=
sjson
.
Set
(
result
,
path
,
op
.
Value
)
if
err
!=
nil
{
break
}
}
case
"move"
:
opFrom
:=
processNegativeIndex
(
result
,
op
.
From
)
opTo
:=
processNegativeIndex
(
result
,
op
.
To
)
...
...
@@ -508,27 +529,82 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
opTo
:=
processNegativeIndex
(
result
,
op
.
To
)
result
,
err
=
copyValue
(
result
,
opFrom
,
opTo
)
case
"prepend"
:
result
,
err
=
modifyValue
(
result
,
opPath
,
op
.
Value
,
op
.
KeepOrigin
,
true
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
modifyValue
(
result
,
path
,
op
.
Value
,
op
.
KeepOrigin
,
true
)
if
err
!=
nil
{
break
}
}
case
"append"
:
result
,
err
=
modifyValue
(
result
,
opPath
,
op
.
Value
,
op
.
KeepOrigin
,
false
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
modifyValue
(
result
,
path
,
op
.
Value
,
op
.
KeepOrigin
,
false
)
if
err
!=
nil
{
break
}
}
case
"trim_prefix"
:
result
,
err
=
trimStringValue
(
result
,
opPath
,
op
.
Value
,
true
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
trimStringValue
(
result
,
path
,
op
.
Value
,
true
)
if
err
!=
nil
{
break
}
}
case
"trim_suffix"
:
result
,
err
=
trimStringValue
(
result
,
opPath
,
op
.
Value
,
false
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
trimStringValue
(
result
,
path
,
op
.
Value
,
false
)
if
err
!=
nil
{
break
}
}
case
"ensure_prefix"
:
result
,
err
=
ensureStringAffix
(
result
,
opPath
,
op
.
Value
,
true
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
ensureStringAffix
(
result
,
path
,
op
.
Value
,
true
)
if
err
!=
nil
{
break
}
}
case
"ensure_suffix"
:
result
,
err
=
ensureStringAffix
(
result
,
opPath
,
op
.
Value
,
false
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
ensureStringAffix
(
result
,
path
,
op
.
Value
,
false
)
if
err
!=
nil
{
break
}
}
case
"trim_space"
:
result
,
err
=
transformStringValue
(
result
,
opPath
,
strings
.
TrimSpace
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
transformStringValue
(
result
,
path
,
strings
.
TrimSpace
)
if
err
!=
nil
{
break
}
}
case
"to_lower"
:
result
,
err
=
transformStringValue
(
result
,
opPath
,
strings
.
ToLower
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
transformStringValue
(
result
,
path
,
strings
.
ToLower
)
if
err
!=
nil
{
break
}
}
case
"to_upper"
:
result
,
err
=
transformStringValue
(
result
,
opPath
,
strings
.
ToUpper
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
transformStringValue
(
result
,
path
,
strings
.
ToUpper
)
if
err
!=
nil
{
break
}
}
case
"replace"
:
result
,
err
=
replaceStringValue
(
result
,
opPath
,
op
.
From
,
op
.
To
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
replaceStringValue
(
result
,
path
,
op
.
From
,
op
.
To
)
if
err
!=
nil
{
break
}
}
case
"regex_replace"
:
result
,
err
=
regexReplaceStringValue
(
result
,
opPath
,
op
.
From
,
op
.
To
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
regexReplaceStringValue
(
result
,
path
,
op
.
From
,
op
.
To
)
if
err
!=
nil
{
break
}
}
case
"return_error"
:
returnErr
,
parseErr
:=
parseParamOverrideReturnError
(
op
.
Value
)
if
parseErr
!=
nil
{
...
...
@@ -536,7 +612,12 @@ func applyOperations(jsonStr string, operations []ParamOperation, conditionConte
}
return
""
,
returnErr
case
"prune_objects"
:
result
,
err
=
pruneObjects
(
result
,
opPath
,
contextJSON
,
op
.
Value
)
for
_
,
path
:=
range
opPaths
{
result
,
err
=
pruneObjects
(
result
,
path
,
contextJSON
,
op
.
Value
)
if
err
!=
nil
{
break
}
}
case
"set_header"
:
err
=
setHeaderOverrideInContext
(
context
,
op
.
Path
,
op
.
Value
,
op
.
KeepOrigin
)
if
err
==
nil
{
...
...
@@ -1174,6 +1255,92 @@ func copyValue(jsonStr, fromPath, toPath string) (string, error) {
return
sjson
.
Set
(
jsonStr
,
toPath
,
sourceValue
.
Value
())
}
func
isPathBasedOperation
(
mode
string
)
bool
{
switch
mode
{
case
"delete"
,
"set"
,
"prepend"
,
"append"
,
"trim_prefix"
,
"trim_suffix"
,
"ensure_prefix"
,
"ensure_suffix"
,
"trim_space"
,
"to_lower"
,
"to_upper"
,
"replace"
,
"regex_replace"
,
"prune_objects"
:
return
true
default
:
return
false
}
}
func
resolveOperationPaths
(
jsonStr
,
path
string
)
([]
string
,
error
)
{
if
!
strings
.
Contains
(
path
,
"*"
)
{
return
[]
string
{
path
},
nil
}
return
expandWildcardPaths
(
jsonStr
,
path
)
}
func
expandWildcardPaths
(
jsonStr
,
path
string
)
([]
string
,
error
)
{
var
root
interface
{}
if
err
:=
common
.
Unmarshal
([]
byte
(
jsonStr
),
&
root
);
err
!=
nil
{
return
nil
,
err
}
segments
:=
strings
.
Split
(
path
,
"."
)
paths
:=
collectWildcardPaths
(
root
,
segments
,
nil
)
return
lo
.
Uniq
(
paths
),
nil
}
func
collectWildcardPaths
(
node
interface
{},
segments
[]
string
,
prefix
[]
string
)
[]
string
{
if
len
(
segments
)
==
0
{
return
[]
string
{
strings
.
Join
(
prefix
,
"."
)}
}
segment
:=
strings
.
TrimSpace
(
segments
[
0
])
if
segment
==
""
{
return
nil
}
isLast
:=
len
(
segments
)
==
1
if
segment
==
"*"
{
switch
typed
:=
node
.
(
type
)
{
case
map
[
string
]
interface
{}
:
keys
:=
lo
.
Keys
(
typed
)
sort
.
Strings
(
keys
)
return
lo
.
FlatMap
(
keys
,
func
(
key
string
,
_
int
)
[]
string
{
return
collectWildcardPaths
(
typed
[
key
],
segments
[
1
:
],
append
(
prefix
,
key
))
})
case
[]
interface
{}
:
return
lo
.
FlatMap
(
lo
.
Range
(
len
(
typed
)),
func
(
index
int
,
_
int
)
[]
string
{
return
collectWildcardPaths
(
typed
[
index
],
segments
[
1
:
],
append
(
prefix
,
strconv
.
Itoa
(
index
)))
})
default
:
return
nil
}
}
switch
typed
:=
node
.
(
type
)
{
case
map
[
string
]
interface
{}
:
if
isLast
{
return
[]
string
{
strings
.
Join
(
append
(
prefix
,
segment
),
"."
)}
}
next
,
exists
:=
typed
[
segment
]
if
!
exists
{
return
nil
}
return
collectWildcardPaths
(
next
,
segments
[
1
:
],
append
(
prefix
,
segment
))
case
[]
interface
{}
:
index
,
err
:=
strconv
.
Atoi
(
segment
)
if
err
!=
nil
||
index
<
0
||
index
>=
len
(
typed
)
{
return
nil
}
if
isLast
{
return
[]
string
{
strings
.
Join
(
append
(
prefix
,
segment
),
"."
)}
}
return
collectWildcardPaths
(
typed
[
index
],
segments
[
1
:
],
append
(
prefix
,
segment
))
default
:
return
nil
}
}
func
deleteValue
(
jsonStr
,
path
string
)
(
string
,
error
)
{
if
strings
.
TrimSpace
(
path
)
==
""
{
return
jsonStr
,
nil
}
return
sjson
.
Delete
(
jsonStr
,
path
)
}
func
modifyValue
(
jsonStr
,
path
string
,
value
interface
{},
keepOrigin
,
isPrepend
bool
)
(
string
,
error
)
{
current
:=
gjson
.
Get
(
jsonStr
,
path
)
switch
{
...
...
relay/common/override_test.go
View file @
d2332685
...
...
@@ -2,6 +2,7 @@ package common
import
(
"encoding/json"
"fmt"
"reflect"
"testing"
...
...
@@ -9,6 +10,7 @@ import (
"github.com/QuantumNous/new-api/dto"
"github.com/QuantumNous/new-api/setting/model_setting"
"github.com/samber/lo"
)
func
TestApplyParamOverrideTrimPrefix
(
t
*
testing
.
T
)
{
...
...
@@ -242,6 +244,224 @@ func TestApplyParamOverrideDelete(t *testing.T) {
}
}
func
TestApplyParamOverrideDeleteWildcardPath
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"tools":[{"type":"bash","custom":{"input_examples":["a"],"other":1}},{"type":"code","custom":{"input_examples":["b"]}},{"type":"noop","custom":{"other":2}}]}`
)
override
:=
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"path"
:
"tools.*.custom.input_examples"
,
"mode"
:
"delete"
,
},
},
}
out
,
err
:=
ApplyParamOverride
(
input
,
override
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"ApplyParamOverride returned error: %v"
,
err
)
}
assertJSONEqual
(
t
,
`{"tools":[{"type":"bash","custom":{"other":1}},{"type":"code","custom":{}},{"type":"noop","custom":{"other":2}}]}`
,
string
(
out
))
}
func
TestApplyParamOverrideSetWildcardPath
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"tools":[{"custom":{"tag":"A"}},{"custom":{"tag":"B"}},{"custom":{"tag":"C"}}]}`
)
override
:=
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"path"
:
"tools.*.custom.enabled"
,
"mode"
:
"set"
,
"value"
:
true
,
},
},
}
out
,
err
:=
ApplyParamOverride
(
input
,
override
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"ApplyParamOverride returned error: %v"
,
err
)
}
var
got
struct
{
Tools
[]
struct
{
Custom
struct
{
Enabled
bool
`json:"enabled"`
}
`json:"custom"`
}
`json:"tools"`
}
if
err
:=
json
.
Unmarshal
(
out
,
&
got
);
err
!=
nil
{
t
.
Fatalf
(
"failed to unmarshal output JSON: %v"
,
err
)
}
if
!
lo
.
EveryBy
(
got
.
Tools
,
func
(
item
struct
{
Custom
struct
{
Enabled
bool
`json:"enabled"`
}
`json:"custom"`
})
bool
{
return
item
.
Custom
.
Enabled
})
{
t
.
Fatalf
(
"expected wildcard set to enable all tools, got: %s"
,
string
(
out
))
}
}
func
TestApplyParamOverrideTrimSpaceWildcardPath
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"tools":[{"custom":{"name":" alpha "}},{"custom":{"name":" beta"}},{"custom":{"name":"gamma "}}]}`
)
override
:=
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"path"
:
"tools.*.custom.name"
,
"mode"
:
"trim_space"
,
},
},
}
out
,
err
:=
ApplyParamOverride
(
input
,
override
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"ApplyParamOverride returned error: %v"
,
err
)
}
var
got
struct
{
Tools
[]
struct
{
Custom
struct
{
Name
string
`json:"name"`
}
`json:"custom"`
}
`json:"tools"`
}
if
err
:=
json
.
Unmarshal
(
out
,
&
got
);
err
!=
nil
{
t
.
Fatalf
(
"failed to unmarshal output JSON: %v"
,
err
)
}
names
:=
lo
.
Map
(
got
.
Tools
,
func
(
item
struct
{
Custom
struct
{
Name
string
`json:"name"`
}
`json:"custom"`
},
_
int
)
string
{
return
item
.
Custom
.
Name
})
if
!
reflect
.
DeepEqual
(
names
,
[]
string
{
"alpha"
,
"beta"
,
"gamma"
})
{
t
.
Fatalf
(
"unexpected names after wildcard trim_space: %v"
,
names
)
}
}
func
TestApplyParamOverrideDeleteWildcardEqualsIndexedPaths
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"tools":[{"custom":{"input_examples":["a"],"other":1}},{"custom":{"input_examples":["b"],"other":2}},{"custom":{"input_examples":["c"],"other":3}}]}`
)
wildcardOverride
:=
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"path"
:
"tools.*.custom.input_examples"
,
"mode"
:
"delete"
,
},
},
}
indexedOverride
:=
map
[
string
]
interface
{}{
"operations"
:
lo
.
Map
(
lo
.
Range
(
3
),
func
(
index
int
,
_
int
)
interface
{}
{
return
map
[
string
]
interface
{}{
"path"
:
fmt
.
Sprintf
(
"tools.%d.custom.input_examples"
,
index
),
"mode"
:
"delete"
,
}
}),
}
wildcardOut
,
err
:=
ApplyParamOverride
(
input
,
wildcardOverride
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"wildcard ApplyParamOverride returned error: %v"
,
err
)
}
indexedOut
,
err
:=
ApplyParamOverride
(
input
,
indexedOverride
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"indexed ApplyParamOverride returned error: %v"
,
err
)
}
assertJSONEqual
(
t
,
string
(
indexedOut
),
string
(
wildcardOut
))
}
func
TestApplyParamOverrideSetWildcardKeepOrigin
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"tools":[{"custom":{"tag":"A"}},{"custom":{"tag":"B","enabled":false}},{"custom":{"tag":"C"}}]}`
)
override
:=
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"path"
:
"tools.*.custom.enabled"
,
"mode"
:
"set"
,
"value"
:
true
,
"keep_origin"
:
true
,
},
},
}
out
,
err
:=
ApplyParamOverride
(
input
,
override
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"ApplyParamOverride returned error: %v"
,
err
)
}
var
got
struct
{
Tools
[]
struct
{
Custom
struct
{
Enabled
bool
`json:"enabled"`
}
`json:"custom"`
}
`json:"tools"`
}
if
err
:=
json
.
Unmarshal
(
out
,
&
got
);
err
!=
nil
{
t
.
Fatalf
(
"failed to unmarshal output JSON: %v"
,
err
)
}
enabledValues
:=
lo
.
Map
(
got
.
Tools
,
func
(
item
struct
{
Custom
struct
{
Enabled
bool
`json:"enabled"`
}
`json:"custom"`
},
_
int
)
bool
{
return
item
.
Custom
.
Enabled
})
if
!
reflect
.
DeepEqual
(
enabledValues
,
[]
bool
{
true
,
false
,
true
})
{
t
.
Fatalf
(
"unexpected enabled values after wildcard keep_origin set: %v"
,
enabledValues
)
}
}
func
TestApplyParamOverrideTrimSpaceMultiWildcardPath
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"tools":[{"custom":{"items":[{"name":" alpha "},{"name":" beta "}]}},{"custom":{"items":[{"name":" gamma"}]}}]}`
)
override
:=
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"path"
:
"tools.*.custom.items.*.name"
,
"mode"
:
"trim_space"
,
},
},
}
out
,
err
:=
ApplyParamOverride
(
input
,
override
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"ApplyParamOverride returned error: %v"
,
err
)
}
var
got
struct
{
Tools
[]
struct
{
Custom
struct
{
Items
[]
struct
{
Name
string
`json:"name"`
}
`json:"items"`
}
`json:"custom"`
}
`json:"tools"`
}
if
err
:=
json
.
Unmarshal
(
out
,
&
got
);
err
!=
nil
{
t
.
Fatalf
(
"failed to unmarshal output JSON: %v"
,
err
)
}
names
:=
lo
.
FlatMap
(
got
.
Tools
,
func
(
tool
struct
{
Custom
struct
{
Items
[]
struct
{
Name
string
`json:"name"`
}
`json:"items"`
}
`json:"custom"`
},
_
int
)
[]
string
{
return
lo
.
Map
(
tool
.
Custom
.
Items
,
func
(
item
struct
{
Name
string
`json:"name"`
},
_
int
)
string
{
return
item
.
Name
})
})
if
!
reflect
.
DeepEqual
(
names
,
[]
string
{
"alpha"
,
"beta"
,
"gamma"
})
{
t
.
Fatalf
(
"unexpected names after multi wildcard trim_space: %v"
,
names
)
}
}
func
TestApplyParamOverrideSet
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"model":"gpt-4","temperature":0.7}`
)
override
:=
map
[
string
]
interface
{}{
...
...
@@ -261,6 +481,42 @@ func TestApplyParamOverrideSet(t *testing.T) {
assertJSONEqual
(
t
,
`{"model":"gpt-4","temperature":0.1}`
,
string
(
out
))
}
func
TestApplyParamOverrideSetWithDescriptionKeepsCompatibility
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"model":"gpt-4","temperature":0.7}`
)
overrideWithoutDesc
:=
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"path"
:
"temperature"
,
"mode"
:
"set"
,
"value"
:
0.1
,
},
},
}
overrideWithDesc
:=
map
[
string
]
interface
{}{
"operations"
:
[]
interface
{}{
map
[
string
]
interface
{}{
"description"
:
"set temperature for deterministic output"
,
"path"
:
"temperature"
,
"mode"
:
"set"
,
"value"
:
0.1
,
},
},
}
outWithoutDesc
,
err
:=
ApplyParamOverride
(
input
,
overrideWithoutDesc
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"ApplyParamOverride without description returned error: %v"
,
err
)
}
outWithDesc
,
err
:=
ApplyParamOverride
(
input
,
overrideWithDesc
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"ApplyParamOverride with description returned error: %v"
,
err
)
}
assertJSONEqual
(
t
,
string
(
outWithoutDesc
),
string
(
outWithDesc
))
assertJSONEqual
(
t
,
`{"model":"gpt-4","temperature":0.1}`
,
string
(
outWithDesc
))
}
func
TestApplyParamOverrideSetKeepOrigin
(
t
*
testing
.
T
)
{
input
:=
[]
byte
(
`{"model":"gpt-4","temperature":0.7}`
)
override
:=
map
[
string
]
interface
{}{
...
...
web/src/components/table/channels/modals/ParamOverrideEditorModal.jsx
View file @
d2332685
...
...
@@ -276,6 +276,7 @@ const LEGACY_TEMPLATE = {
const
OPERATION_TEMPLATE
=
{
operations
:
[
{
description
:
'Set default temperature for openai/* models.'
,
path
:
'temperature'
,
mode
:
'set'
,
value
:
0.7
,
...
...
@@ -294,8 +295,9 @@ const OPERATION_TEMPLATE = {
const
HEADER_PASSTHROUGH_TEMPLATE
=
{
operations
:
[
{
description
:
'Pass through X-Request-Id header to upstream.'
,
mode
:
'pass_headers'
,
value
:
[
'
Authorization
'
],
value
:
[
'
X-Request-Id
'
],
keep_origin
:
true
,
},
],
...
...
@@ -304,6 +306,8 @@ const HEADER_PASSTHROUGH_TEMPLATE = {
const
GEMINI_IMAGE_4K_TEMPLATE
=
{
operations
:
[
{
description
:
'Set imageSize to 4K when model contains gemini/image and ends with 4k.'
,
mode
:
'set'
,
path
:
'generationConfig.imageConfig.imageSize'
,
value
:
'4K'
,
...
...
@@ -311,7 +315,17 @@ const GEMINI_IMAGE_4K_TEMPLATE = {
{
path
:
'original_model'
,
mode
:
'contains'
,
value
:
'gemini-3-pro-image-preview'
,
value
:
'gemini'
,
},
{
path
:
'original_model'
,
mode
:
'contains'
,
value
:
'image'
,
},
{
path
:
'original_model'
,
mode
:
'suffix'
,
value
:
'4k'
,
},
],
logic
:
'AND'
,
...
...
@@ -319,11 +333,13 @@ const GEMINI_IMAGE_4K_TEMPLATE = {
],
};
const
AWS_BEDROCK_ANTHROPIC_
BETA_OVERRIDE
_TEMPLATE
=
{
const
AWS_BEDROCK_ANTHROPIC_
COMPAT
_TEMPLATE
=
{
operations
:
[
{
description
:
'Normalize anthropic-beta header tokens for Bedrock compatibility.'
,
mode
:
'set_header'
,
path
:
'anthropic-beta'
,
// https://github.com/BerriAI/litellm/blob/main/litellm/anthropic_beta_headers_config.json
value
:
{
'advanced-tool-use-2025-11-20'
:
'tool-search-tool-2025-10-19'
,
bash_20241022
:
null
,
...
...
@@ -355,6 +371,11 @@ const AWS_BEDROCK_ANTHROPIC_BETA_OVERRIDE_TEMPLATE = {
'web-search-2025-03-05'
:
null
,
},
},
{
description
:
'Remove all tools[*].custom.input_examples before upstream relay.'
,
mode
:
'delete'
,
path
:
'tools.*.custom.input_examples'
,
},
],
};
...
...
@@ -378,7 +399,7 @@ const TEMPLATE_PRESET_CONFIG = {
},
pass_headers_auth
:
{
group
:
'scenario'
,
label
:
'请求头透传(
Authorization
)'
,
label
:
'请求头透传(
X-Request-Id
)'
,
kind
:
'operations'
,
payload
:
HEADER_PASSTHROUGH_TEMPLATE
,
},
...
...
@@ -402,9 +423,9 @@ const TEMPLATE_PRESET_CONFIG = {
},
aws_bedrock_anthropic_beta_override
:
{
group
:
'scenario'
,
label
:
'AWS Bedrock
anthropic-beta覆盖
'
,
label
:
'AWS Bedrock
Claude 兼容模板
'
,
kind
:
'operations'
,
payload
:
AWS_BEDROCK_ANTHROPIC_
BETA_OVERRIDE
_TEMPLATE
,
payload
:
AWS_BEDROCK_ANTHROPIC_
COMPAT
_TEMPLATE
,
},
};
...
...
@@ -764,6 +785,7 @@ const createDefaultCondition = () => normalizeCondition({});
const
normalizeOperation
=
(
operation
=
{})
=>
({
id
:
nextLocalId
(),
description
:
typeof
operation
.
description
===
'string'
?
operation
.
description
:
''
,
path
:
typeof
operation
.
path
===
'string'
?
operation
.
path
:
''
,
mode
:
OPERATION_MODE_VALUES
.
has
(
operation
.
mode
)
?
operation
.
mode
:
'set'
,
value_text
:
toValueText
(
operation
.
value
),
...
...
@@ -1086,6 +1108,7 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
if
(
!
keyword
)
return
operations
;
return
operations
.
filter
((
operation
)
=>
{
const
searchableText
=
[
operation
.
description
,
operation
.
mode
,
operation
.
path
,
operation
.
from
,
...
...
@@ -1151,10 +1174,14 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
const
payloadOps
=
filteredOps
.
map
((
operation
)
=>
{
const
mode
=
operation
.
mode
||
'set'
;
const
meta
=
MODE_META
[
mode
]
||
MODE_META
.
set
;
const
descriptionValue
=
String
(
operation
.
description
||
''
).
trim
();
const
pathValue
=
operation
.
path
.
trim
();
const
fromValue
=
operation
.
from
.
trim
();
const
toValue
=
operation
.
to
.
trim
();
const
payload
=
{
mode
};
if
(
descriptionValue
)
{
payload
.
description
=
descriptionValue
;
}
if
(
meta
.
path
)
{
payload
.
path
=
pathValue
;
}
...
...
@@ -1563,6 +1590,7 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
if
(
index
<
0
)
return
prev
;
const
source
=
prev
[
index
];
const
cloned
=
normalizeOperation
({
description
:
source
.
description
,
path
:
source
.
path
,
mode
:
source
.
mode
,
value
:
parseLooseValue
(
source
.
value_text
),
...
...
@@ -1891,7 +1919,7 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
<
Input
value=
{
operationSearch
}
placeholder=
{
t
(
'搜索规则(类型 / 路径 / 来源 / 目标)'
)
}
placeholder=
{
t
(
'搜索规则(
描述 /
类型 / 路径 / 来源 / 目标)'
)
}
onChange=
{
(
nextValue
)
=>
setOperationSearch
(
nextValue
||
''
)
}
...
...
@@ -1958,6 +1986,23 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
>
{
getOperationSummary
(
operation
,
index
)
}
</
Text
>
{
String
(
operation
.
description
||
''
).
trim
()
?
(
<
Text
type=
'tertiary'
size=
'small'
className=
'block mt-1'
style=
{
{
lineHeight
:
1.5
,
wordBreak
:
'break-word'
,
overflow
:
'hidden'
,
display
:
'-webkit-box'
,
WebkitLineClamp
:
2
,
WebkitBoxOrient
:
'vertical'
,
}
}
>
{
operation
.
description
}
</
Text
>
)
:
null
}
</
div
>
<
Tag
size=
'small'
color=
'grey'
>
{
(
operation
.
conditions
||
[]).
length
}
...
...
@@ -2035,6 +2080,7 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
type=
'danger'
theme=
'borderless'
icon=
{
<
IconDelete
/>
}
aria
-
label=
{
t
(
'删除规则'
)
}
onClick=
{
()
=>
removeOperation
(
selectedOperation
.
id
)
}
...
...
@@ -2085,6 +2131,25 @@ const ParamOverrideEditorModal = ({ visible, value, onSave, onCancel }) => {
>
{
MODE_DESCRIPTIONS
[
mode
]
||
''
}
</
Text
>
<
div
className=
'mt-2'
>
<
Text
type=
'tertiary'
size=
'small'
>
{
t
(
'规则描述(可选)'
)
}
</
Text
>
<
Input
value=
{
selectedOperation
.
description
||
''
}
placeholder=
{
t
(
'例如:清理工具参数,避免上游校验错误'
)
}
onChange=
{
(
nextValue
)
=>
updateOperation
(
selectedOperation
.
id
,
{
description
:
nextValue
||
''
,
})
}
maxLength=
{
180
}
showClear
/>
<
Text
type=
'tertiary'
size=
'small'
className=
'mt-1 block'
>
{
`${String(selectedOperation.description || '').length}/180`
}
</
Text
>
</
div
>
{
meta
.
value
?
(
mode
===
'return_error'
&&
returnErrorDraft
?
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment