Fix responses compact fallback handling (#426)

* fix route drag overlay alignment

* fix(web): address route drag review comments

* fix route card review follow-up

* Fix responses compact fallback handling

* Fix compact endpoint candidate typing

* Tighten compact fallback matcher
This commit is contained in:
Cita
2026-04-05 19:41:09 +08:00
committed by GitHub
parent b3f6fa0d4f
commit df75a80bf9
10 changed files with 368 additions and 29 deletions
+1
View File
@@ -344,6 +344,7 @@ export type RuntimeSettingsPayload = {
systemProxyUrl?: string;
modelAvailabilityProbeEnabled?: boolean;
codexUpstreamWebsocketEnabled?: boolean;
responsesCompactFallbackToResponsesEnabled?: boolean;
disableCrossProtocolFallback?: boolean;
proxySessionChannelConcurrencyLimit?: number;
proxySessionChannelQueueWaitMs?: number;
+18
View File
@@ -62,6 +62,7 @@ type RuntimeSettings = {
logCleanupRetentionDays: number;
modelAvailabilityProbeEnabled: boolean;
codexUpstreamWebsocketEnabled: boolean;
responsesCompactFallbackToResponsesEnabled: boolean;
disableCrossProtocolFallback: boolean;
proxySessionChannelConcurrencyLimit: number;
proxySessionChannelQueueWaitMs: number;
@@ -241,6 +242,7 @@ export default function Settings() {
logCleanupRetentionDays: 30,
modelAvailabilityProbeEnabled: false,
codexUpstreamWebsocketEnabled: false,
responsesCompactFallbackToResponsesEnabled: false,
disableCrossProtocolFallback: false,
proxySessionChannelConcurrencyLimit: 2,
proxySessionChannelQueueWaitMs: 1500,
@@ -488,6 +490,7 @@ export default function Settings() {
: 30,
modelAvailabilityProbeEnabled: !!runtimeInfo.modelAvailabilityProbeEnabled,
codexUpstreamWebsocketEnabled: !!runtimeInfo.codexUpstreamWebsocketEnabled,
responsesCompactFallbackToResponsesEnabled: !!runtimeInfo.responsesCompactFallbackToResponsesEnabled,
disableCrossProtocolFallback: !!runtimeInfo.disableCrossProtocolFallback,
proxySessionChannelConcurrencyLimit: Number(runtimeInfo.proxySessionChannelConcurrencyLimit) >= 0
? Math.trunc(Number(runtimeInfo.proxySessionChannelConcurrencyLimit))
@@ -709,6 +712,7 @@ export default function Settings() {
try {
const res = await api.updateRuntimeSettings({
codexUpstreamWebsocketEnabled: runtime.codexUpstreamWebsocketEnabled,
responsesCompactFallbackToResponsesEnabled: runtime.responsesCompactFallbackToResponsesEnabled,
proxySessionChannelConcurrencyLimit: runtime.proxySessionChannelConcurrencyLimit,
proxySessionChannelQueueWaitMs: runtime.proxySessionChannelQueueWaitMs,
});
@@ -717,6 +721,9 @@ export default function Settings() {
codexUpstreamWebsocketEnabled: typeof res?.codexUpstreamWebsocketEnabled === 'boolean'
? res.codexUpstreamWebsocketEnabled
: prev.codexUpstreamWebsocketEnabled,
responsesCompactFallbackToResponsesEnabled: typeof res?.responsesCompactFallbackToResponsesEnabled === 'boolean'
? res.responsesCompactFallbackToResponsesEnabled
: prev.responsesCompactFallbackToResponsesEnabled,
proxySessionChannelConcurrencyLimit: Number(res?.proxySessionChannelConcurrencyLimit) >= 0
? Math.trunc(Number(res.proxySessionChannelConcurrencyLimit))
: prev.proxySessionChannelConcurrencyLimit,
@@ -1439,6 +1446,14 @@ export default function Settings() {
/>
允许 metapi 到 Codex 上游使用 WebSocket
</label>
<label style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontSize: 13, color: 'var(--color-text-secondary)', marginBottom: 14 }}>
<input
type="checkbox"
checked={runtime.responsesCompactFallbackToResponsesEnabled}
onChange={(e) => setRuntime((prev) => ({ ...prev, responsesCompactFallbackToResponsesEnabled: e.target.checked }))}
/>
Compact 明确不支持时回退到普通 Responses
</label>
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 12, marginBottom: 10 }}>
<div>
<div style={{ fontSize: 12, color: 'var(--color-text-muted)', marginBottom: 6 }}>会话通道并发上限</div>
@@ -1481,6 +1496,9 @@ export default function Settings() {
<div style={{ fontSize: 12, color: 'var(--color-text-muted)', marginBottom: 12, lineHeight: 1.7 }}>
这组 lease 只作用于能识别稳定 `session_id` 的会话型请求;没有稳定会话标识的普通请求不会进入这个池。
</div>
<div style={{ fontSize: 12, color: 'var(--color-text-muted)', marginBottom: 12, lineHeight: 1.7 }}>
默认只走原生 `/responses/compact`。开启回退后,只有当上游明确返回 compact 不支持或路径不存在时,才会退回普通 `/responses`。
</div>
<div>
<button onClick={saveProxyTransportSettings} disabled={savingProxyTransport} className="btn btn-primary">
{savingProxyTransport ? <><span className="spinner spinner-sm" style={{ borderTopColor: 'white', borderColor: 'rgba(255,255,255,0.3)' }} /> 保存中...</> : '保存传输与并发'}
@@ -56,6 +56,7 @@ describe('Settings proxy transport', () => {
logCleanupProgramLogsEnabled: true,
logCleanupRetentionDays: 14,
codexUpstreamWebsocketEnabled: false,
responsesCompactFallbackToResponsesEnabled: false,
proxySessionChannelConcurrencyLimit: 4,
proxySessionChannelQueueWaitMs: 3200,
routingFallbackUnitCost: 1,
@@ -74,6 +75,7 @@ describe('Settings proxy transport', () => {
apiMock.updateRuntimeSettings.mockResolvedValue({
success: true,
codexUpstreamWebsocketEnabled: true,
responsesCompactFallbackToResponsesEnabled: true,
proxySessionChannelConcurrencyLimit: 6,
proxySessionChannelQueueWaitMs: 4200,
});
@@ -104,6 +106,12 @@ describe('Settings proxy transport', () => {
));
const websocketToggle = websocketToggleLabel.findByType('input');
expect(websocketToggle.props.checked).toBe(false);
const compactFallbackToggleLabel = root.root.find((node) => (
node.type === 'label'
&& collectText(node).includes('Compact 明确不支持时回退到普通 Responses')
));
const compactFallbackToggle = compactFallbackToggleLabel.findByType('input');
expect(compactFallbackToggle.props.checked).toBe(false);
const concurrencyInput = root.root.find((node) => (
node.type === 'input'
@@ -118,6 +126,7 @@ describe('Settings proxy transport', () => {
await act(async () => {
websocketToggle.props.onChange({ target: { checked: true } });
compactFallbackToggle.props.onChange({ target: { checked: true } });
concurrencyInput.props.onChange({ target: { value: '6' } });
queueWaitInput.props.onChange({ target: { value: '4200' } });
});
@@ -134,6 +143,7 @@ describe('Settings proxy transport', () => {
expect(apiMock.updateRuntimeSettings).toHaveBeenCalledWith({
codexUpstreamWebsocketEnabled: true,
responsesCompactFallbackToResponsesEnabled: true,
proxySessionChannelConcurrencyLimit: 6,
proxySessionChannelQueueWaitMs: 4200,
});