Merge pull request #2627 from seefs001/feature/channel-test-param-override
Some checks failed
Publish Docker image (Multi Registries, native amd64+arm64) / Build & push (amd64) [native] (push) Has been cancelled
Publish Docker image (Multi Registries, native amd64+arm64) / Build & push (arm64) [native] (push) Has been cancelled
Publish Docker image (Multi Registries, native amd64+arm64) / Create multi-arch manifests (Docker Hub) (push) Has been cancelled
Some checks failed
Publish Docker image (Multi Registries, native amd64+arm64) / Build & push (amd64) [native] (push) Has been cancelled
Publish Docker image (Multi Registries, native amd64+arm64) / Build & push (arm64) [native] (push) Has been cancelled
Publish Docker image (Multi Registries, native amd64+arm64) / Create multi-arch manifests (Docker Hub) (push) Has been cancelled
feat: channel testing supports parameter overriding
This commit is contained in:
parent
ef572392b7
commit
eae53ac398
@ -193,6 +193,7 @@ func testChannel(channel *model.Channel, testModel string, endpointType string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.IsChannelTest = true
|
||||||
info.InitChannelMeta(c)
|
info.InitChannelMeta(c)
|
||||||
|
|
||||||
err = helper.ModelMappedHelper(c, info, request)
|
err = helper.ModelMappedHelper(c, info, request)
|
||||||
@ -309,6 +310,27 @@ func testChannel(channel *model.Channel, testModel string, endpointType string)
|
|||||||
newAPIError: types.NewError(err, types.ErrorCodeJsonMarshalFailed),
|
newAPIError: types.NewError(err, types.ErrorCodeJsonMarshalFailed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//jsonData, err = relaycommon.RemoveDisabledFields(jsonData, info.ChannelOtherSettings)
|
||||||
|
//if err != nil {
|
||||||
|
// return testResult{
|
||||||
|
// context: c,
|
||||||
|
// localErr: err,
|
||||||
|
// newAPIError: types.NewError(err, types.ErrorCodeConvertRequestFailed),
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
if len(info.ParamOverride) > 0 {
|
||||||
|
jsonData, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, relaycommon.BuildParamOverrideContext(info))
|
||||||
|
if err != nil {
|
||||||
|
return testResult{
|
||||||
|
context: c,
|
||||||
|
localErr: err,
|
||||||
|
newAPIError: types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
requestBody := bytes.NewBuffer(jsonData)
|
requestBody := bytes.NewBuffer(jsonData)
|
||||||
c.Request.Body = io.NopCloser(requestBody)
|
c.Request.Body = io.NopCloser(requestBody)
|
||||||
resp, err := adaptor.DoRequest(c, info, requestBody)
|
resp, err := adaptor.DoRequest(c, info, requestBody)
|
||||||
|
|||||||
@ -570,18 +570,19 @@ func mergeObjects(jsonStr, path string, value interface{}, keepOrigin bool) (str
|
|||||||
|
|
||||||
// BuildParamOverrideContext 提供 ApplyParamOverride 可用的上下文信息。
|
// BuildParamOverrideContext 提供 ApplyParamOverride 可用的上下文信息。
|
||||||
// 目前内置以下字段:
|
// 目前内置以下字段:
|
||||||
// - model:优先使用上游模型名(UpstreamModelName),若不存在则回落到原始模型名(OriginModelName)。
|
// - upstream_model/model:始终为通道映射后的上游模型名。
|
||||||
// - upstream_model:始终为通道映射后的上游模型名。
|
|
||||||
// - original_model:请求最初指定的模型名。
|
// - original_model:请求最初指定的模型名。
|
||||||
|
// - request_path:请求路径
|
||||||
|
// - is_channel_test:是否为渠道测试请求(同 is_test)。
|
||||||
func BuildParamOverrideContext(info *RelayInfo) map[string]interface{} {
|
func BuildParamOverrideContext(info *RelayInfo) map[string]interface{} {
|
||||||
if info == nil || info.ChannelMeta == nil {
|
if info == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := make(map[string]interface{})
|
ctx := make(map[string]interface{})
|
||||||
if info.UpstreamModelName != "" {
|
if info.ChannelMeta != nil && info.ChannelMeta.UpstreamModelName != "" {
|
||||||
ctx["model"] = info.UpstreamModelName
|
ctx["model"] = info.ChannelMeta.UpstreamModelName
|
||||||
ctx["upstream_model"] = info.UpstreamModelName
|
ctx["upstream_model"] = info.ChannelMeta.UpstreamModelName
|
||||||
}
|
}
|
||||||
if info.OriginModelName != "" {
|
if info.OriginModelName != "" {
|
||||||
ctx["original_model"] = info.OriginModelName
|
ctx["original_model"] = info.OriginModelName
|
||||||
@ -590,8 +591,13 @@ func BuildParamOverrideContext(info *RelayInfo) map[string]interface{} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ctx) == 0 {
|
if info.RequestURLPath != "" {
|
||||||
return nil
|
requestPath := info.RequestURLPath
|
||||||
|
if requestPath != "" {
|
||||||
|
ctx["request_path"] = requestPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx["is_channel_test"] = info.IsChannelTest
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,6 +115,7 @@ type RelayInfo struct {
|
|||||||
SendResponseCount int
|
SendResponseCount int
|
||||||
FinalPreConsumedQuota int // 最终预消耗的配额
|
FinalPreConsumedQuota int // 最终预消耗的配额
|
||||||
IsClaudeBetaQuery bool // /v1/messages?beta=true
|
IsClaudeBetaQuery bool // /v1/messages?beta=true
|
||||||
|
IsChannelTest bool // channel test request
|
||||||
|
|
||||||
PriceData types.PriceData
|
PriceData types.PriceData
|
||||||
|
|
||||||
|
|||||||
@ -199,17 +199,11 @@ const EditChannelModal = (props) => {
|
|||||||
if (!trimmed) return [];
|
if (!trimmed) return [];
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(trimmed);
|
const parsed = JSON.parse(trimmed);
|
||||||
if (
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
||||||
!parsed ||
|
|
||||||
typeof parsed !== 'object' ||
|
|
||||||
Array.isArray(parsed)
|
|
||||||
) {
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const values = Object.values(parsed)
|
const values = Object.values(parsed)
|
||||||
.map((value) =>
|
.map((value) => (typeof value === 'string' ? value.trim() : undefined))
|
||||||
typeof value === 'string' ? value.trim() : undefined,
|
|
||||||
)
|
|
||||||
.filter((value) => value);
|
.filter((value) => value);
|
||||||
return Array.from(new Set(values));
|
return Array.from(new Set(values));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -509,6 +503,18 @@ const EditChannelModal = (props) => {
|
|||||||
//setAutoBan
|
//setAutoBan
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatJsonField = (fieldName) => {
|
||||||
|
const rawValue = (inputs?.[fieldName] ?? '').trim();
|
||||||
|
if (!rawValue) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(rawValue);
|
||||||
|
handleInputChange(fieldName, JSON.stringify(parsed, null, 2));
|
||||||
|
} catch (error) {
|
||||||
|
showError(`${t('JSON格式错误')}: ${error.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const loadChannel = async () => {
|
const loadChannel = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let res = await API.get(`/api/channel/${channelId}`);
|
let res = await API.get(`/api/channel/${channelId}`);
|
||||||
@ -2812,6 +2818,12 @@ const EditChannelModal = (props) => {
|
|||||||
>
|
>
|
||||||
{t('新格式模板')}
|
{t('新格式模板')}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text
|
||||||
|
className='!text-semi-color-primary cursor-pointer'
|
||||||
|
onClick={() => formatJsonField('param_override')}
|
||||||
|
>
|
||||||
|
{t('格式化')}
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
showClear
|
showClear
|
||||||
@ -2852,6 +2864,12 @@ const EditChannelModal = (props) => {
|
|||||||
>
|
>
|
||||||
{t('填入模板')}
|
{t('填入模板')}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text
|
||||||
|
className='!text-semi-color-primary cursor-pointer'
|
||||||
|
onClick={() => formatJsonField('header_override')}
|
||||||
|
>
|
||||||
|
{t('格式化')}
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Text type='tertiary' size='small'>
|
<Text type='tertiary' size='small'>
|
||||||
@ -3181,7 +3199,9 @@ const EditChannelModal = (props) => {
|
|||||||
? inputs.models.map(String)
|
? inputs.models.map(String)
|
||||||
: [];
|
: [];
|
||||||
const incoming = modelIds.map(String);
|
const incoming = modelIds.map(String);
|
||||||
const nextModels = Array.from(new Set([...existingModels, ...incoming]));
|
const nextModels = Array.from(
|
||||||
|
new Set([...existingModels, ...incoming]),
|
||||||
|
);
|
||||||
|
|
||||||
handleInputChange('models', nextModels);
|
handleInputChange('models', nextModels);
|
||||||
if (formApiRef.current) {
|
if (formApiRef.current) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user