feat(channel): 添加权重属性以优化通道选择
在 ChannelConfig 中添加了可选的 weight 属性,用于 优化通道的选择逻辑。更新了相关 API 端点以支持 权重的验证和处理。通道选择逻辑现在会优先选择 权重最高的通道,提升请求路由的灵活性和效率。
This commit is contained in:
1 parent
1243bf629d
commit
45d717a028
7 files changed
+598
-516
No files matched your search
+565
-511
File diff suppressed because it is too large.
Load diff
@@ -14,6 +14,7 @@ export interface ChannelConfig {
|
||||
type: 'openai' | 'gemini' | 'azure-openai' | 'openai-audio' | 'azure-openai-audio' | 'claude' | 'claude-to-openai' | 'openai-responses' | 'azure-openai-responses'
|
||||
endpoint: string
|
||||
enabled?: boolean
|
||||
weight?: number
|
||||
api_key?: string
|
||||
api_keys?: string[]
|
||||
auto_retry?: boolean
|
||||
|
||||
@@ -155,7 +155,11 @@ export class ChannelGetEndpoint extends OpenAPIRoute {
|
||||
|
||||
async handle(c: Context<HonoCustomType>) {
|
||||
const result = await c.env.DB.prepare(
|
||||
"SELECT * FROM channel_config"
|
||||
`SELECT * FROM channel_config
|
||||
ORDER BY
|
||||
COALESCE(CAST(json_extract(value, '$.weight') AS INTEGER), 0) DESC,
|
||||
datetime(created_at) DESC,
|
||||
key ASC`
|
||||
).all<ChannelConfigRow>();
|
||||
|
||||
return {
|
||||
@@ -182,6 +186,7 @@ export class ChannelUpsertEndpoint extends OpenAPIRoute {
|
||||
type: z.string().describe('Channel type'),
|
||||
endpoint: z.string().describe('API endpoint'),
|
||||
enabled: z.boolean().optional().describe('Whether this channel participates in request routing'),
|
||||
weight: z.number().int().min(0).max(5).optional().describe('Priority weight for channel routing'),
|
||||
api_key: z.string().optional().describe('Deprecated single API key'),
|
||||
api_keys: z.array(z.string()).optional().describe('API keys, one request will pick one randomly'),
|
||||
auto_retry: z.boolean().optional().describe('Automatically retry the same API key'),
|
||||
@@ -254,6 +259,7 @@ export class ChannelFetchModelsEndpoint extends OpenAPIRoute {
|
||||
type: z.string().optional(),
|
||||
endpoint: z.string().optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
weight: z.number().int().min(0).max(5).optional(),
|
||||
api_key: z.string().optional(),
|
||||
api_keys: z.array(z.string()).optional(),
|
||||
auto_retry: z.boolean().optional(),
|
||||
|
||||
+15
-1
@@ -1,12 +1,15 @@
|
||||
export const DEFAULT_CHANNEL_AUTO_RETRY = true;
|
||||
export const DEFAULT_CHANNEL_AUTO_ROTATE = true;
|
||||
export const DEFAULT_CHANNEL_ENABLED = true;
|
||||
export const DEFAULT_CHANNEL_WEIGHT = 0;
|
||||
export const MAX_CHANNEL_WEIGHT = 5;
|
||||
export const DEFAULT_CLAUDE_API_VERSION = "2023-06-01";
|
||||
export const MAX_RETRIES_PER_KEY = 3;
|
||||
export const MAX_ROTATION_ATTEMPTS = 3;
|
||||
|
||||
export type NormalizedChannelConfig = Omit<ChannelConfig, "enabled" | "api_keys" | "auto_retry" | "auto_rotate" | "models" | "supported_models" | "deployment_mapper"> & {
|
||||
export type NormalizedChannelConfig = Omit<ChannelConfig, "enabled" | "weight" | "api_keys" | "auto_retry" | "auto_rotate" | "models" | "supported_models" | "deployment_mapper"> & {
|
||||
enabled: boolean;
|
||||
weight: number;
|
||||
api_keys: string[];
|
||||
auto_retry: boolean;
|
||||
auto_rotate: boolean;
|
||||
@@ -15,6 +18,15 @@ export type NormalizedChannelConfig = Omit<ChannelConfig, "enabled" | "api_keys"
|
||||
deployment_mapper: Record<string, string>;
|
||||
};
|
||||
|
||||
const normalizeChannelWeight = (weight: unknown): number => {
|
||||
if (typeof weight !== "number" || !Number.isFinite(weight)) {
|
||||
return DEFAULT_CHANNEL_WEIGHT;
|
||||
}
|
||||
|
||||
const normalizedWeight = Math.trunc(weight);
|
||||
return Math.min(MAX_CHANNEL_WEIGHT, Math.max(DEFAULT_CHANNEL_WEIGHT, normalizedWeight));
|
||||
};
|
||||
|
||||
const normalizeApiKeys = (config: Partial<ChannelConfig>): string[] => {
|
||||
const rawKeys = Array.isArray(config.api_keys)
|
||||
? [...config.api_keys]
|
||||
@@ -108,6 +120,7 @@ export const normalizeChannelConfig = (
|
||||
type: config.type,
|
||||
endpoint: config.endpoint || "",
|
||||
enabled: config.enabled ?? DEFAULT_CHANNEL_ENABLED,
|
||||
weight: normalizeChannelWeight(config.weight),
|
||||
api_key: typeof config.api_key === "string" ? config.api_key.trim() : undefined,
|
||||
api_keys: normalizeApiKeys(config),
|
||||
auto_retry: config.auto_retry ?? DEFAULT_CHANNEL_AUTO_RETRY,
|
||||
@@ -129,6 +142,7 @@ export const sanitizeChannelConfig = (
|
||||
type: normalized.type,
|
||||
endpoint: normalized.endpoint,
|
||||
enabled: normalized.enabled,
|
||||
weight: normalized.weight,
|
||||
api_keys: normalized.api_keys,
|
||||
auto_retry: normalized.auto_retry,
|
||||
auto_rotate: normalized.auto_rotate,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
export const CONSTANTS = {
|
||||
VERSION: "v0.0.1",
|
||||
DB_VERSION_KEY: "db_version",
|
||||
DB_VERSION: "v0.0.4",
|
||||
DB_VERSION: "v0.0.5",
|
||||
MODEL_PRICING_KEY: "MODEL_PRICING_CONFIG",
|
||||
}
|
||||
@@ -103,8 +103,14 @@ export const resolveChannel = async (
|
||||
return c.text(`Model not supported: ${requestedModel}. Please configure models.`, 400);
|
||||
}
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * availableChannels.length);
|
||||
const selectedChannel = availableChannels[randomIndex];
|
||||
const maxWeight = availableChannels.reduce((highest, current) => {
|
||||
return Math.max(highest, current.config.weight ?? 0);
|
||||
}, 0);
|
||||
const highestPriorityChannels = availableChannels.filter((channel) => {
|
||||
return (channel.config.weight ?? 0) === maxWeight;
|
||||
});
|
||||
const randomIndex = Math.floor(Math.random() * highestPriorityChannels.length);
|
||||
const selectedChannel = highestPriorityChannels[randomIndex];
|
||||
const targetChannelKey = selectedChannel.key;
|
||||
const targetChannelConfig = selectedChannel.config;
|
||||
const trackingState: RequestTrackingState = {
|
||||
|
||||
Reference in new issue
Block a user