|
1 | | -export const CHANNEL_ROUTE_MODES = ['sequential', 'balanced', 'project'] as const; |
| 1 | +export const CHANNEL_ROUTE_MODES = ['sequential', 'balanced'] as const; |
2 | 2 | export const PROJECT_MODE_FALLBACK_ROUTE_MODES = ['sequential', 'balanced'] as const; |
3 | 3 | export const CHANNEL_FALLBACK_MODES = ['fail-closed', 'fallback-default', 'fallback-global'] as const; |
4 | 4 | export const UPSTREAM_COMPAT_ROUTE_MODES = ['dedicated', 'prefer', 'ordered', 'weighted', 'canary'] as const; |
| 5 | +export const LEGACY_CHANNEL_ROUTING_BYPASS_IDS = ['session-affinity', 'websocket-pin', 'route-order-header'] as const; |
5 | 6 |
|
6 | 7 | export type ChannelID = 'codex' | 'claude'; |
7 | 8 | export type ChannelRouteMode = (typeof CHANNEL_ROUTE_MODES)[number]; |
@@ -69,12 +70,50 @@ export interface ChannelRoutingConfigDraft { |
69 | 70 | shadowRouteMode?: unknown; |
70 | 71 | } |
71 | 72 |
|
| 73 | +export type LegacyChannelRoutingBypassID = (typeof LEGACY_CHANNEL_ROUTING_BYPASS_IDS)[number]; |
| 74 | +export type LegacyChannelRoutingBypassDisposition = 'blocked' | 'ignored'; |
| 75 | + |
| 76 | +export interface LegacyChannelRoutingBypass { |
| 77 | + id: LegacyChannelRoutingBypassID; |
| 78 | + label: string; |
| 79 | + disposition: LegacyChannelRoutingBypassDisposition; |
| 80 | + detail: string; |
| 81 | +} |
| 82 | + |
| 83 | +export interface LegacyRoutingMaskPanel { |
| 84 | + title: string; |
| 85 | + summary: string; |
| 86 | + note: string; |
| 87 | + hasDetails: false; |
| 88 | +} |
| 89 | + |
72 | 90 | export interface NormalizedChannelRoutingConfig { |
73 | 91 | config: ChannelRoutingConfig; |
74 | 92 | ignoredUpstreamModes: UpstreamCompatRouteMode[]; |
75 | 93 | invalidModes: string[]; |
76 | 94 | } |
77 | 95 |
|
| 96 | +export const LEGACY_CHANNEL_ROUTING_BYPASSES: LegacyChannelRoutingBypass[] = [ |
| 97 | + { |
| 98 | + id: 'session-affinity', |
| 99 | + label: 'Session affinity', |
| 100 | + disposition: 'blocked', |
| 101 | + detail: '跳过,不进入新配置;仅保留为 runtime 粘性信号', |
| 102 | + }, |
| 103 | + { |
| 104 | + id: 'websocket-pin', |
| 105 | + label: 'WebSocket pin', |
| 106 | + disposition: 'blocked', |
| 107 | + detail: '屏蔽,不进入新配置;仅保留为连接级运行态信号', |
| 108 | + }, |
| 109 | + { |
| 110 | + id: 'route-order-header', |
| 111 | + label: 'Route order header', |
| 112 | + disposition: 'ignored', |
| 113 | + detail: '跳过,不进入新配置;探测路径不再注入顺序头', |
| 114 | + }, |
| 115 | +]; |
| 116 | + |
78 | 117 | export interface ChannelRouteAuditEvent { |
79 | 118 | id: string; |
80 | 119 | recordedAt: string; |
@@ -190,6 +229,19 @@ export function updateChannelRoutingConfig( |
190 | 229 | }; |
191 | 230 | } |
192 | 231 |
|
| 232 | +export function buildLegacyRoutingBypassSummary() { |
| 233 | + return `${LEGACY_CHANNEL_ROUTING_BYPASSES.length} 个 legacy 输入已从新配置中屏蔽`; |
| 234 | +} |
| 235 | + |
| 236 | +export function buildLegacyRoutingMaskPanel(): LegacyRoutingMaskPanel { |
| 237 | + return { |
| 238 | + title: 'Legacy compatibility mask', |
| 239 | + summary: buildLegacyRoutingBypassSummary(), |
| 240 | + note: '这些信号只保留为兼容层,不写入新配置,也不影响上游合并后的主路由模型。', |
| 241 | + hasDetails: false, |
| 242 | + }; |
| 243 | +} |
| 244 | + |
193 | 245 | export function buildChannelRouteAuditEventSummary(event: ChannelRouteAuditEvent): ChannelRouteAuditEventSummary { |
194 | 246 | const routeMode = String(event.routeMode || 'unknown').trim() || 'unknown'; |
195 | 247 | const selected = String(event.selectedAccountID || 'none').trim() || 'none'; |
@@ -290,8 +342,6 @@ function normalizeProjectFallbackRouteMode( |
290 | 342 | const classified = classifyChannelRouteMode(input); |
291 | 343 | if (classified.kind === 'upstream-compat') { |
292 | 344 | ignoredUpstreamModes.push(classified.mode); |
293 | | - } else if (classified.kind === 'gettokens' && classified.mode === 'project') { |
294 | | - invalidModes.push(classified.mode); |
295 | 345 | } else if (classified.kind === 'invalid' && classified.mode) { |
296 | 346 | invalidModes.push(classified.mode); |
297 | 347 | } |
|
0 commit comments