feat(channels): 添加默认参数功能以增强模型配置灵活性
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
1 parent
fa5fed56d9
commit
315e995656
9 files changed
+469
-179
No files matched your search
@@ -157,6 +157,13 @@
|
|||||||
"modelConfigDesc": "External calls use the model name; the system maps it to the channel model ID.",
|
"modelConfigDesc": "External calls use the model name; the system maps it to the channel model ID.",
|
||||||
"modelId": "Model ID",
|
"modelId": "Model ID",
|
||||||
"modelName": "Model Name",
|
"modelName": "Model Name",
|
||||||
|
"defaultParams": "Params",
|
||||||
|
"defaultParamsEmpty": "Set Params",
|
||||||
|
"defaultParamsConfigured": "{{count}} set",
|
||||||
|
"defaultParamsDialogTitle": "Edit Default Parameters",
|
||||||
|
"defaultParamsDialogDesc": "Enter a JSON object. Parameters explicitly sent by callers override matching defaults here.",
|
||||||
|
"defaultParamsPlaceholder": "{\n \"temperature\": 0.7,\n \"top_p\": 0.9,\n \"reasoning\": { \"effort\": \"medium\" }\n}",
|
||||||
|
"formatJson": "Format",
|
||||||
"modelStatusDelete": "Status / Delete",
|
"modelStatusDelete": "Status / Delete",
|
||||||
"modelIdPlaceholder": "Model ID, e.g. gpt-4.1-mini",
|
"modelIdPlaceholder": "Model ID, e.g. gpt-4.1-mini",
|
||||||
"modelNamePlaceholder": "Model name, defaults to model ID",
|
"modelNamePlaceholder": "Model name, defaults to model ID",
|
||||||
@@ -173,7 +180,7 @@
|
|||||||
"noMatchingModels": "No matching models",
|
"noMatchingModels": "No matching models",
|
||||||
"saveChannel": "Save Channel",
|
"saveChannel": "Save Channel",
|
||||||
"jsonConfig": "JSON Config",
|
"jsonConfig": "JSON Config",
|
||||||
"modelJsonStructure": "JSON array with `id`, `name`, `enabled` fields per item.",
|
"modelJsonStructure": "JSON array with `id`, `name`, `enabled`, and optional `default_params` object per item.",
|
||||||
"fillRequired": "Please fill in all required fields",
|
"fillRequired": "Please fill in all required fields",
|
||||||
"fillChannelKey": "Please enter a channel key",
|
"fillChannelKey": "Please enter a channel key",
|
||||||
"fillTypeEndpointKey": "Please fill in channel type, API endpoint, and API key first",
|
"fillTypeEndpointKey": "Please fill in channel type, API endpoint, and API key first",
|
||||||
@@ -186,7 +193,9 @@
|
|||||||
"modelJsonInvalid": "Invalid model JSON format",
|
"modelJsonInvalid": "Invalid model JSON format",
|
||||||
"modelJsonMustBeArray": "Model JSON must be an array",
|
"modelJsonMustBeArray": "Model JSON must be an array",
|
||||||
"modelJsonItemMustBeObject": "Model JSON items must be objects",
|
"modelJsonItemMustBeObject": "Model JSON items must be objects",
|
||||||
"modelJsonIdEmpty": "Model ID in JSON cannot be empty"
|
"modelJsonIdEmpty": "Model ID in JSON cannot be empty",
|
||||||
|
"defaultParamsJsonInvalid": "Invalid default parameters JSON",
|
||||||
|
"defaultParamsJsonMustBeObject": "Default parameters must be a JSON object"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tokens": {
|
"tokens": {
|
||||||
|
|||||||
@@ -157,6 +157,13 @@
|
|||||||
"modelConfigDesc": "外部使用模型名称调用,程序会自动映射到渠道模型 ID。",
|
"modelConfigDesc": "外部使用模型名称调用,程序会自动映射到渠道模型 ID。",
|
||||||
"modelId": "模型 ID",
|
"modelId": "模型 ID",
|
||||||
"modelName": "模型名称",
|
"modelName": "模型名称",
|
||||||
|
"defaultParams": "参数",
|
||||||
|
"defaultParamsEmpty": "设置参数",
|
||||||
|
"defaultParamsConfigured": "已设置 {{count}} 项",
|
||||||
|
"defaultParamsDialogTitle": "编辑默认参数",
|
||||||
|
"defaultParamsDialogDesc": "输入 JSON 对象;调用接口时显式传入的同名参数会覆盖这里的默认值。",
|
||||||
|
"defaultParamsPlaceholder": "{\n \"temperature\": 0.7,\n \"top_p\": 0.9,\n \"reasoning\": { \"effort\": \"medium\" }\n}",
|
||||||
|
"formatJson": "格式化",
|
||||||
"modelStatusDelete": "状态 / 删除",
|
"modelStatusDelete": "状态 / 删除",
|
||||||
"modelIdPlaceholder": "模型ID,例如:gpt-4.1-mini",
|
"modelIdPlaceholder": "模型ID,例如:gpt-4.1-mini",
|
||||||
"modelNamePlaceholder": "模型名称,默认等于模型ID",
|
"modelNamePlaceholder": "模型名称,默认等于模型ID",
|
||||||
@@ -173,7 +180,7 @@
|
|||||||
"noMatchingModels": "没有匹配的模型",
|
"noMatchingModels": "没有匹配的模型",
|
||||||
"saveChannel": "保存渠道",
|
"saveChannel": "保存渠道",
|
||||||
"jsonConfig": "JSON 配置",
|
"jsonConfig": "JSON 配置",
|
||||||
"modelJsonStructure": "JSON 结构为数组,每项包含 `id`、`name`、`enabled`。",
|
"modelJsonStructure": "JSON 结构为数组,每项包含 `id`、`name`、`enabled`,可选 `default_params` 对象。",
|
||||||
"fillRequired": "请填写所有必填字段",
|
"fillRequired": "请填写所有必填字段",
|
||||||
"fillChannelKey": "请填写渠道标识",
|
"fillChannelKey": "请填写渠道标识",
|
||||||
"fillTypeEndpointKey": "请先填写渠道类型、API 端点和 API 密钥",
|
"fillTypeEndpointKey": "请先填写渠道类型、API 端点和 API 密钥",
|
||||||
@@ -186,7 +193,9 @@
|
|||||||
"modelJsonInvalid": "模型 JSON 格式错误",
|
"modelJsonInvalid": "模型 JSON 格式错误",
|
||||||
"modelJsonMustBeArray": "模型 JSON 必须是数组",
|
"modelJsonMustBeArray": "模型 JSON 必须是数组",
|
||||||
"modelJsonItemMustBeObject": "模型 JSON 项必须是对象",
|
"modelJsonItemMustBeObject": "模型 JSON 项必须是对象",
|
||||||
"modelJsonIdEmpty": "模型 JSON 中的 id 不能为空"
|
"modelJsonIdEmpty": "模型 JSON 中的 id 不能为空",
|
||||||
|
"defaultParamsJsonInvalid": "默认参数 JSON 格式错误",
|
||||||
|
"defaultParamsJsonMustBeObject": "默认参数必须是 JSON 对象"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tokens": {
|
"tokens": {
|
||||||
|
|||||||
@@ -157,6 +157,13 @@
|
|||||||
"modelConfigDesc": "外部使用模型名稱呼叫,程式會自動對應到渠道模型 ID。",
|
"modelConfigDesc": "外部使用模型名稱呼叫,程式會自動對應到渠道模型 ID。",
|
||||||
"modelId": "模型 ID",
|
"modelId": "模型 ID",
|
||||||
"modelName": "模型名稱",
|
"modelName": "模型名稱",
|
||||||
|
"defaultParams": "參數",
|
||||||
|
"defaultParamsEmpty": "設定參數",
|
||||||
|
"defaultParamsConfigured": "已設定 {{count}} 項",
|
||||||
|
"defaultParamsDialogTitle": "編輯預設參數",
|
||||||
|
"defaultParamsDialogDesc": "輸入 JSON 物件;呼叫介面時明確傳入的同名參數會覆蓋這裡的預設值。",
|
||||||
|
"defaultParamsPlaceholder": "{\n \"temperature\": 0.7,\n \"top_p\": 0.9,\n \"reasoning\": { \"effort\": \"medium\" }\n}",
|
||||||
|
"formatJson": "格式化",
|
||||||
"modelStatusDelete": "狀態 / 刪除",
|
"modelStatusDelete": "狀態 / 刪除",
|
||||||
"modelIdPlaceholder": "模型ID,例如:gpt-4.1-mini",
|
"modelIdPlaceholder": "模型ID,例如:gpt-4.1-mini",
|
||||||
"modelNamePlaceholder": "模型名稱,預設等於模型ID",
|
"modelNamePlaceholder": "模型名稱,預設等於模型ID",
|
||||||
@@ -173,7 +180,7 @@
|
|||||||
"noMatchingModels": "沒有匹配的模型",
|
"noMatchingModels": "沒有匹配的模型",
|
||||||
"saveChannel": "儲存渠道",
|
"saveChannel": "儲存渠道",
|
||||||
"jsonConfig": "JSON 設定",
|
"jsonConfig": "JSON 設定",
|
||||||
"modelJsonStructure": "JSON 結構為陣列,每項包含 `id`、`name`、`enabled`。",
|
"modelJsonStructure": "JSON 結構為陣列,每項包含 `id`、`name`、`enabled`,可選 `default_params` 物件。",
|
||||||
"fillRequired": "請填寫所有必填欄位",
|
"fillRequired": "請填寫所有必填欄位",
|
||||||
"fillChannelKey": "請填寫渠道標識",
|
"fillChannelKey": "請填寫渠道標識",
|
||||||
"fillTypeEndpointKey": "請先填寫渠道類型、API 端點和 API 金鑰",
|
"fillTypeEndpointKey": "請先填寫渠道類型、API 端點和 API 金鑰",
|
||||||
@@ -186,7 +193,9 @@
|
|||||||
"modelJsonInvalid": "模型 JSON 格式錯誤",
|
"modelJsonInvalid": "模型 JSON 格式錯誤",
|
||||||
"modelJsonMustBeArray": "模型 JSON 必須是陣列",
|
"modelJsonMustBeArray": "模型 JSON 必須是陣列",
|
||||||
"modelJsonItemMustBeObject": "模型 JSON 項必須是物件",
|
"modelJsonItemMustBeObject": "模型 JSON 項必須是物件",
|
||||||
"modelJsonIdEmpty": "模型 JSON 中的 id 不能為空"
|
"modelJsonIdEmpty": "模型 JSON 中的 id 不能為空",
|
||||||
|
"defaultParamsJsonInvalid": "預設參數 JSON 格式錯誤",
|
||||||
|
"defaultParamsJsonMustBeObject": "預設參數必須是 JSON 物件"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tokens": {
|
"tokens": {
|
||||||
|
|||||||
+386
-169
File diff suppressed because it is too large.
Load diff
@@ -8,6 +8,7 @@ export interface ChannelModelMapping {
|
|||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
|
default_params?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChannelConfig {
|
export interface ChannelConfig {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const ChannelModelSchema = z.object({
|
|||||||
id: z.string().describe('Upstream model ID'),
|
id: z.string().describe('Upstream model ID'),
|
||||||
name: z.string().describe('External model name exposed by this proxy'),
|
name: z.string().describe('External model name exposed by this proxy'),
|
||||||
enabled: z.boolean().optional().describe('Whether this model is available for routing'),
|
enabled: z.boolean().optional().describe('Whether this model is available for routing'),
|
||||||
|
default_params: z.record(z.string(), z.unknown()).optional().describe('Default request parameters applied when callers do not provide them'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const parseFetchedModels = (payload: any): ChannelModelMapping[] => {
|
const parseFetchedModels = (payload: any): ChannelModelMapping[] => {
|
||||||
|
|||||||
+26
-3
@@ -52,11 +52,28 @@ const normalizeApiKeys = (config: Partial<ChannelConfig>): string[] => {
|
|||||||
return normalizedKeys;
|
return normalizedKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeDefaultParams = (defaultParams: unknown): Record<string, unknown> | undefined => {
|
||||||
|
if (!defaultParams || typeof defaultParams !== "object" || Array.isArray(defaultParams)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(defaultParams).length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultParams as Record<string, unknown>;
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeLegacyModels = (config: Partial<ChannelConfig>): ChannelModelMapping[] => {
|
const normalizeLegacyModels = (config: Partial<ChannelConfig>): ChannelModelMapping[] => {
|
||||||
const normalizedModels: ChannelModelMapping[] = [];
|
const normalizedModels: ChannelModelMapping[] = [];
|
||||||
const seenNames = new Set<string>();
|
const seenNames = new Set<string>();
|
||||||
|
|
||||||
const pushModel = (modelId: string, modelName?: string, enabled = DEFAULT_CHANNEL_MODEL_ENABLED) => {
|
const pushModel = (
|
||||||
|
modelId: string,
|
||||||
|
modelName?: string,
|
||||||
|
enabled = DEFAULT_CHANNEL_MODEL_ENABLED,
|
||||||
|
defaultParams?: unknown
|
||||||
|
) => {
|
||||||
const id = modelId.trim();
|
const id = modelId.trim();
|
||||||
const name = (modelName || modelId).trim();
|
const name = (modelName || modelId).trim();
|
||||||
|
|
||||||
@@ -64,8 +81,14 @@ const normalizeLegacyModels = (config: Partial<ChannelConfig>): ChannelModelMapp
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizedDefaultParams = normalizeDefaultParams(defaultParams);
|
||||||
|
const normalizedModel: ChannelModelMapping = { id, name, enabled };
|
||||||
|
if (normalizedDefaultParams) {
|
||||||
|
normalizedModel.default_params = normalizedDefaultParams;
|
||||||
|
}
|
||||||
|
|
||||||
seenNames.add(name);
|
seenNames.add(name);
|
||||||
normalizedModels.push({ id, name, enabled });
|
normalizedModels.push(normalizedModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Array.isArray(config.models)) {
|
if (Array.isArray(config.models)) {
|
||||||
@@ -77,7 +100,7 @@ const normalizeLegacyModels = (config: Partial<ChannelConfig>): ChannelModelMapp
|
|||||||
const id = typeof model.id === "string" ? model.id : "";
|
const id = typeof model.id === "string" ? model.id : "";
|
||||||
const name = typeof model.name === "string" ? model.name : id;
|
const name = typeof model.name === "string" ? model.name : id;
|
||||||
const enabled = model.enabled !== false;
|
const enabled = model.enabled !== false;
|
||||||
pushModel(id, name, enabled);
|
pushModel(id, name, enabled, model.default_params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,13 +50,33 @@ const cloneRequestBody = <T>(requestBody: T): T => {
|
|||||||
return JSON.parse(JSON.stringify(requestBody)) as T;
|
return JSON.parse(JSON.stringify(requestBody)) as T;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getModelDefaultParams = (
|
||||||
|
mapping: ChannelModelMapping
|
||||||
|
): Record<string, unknown> | null => {
|
||||||
|
const defaultParams = mapping.default_params;
|
||||||
|
if (!defaultParams || typeof defaultParams !== "object" || Array.isArray(defaultParams)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultParams;
|
||||||
|
};
|
||||||
|
|
||||||
const buildChannelRequestBody = (
|
const buildChannelRequestBody = (
|
||||||
requestBody: any,
|
requestBody: any,
|
||||||
channel: ResolvedChannelCandidate
|
channel: ResolvedChannelCandidate
|
||||||
) => {
|
) => {
|
||||||
const runtimeRequestBody = cloneRequestBody(requestBody);
|
const runtimeRequestBody = cloneRequestBody(requestBody);
|
||||||
if (runtimeRequestBody && typeof runtimeRequestBody === "object") {
|
if (runtimeRequestBody && typeof runtimeRequestBody === "object") {
|
||||||
runtimeRequestBody.model = channel.mapping.id;
|
const defaultParams = getModelDefaultParams(channel.mapping);
|
||||||
|
const mergedRequestBody = defaultParams
|
||||||
|
? {
|
||||||
|
...cloneRequestBody(defaultParams),
|
||||||
|
...runtimeRequestBody,
|
||||||
|
}
|
||||||
|
: runtimeRequestBody;
|
||||||
|
|
||||||
|
mergedRequestBody.model = channel.mapping.id;
|
||||||
|
return mergedRequestBody;
|
||||||
}
|
}
|
||||||
return runtimeRequestBody;
|
return runtimeRequestBody;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in new issue
Block a user