fix: stabilize Codex continuation and proxy retry recovery / 修复 Codex 续传与代理重试稳定性 (#257)

* wip: preserve local changes before upstream merge

* Improve proxy routing stability and add stable-first strategy

* Sync explicit-group routing strategy to eligible source routes

* Fix proxy retry and runtime health review issues

* fix: make proxy channel retry attempts configurable

* fix: harden proxy retry recovery and bookkeeping

* fix: address pr 257 review comments

---------

Co-authored-by: Cita <juricek.chen@gmail.com>
This commit is contained in:
Bainily
2026-03-23 19:17:22 +08:00
committed by GitHub
parent 5be5e2eb07
commit 1449f08e6c
36 changed files with 1802 additions and 656 deletions
+2 -1
View File
@@ -2092,7 +2092,8 @@ body {
}
.site-balance-inline {
display: inline-flex;
display: flex;
width: 100%;
flex-wrap: wrap;
align-items: baseline;
gap: 6px;
+4 -12
View File
@@ -24,6 +24,10 @@ import {
} from './helpers/routeMissingTokenHints.js';
import { buildVisibleRouteList } from './helpers/routeListVisibility.js';
import { buildZeroChannelPlaceholderRoutes } from './helpers/zeroChannelRoutes.js';
import {
getRouteRoutingStrategyLabel,
normalizeRouteRoutingStrategyValue,
} from './token-routes/routingStrategy.js';
import type {
RouteSortBy,
@@ -89,18 +93,6 @@ const EMPTY_ROUTE_FORM: RouteEditorForm = {
advancedOpen: false,
};
function normalizeRouteRoutingStrategyValue(value?: RouteRoutingStrategy | null): RouteRoutingStrategy {
if (value === 'round_robin' || value === 'stable_first') return value;
return 'weighted';
}
function getRouteRoutingStrategyLabel(value?: RouteRoutingStrategy | null): string {
const strategy = normalizeRouteRoutingStrategyValue(value);
if (strategy === 'round_robin') return tr('轮询');
if (strategy === 'stable_first') return tr('稳定优先');
return tr('权重随机');
}
function getRouteRoutingStrategySuccessMessage(value: RouteRoutingStrategy): string {
if (value === 'round_robin') return '已切换为轮询策略';
if (value === 'stable_first') return '已切换为稳定优先策略';
+7 -15
View File
@@ -28,6 +28,10 @@ import type {
} from './types.js';
import type { RouteCandidateView, RouteTokenOption } from '../helpers/routeModelCandidatesIndex.js';
import { SortableChannelRow } from './SortableChannelRow.js';
import {
getRouteRoutingStrategyLabel,
normalizeRouteRoutingStrategyValue,
} from './routingStrategy.js';
import {
isRouteExactModel,
isExplicitGroupRoute,
@@ -76,18 +80,6 @@ type RouteCardProps = {
onToggleSourceGroup: (groupKey: string) => void;
};
function normalizeRoutingStrategy(value?: RouteRoutingStrategy | null): RouteRoutingStrategy {
if (value === 'round_robin' || value === 'stable_first') return value;
return 'weighted';
}
function getRoutingStrategyLabel(value?: RouteRoutingStrategy | null): string {
const strategy = normalizeRoutingStrategy(value);
if (strategy === 'round_robin') return tr('轮询');
if (strategy === 'stable_first') return tr('稳定优先');
return tr('权重随机');
}
function AnimatedCollapseSection({ open, children }: { open: boolean; children: ReactNode }) {
const presence = useAnimatedVisibility(open, 220);
if (!presence.shouldRender) return null;
@@ -139,7 +131,7 @@ function RouteCardInner({
const readOnlyRoute = route.kind === 'zero_channel' || route.readOnly === true || route.isVirtual === true;
const channelManagementDisabled = explicitGroupRoute;
const title = resolveRouteTitle(route);
const routingStrategy = normalizeRoutingStrategy(route.routingStrategy);
const routingStrategy = normalizeRouteRoutingStrategyValue(route.routingStrategy);
const routingStrategyOptions = [
{
value: 'weighted',
@@ -252,7 +244,7 @@ function RouteCardInner({
</span>
) : (
<span className="badge badge-muted" style={{ fontSize: 10, flexShrink: 0 }}>
{getRoutingStrategyLabel(routingStrategy)}
{getRouteRoutingStrategyLabel(routingStrategy)}
</span>
)}
@@ -394,7 +386,7 @@ function RouteCardInner({
{explicitGroupRoute ? (
<div style={{ fontSize: 12, color: 'var(--color-text-muted)', marginBottom: 10 }}>
{tr('该群组会将多个来源模型聚合为一个对外模型名;通道信息继承自来源模型,当前仅支持查看,不支持直接维护。')}
{tr('该群组会将多个来源模型聚合为一个对外模型名;当前策略以群组设置为准,来源模型会尽量跟随同步,但已单独自定义或被其他群组复用的来源模型不会被覆盖。')}
</div>
) : !exactRoute ? (
<div style={{ fontSize: 12, color: 'var(--color-text-muted)', marginBottom: 10 }}>
@@ -0,0 +1,14 @@
import { tr } from '../../i18n.js';
import type { RouteRoutingStrategy } from './types.js';
export function normalizeRouteRoutingStrategyValue(value?: RouteRoutingStrategy | null): RouteRoutingStrategy {
if (value === 'round_robin' || value === 'stable_first') return value;
return 'weighted';
}
export function getRouteRoutingStrategyLabel(value?: RouteRoutingStrategy | null): string {
const strategy = normalizeRouteRoutingStrategyValue(value);
if (strategy === 'round_robin') return tr('轮询');
if (strategy === 'stable_first') return tr('稳定优先');
return tr('权重随机');
}