mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-27 08:14:33 +08:00
8217bfff50
* feat: add Bedrock request optimizer (PRE-SEND thinking + cache injection) Add a PRE-SEND request optimizer that enhances Bedrock API requests before forwarding, complementing the existing POST-ERROR rectifier system. New modules: - thinking_optimizer: 3-path model detection (adaptive/legacy/skip) - Opus 4.6/Sonnet 4.6: adaptive thinking + effort max + 1M context beta - Legacy models: inject extended thinking with max budget - Haiku: skip (no modification) - cache_injector: auto-inject cache_control breakpoints (max 4) - Injects at tools/system/assistant message positions - TTL upgrade for existing breakpoints (5m → 1h) Gate: only activates for Bedrock providers (CLAUDE_CODE_USE_BEDROCK=1) Config: stored in SQLite settings table, default OFF, user opt-in UI: new Optimizer section in RectifierConfigPanel with 3 toggles + TTL 18 unit tests covering all paths. Verified against live Bedrock API. * chore: remove docs/plans directory * fix: address code review findings for Bedrock request optimizer P0 fixes: - Replace hardcoded Chinese with i18n t() calls in optimizer panel, add translation keys to zh/en/ja locale files - Fix u64 underflow: max_tokens - 1 → max_tokens.saturating_sub(1) - Move optimizer from before retry loop to per-provider with body cloning, preventing Bedrock fields leaking to non-Bedrock providers P1 fixes: - Replace .map() side-effect pattern with idiomatic if-let (clippy) - Fix module alphabetical ordering in mod.rs - Add cache_ttl whitelist validation in set_optimizer_config - Remove #[allow(unused_assignments)] and dead budget decrement --------- Co-authored-by: Keith (via OpenClaw) <keithyt06@users.noreply.github.com> Co-authored-by: Jason <farion1231@gmail.com>
206 lines
7.0 KiB
TypeScript
206 lines
7.0 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { toast } from "sonner";
|
|
import { Switch } from "@/components/ui/switch";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
settingsApi,
|
|
type RectifierConfig,
|
|
type OptimizerConfig,
|
|
} from "@/lib/api/settings";
|
|
|
|
export function RectifierConfigPanel() {
|
|
const { t } = useTranslation();
|
|
const [config, setConfig] = useState<RectifierConfig>({
|
|
enabled: true,
|
|
requestThinkingSignature: true,
|
|
requestThinkingBudget: true,
|
|
});
|
|
const [optimizerConfig, setOptimizerConfig] = useState<OptimizerConfig>({
|
|
enabled: false,
|
|
thinkingOptimizer: true,
|
|
cacheInjection: true,
|
|
cacheTtl: "1h",
|
|
});
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
settingsApi
|
|
.getRectifierConfig()
|
|
.then(setConfig)
|
|
.catch((e) => console.error("Failed to load rectifier config:", e))
|
|
.finally(() => setIsLoading(false));
|
|
settingsApi
|
|
.getOptimizerConfig()
|
|
.then(setOptimizerConfig)
|
|
.catch((e) => console.error("Failed to load optimizer config:", e));
|
|
}, []);
|
|
|
|
const handleChange = async (updates: Partial<RectifierConfig>) => {
|
|
const newConfig = { ...config, ...updates };
|
|
setConfig(newConfig);
|
|
try {
|
|
await settingsApi.setRectifierConfig(newConfig);
|
|
} catch (e) {
|
|
console.error("Failed to save rectifier config:", e);
|
|
toast.error(String(e));
|
|
setConfig(config);
|
|
}
|
|
};
|
|
|
|
const handleOptimizerChange = async (updates: Partial<OptimizerConfig>) => {
|
|
const newConfig = { ...optimizerConfig, ...updates };
|
|
setOptimizerConfig(newConfig);
|
|
try {
|
|
await settingsApi.setOptimizerConfig(newConfig);
|
|
} catch (e) {
|
|
console.error("Failed to save optimizer config:", e);
|
|
toast.error(String(e));
|
|
setOptimizerConfig(optimizerConfig);
|
|
}
|
|
};
|
|
|
|
if (isLoading) return null;
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label>{t("settings.advanced.rectifier.enabled")}</Label>
|
|
<p className="text-xs text-muted-foreground">
|
|
{t("settings.advanced.rectifier.enabledDescription")}
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={config.enabled}
|
|
onCheckedChange={(checked) => handleChange({ enabled: checked })}
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<h4 className="text-sm font-medium text-muted-foreground">
|
|
{t("settings.advanced.rectifier.requestGroup")}
|
|
</h4>
|
|
<div className="flex items-center justify-between pl-4">
|
|
<div className="space-y-0.5">
|
|
<Label>{t("settings.advanced.rectifier.thinkingSignature")}</Label>
|
|
<p className="text-xs text-muted-foreground">
|
|
{t("settings.advanced.rectifier.thinkingSignatureDescription")}
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={config.requestThinkingSignature}
|
|
disabled={!config.enabled}
|
|
onCheckedChange={(checked) =>
|
|
handleChange({ requestThinkingSignature: checked })
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="flex items-center justify-between pl-4">
|
|
<div className="space-y-0.5">
|
|
<Label>{t("settings.advanced.rectifier.thinkingBudget")}</Label>
|
|
<p className="text-xs text-muted-foreground">
|
|
{t("settings.advanced.rectifier.thinkingBudgetDescription")}
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={config.requestThinkingBudget}
|
|
disabled={!config.enabled}
|
|
onCheckedChange={(checked) =>
|
|
handleChange({ requestThinkingBudget: checked })
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="border-t pt-6 mt-6">
|
|
<div className="space-y-1 mb-4">
|
|
<h3 className="text-sm font-medium">
|
|
{t("settings.advanced.optimizer.title")}
|
|
</h3>
|
|
<p className="text-xs text-muted-foreground">
|
|
{t("settings.advanced.optimizer.description")}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label>{t("settings.advanced.optimizer.enabled")}</Label>
|
|
</div>
|
|
<Switch
|
|
checked={optimizerConfig.enabled}
|
|
onCheckedChange={(checked) =>
|
|
handleOptimizerChange({ enabled: checked })
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-4 pl-4">
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label>
|
|
{t("settings.advanced.optimizer.thinkingOptimizer")}
|
|
</Label>
|
|
<p className="text-xs text-muted-foreground">
|
|
{t(
|
|
"settings.advanced.optimizer.thinkingOptimizerDescription",
|
|
)}
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={optimizerConfig.thinkingOptimizer}
|
|
disabled={!optimizerConfig.enabled}
|
|
onCheckedChange={(checked) =>
|
|
handleOptimizerChange({ thinkingOptimizer: checked })
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label>{t("settings.advanced.optimizer.cacheInjection")}</Label>
|
|
<p className="text-xs text-muted-foreground">
|
|
{t("settings.advanced.optimizer.cacheInjectionDescription")}
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={optimizerConfig.cacheInjection}
|
|
disabled={!optimizerConfig.enabled}
|
|
onCheckedChange={(checked) =>
|
|
handleOptimizerChange({ cacheInjection: checked })
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
{optimizerConfig.cacheInjection && (
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label>{t("settings.advanced.optimizer.cacheTtl")}</Label>
|
|
</div>
|
|
<select
|
|
className="h-9 rounded-md border border-input bg-background px-3 text-sm"
|
|
value={optimizerConfig.cacheTtl}
|
|
disabled={
|
|
!optimizerConfig.enabled || !optimizerConfig.cacheInjection
|
|
}
|
|
onChange={(e) =>
|
|
handleOptimizerChange({ cacheTtl: e.target.value })
|
|
}
|
|
>
|
|
<option value="5m">
|
|
{t("settings.advanced.optimizer.cacheTtl5m")}
|
|
</option>
|
|
<option value="1h">
|
|
{t("settings.advanced.optimizer.cacheTtl1h")}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|