mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 01:25:33 +08:00
feat: add Bedrock request optimizer (PRE-SEND thinking + cache injection) (#1301)
* 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>
This commit is contained in:
@@ -3,7 +3,11 @@ 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 } from "@/lib/api/settings";
|
||||
import {
|
||||
settingsApi,
|
||||
type RectifierConfig,
|
||||
type OptimizerConfig,
|
||||
} from "@/lib/api/settings";
|
||||
|
||||
export function RectifierConfigPanel() {
|
||||
const { t } = useTranslation();
|
||||
@@ -12,6 +16,12 @@ export function RectifierConfigPanel() {
|
||||
requestThinkingSignature: true,
|
||||
requestThinkingBudget: true,
|
||||
});
|
||||
const [optimizerConfig, setOptimizerConfig] = useState<OptimizerConfig>({
|
||||
enabled: false,
|
||||
thinkingOptimizer: true,
|
||||
cacheInjection: true,
|
||||
cacheTtl: "1h",
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -20,6 +30,10 @@ export function RectifierConfigPanel() {
|
||||
.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>) => {
|
||||
@@ -34,6 +48,18 @@ export function RectifierConfigPanel() {
|
||||
}
|
||||
};
|
||||
|
||||
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 (
|
||||
@@ -86,6 +112,94 @@ export function RectifierConfigPanel() {
|
||||
/>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user