mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-30 10:25:05 +08:00
feat: add Claude subagent model config (#4830)
* feat: add Claude subagent takeover config * feat: add Claude subagent model field * i18n: add Claude subagent model labels * fix(proxy): preserve configured subagent model mapping * fix(providers): exclude subagent model from Claude common config * style: format rust code
This commit is contained in:
@@ -126,6 +126,7 @@ interface ClaudeFormFieldsProps {
|
||||
defaultOpusModelName: string;
|
||||
defaultFableModel: string;
|
||||
defaultFableModelName: string;
|
||||
subagentModel: string;
|
||||
onModelChange: (field: ClaudeModelEnvField, value: string) => void;
|
||||
|
||||
// Speed Test Endpoints
|
||||
@@ -196,6 +197,7 @@ export function ClaudeFormFields({
|
||||
defaultOpusModelName,
|
||||
defaultFableModel,
|
||||
defaultFableModelName,
|
||||
subagentModel,
|
||||
onModelChange,
|
||||
speedTestEndpoints,
|
||||
apiFormat,
|
||||
@@ -221,6 +223,7 @@ export function ClaudeFormFields({
|
||||
defaultSonnetModel ||
|
||||
defaultOpusModel ||
|
||||
defaultFableModel ||
|
||||
subagentModel ||
|
||||
apiFormat !== "anthropic" ||
|
||||
apiKeyField !== "ANTHROPIC_AUTH_TOKEN" ||
|
||||
customUserAgent ||
|
||||
@@ -513,12 +516,12 @@ export function ClaudeFormFields({
|
||||
};
|
||||
|
||||
type ModelRoleRow = {
|
||||
role: "sonnet" | "opus" | "fable" | "haiku";
|
||||
role: "sonnet" | "opus" | "fable" | "haiku" | "subagent";
|
||||
label: string;
|
||||
model: string;
|
||||
displayName: string;
|
||||
displayName?: string;
|
||||
modelField: ClaudeModelEnvField;
|
||||
displayNameField: ClaudeModelEnvField;
|
||||
displayNameField?: ClaudeModelEnvField;
|
||||
inputId: string;
|
||||
supportsOneM: boolean;
|
||||
};
|
||||
@@ -564,6 +567,16 @@ export function ClaudeFormFields({
|
||||
inputId: "claudeDefaultHaikuModel",
|
||||
supportsOneM: false,
|
||||
},
|
||||
{
|
||||
role: "subagent",
|
||||
label: t("providerForm.modelRoleSubagent", {
|
||||
defaultValue: "Subagent",
|
||||
}),
|
||||
model: subagentModel,
|
||||
modelField: "CLAUDE_CODE_SUBAGENT_MODEL",
|
||||
inputId: "claudeCodeSubagentModel",
|
||||
supportsOneM: true,
|
||||
},
|
||||
];
|
||||
|
||||
const handleRoleModelChange = (row: ModelRoleRow, value: string) => {
|
||||
@@ -572,10 +585,10 @@ export function ClaudeFormFields({
|
||||
? value
|
||||
: stripClaudeOneMMarker(value);
|
||||
const nextModelBase = stripClaudeOneMMarker(normalizedValue).trim();
|
||||
const displayName = row.displayName.trim();
|
||||
const displayName = row.displayName?.trim() ?? "";
|
||||
const shouldSyncDisplayName = !displayName || displayName === oldModelBase;
|
||||
onModelChange(row.modelField, normalizedValue);
|
||||
if (shouldSyncDisplayName) {
|
||||
if (row.displayNameField && shouldSyncDisplayName) {
|
||||
onModelChange(row.displayNameField, nextModelBase);
|
||||
}
|
||||
};
|
||||
@@ -815,17 +828,20 @@ export function ClaudeFormFields({
|
||||
defaultSonnetModel ||
|
||||
defaultOpusModel ||
|
||||
defaultFableModel ||
|
||||
defaultHaikuModel;
|
||||
defaultHaikuModel ||
|
||||
subagentModel;
|
||||
if (value) {
|
||||
for (const row of modelRoleRows) {
|
||||
const roleValue = row.supportsOneM
|
||||
? value
|
||||
: stripClaudeOneMMarker(value);
|
||||
onModelChange(row.modelField, roleValue);
|
||||
onModelChange(
|
||||
row.displayNameField,
|
||||
stripClaudeOneMMarker(roleValue),
|
||||
);
|
||||
if (row.displayNameField) {
|
||||
onModelChange(
|
||||
row.displayNameField,
|
||||
stripClaudeOneMMarker(roleValue),
|
||||
);
|
||||
}
|
||||
}
|
||||
toast.success(
|
||||
t("providerForm.quickSetSuccess", {
|
||||
@@ -839,7 +855,8 @@ export function ClaudeFormFields({
|
||||
!defaultHaikuModel &&
|
||||
!defaultSonnetModel &&
|
||||
!defaultOpusModel &&
|
||||
!defaultFableModel
|
||||
!defaultFableModel &&
|
||||
!subagentModel
|
||||
}
|
||||
className="h-7 gap-1"
|
||||
>
|
||||
@@ -907,19 +924,30 @@ export function ClaudeFormFields({
|
||||
<div className="flex h-9 items-center rounded-md border border-input bg-muted px-3 text-sm font-medium text-muted-foreground">
|
||||
{row.label}
|
||||
</div>
|
||||
<Input
|
||||
value={row.displayName}
|
||||
onChange={(event) =>
|
||||
onModelChange(row.displayNameField, event.target.value)
|
||||
}
|
||||
placeholder={
|
||||
modelBase ||
|
||||
t("providerForm.modelDisplayNamePlaceholder", {
|
||||
defaultValue: "例如 DeepSeek V4 Pro",
|
||||
})
|
||||
}
|
||||
autoComplete="off"
|
||||
/>
|
||||
{row.displayNameField ? (
|
||||
<Input
|
||||
value={row.displayName ?? ""}
|
||||
onChange={(event) =>
|
||||
onModelChange(
|
||||
row.displayNameField!,
|
||||
event.target.value,
|
||||
)
|
||||
}
|
||||
placeholder={
|
||||
modelBase ||
|
||||
t("providerForm.modelDisplayNamePlaceholder", {
|
||||
defaultValue: "例如 DeepSeek V4 Pro",
|
||||
})
|
||||
}
|
||||
autoComplete="off"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-9 items-center rounded-md border border-input bg-muted px-3 text-sm text-muted-foreground">
|
||||
{t("providerForm.modelNoDisplayName", {
|
||||
defaultValue: "不显示在 /model 菜单",
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{renderModelInput(
|
||||
row.inputId,
|
||||
modelBase,
|
||||
|
||||
@@ -489,6 +489,7 @@ function ProviderFormFull({
|
||||
defaultOpusModelName,
|
||||
defaultFableModel,
|
||||
defaultFableModelName,
|
||||
subagentModel,
|
||||
handleModelChange,
|
||||
} = useModelState({
|
||||
settingsConfig: form.getValues("settingsConfig"),
|
||||
@@ -2099,6 +2100,7 @@ function ProviderFormFull({
|
||||
defaultOpusModelName={defaultOpusModelName}
|
||||
defaultFableModel={defaultFableModel}
|
||||
defaultFableModelName={defaultFableModelName}
|
||||
subagentModel={subagentModel}
|
||||
onModelChange={handleModelChange}
|
||||
speedTestEndpoints={speedTestEndpoints}
|
||||
apiFormat={localApiFormat}
|
||||
|
||||
@@ -14,7 +14,8 @@ export type ClaudeModelEnvField =
|
||||
| "ANTHROPIC_DEFAULT_OPUS_MODEL"
|
||||
| "ANTHROPIC_DEFAULT_OPUS_MODEL_NAME"
|
||||
| "ANTHROPIC_DEFAULT_FABLE_MODEL"
|
||||
| "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME";
|
||||
| "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME"
|
||||
| "CLAUDE_CODE_SUBAGENT_MODEL";
|
||||
|
||||
export const CLAUDE_ONE_M_MARKER = "[1M]";
|
||||
|
||||
@@ -81,6 +82,10 @@ function parseModelsFromConfig(settingsConfig: string) {
|
||||
typeof env.ANTHROPIC_DEFAULT_FABLE_MODEL_NAME === "string"
|
||||
? env.ANTHROPIC_DEFAULT_FABLE_MODEL_NAME
|
||||
: stripClaudeOneMMarker(fable);
|
||||
const subagent =
|
||||
typeof env.CLAUDE_CODE_SUBAGENT_MODEL === "string"
|
||||
? env.CLAUDE_CODE_SUBAGENT_MODEL
|
||||
: "";
|
||||
|
||||
return {
|
||||
model,
|
||||
@@ -92,6 +97,7 @@ function parseModelsFromConfig(settingsConfig: string) {
|
||||
opusName,
|
||||
fable,
|
||||
fableName,
|
||||
subagent,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
@@ -104,6 +110,7 @@ function parseModelsFromConfig(settingsConfig: string) {
|
||||
opusName: "",
|
||||
fable: "",
|
||||
fableName: "",
|
||||
subagent: "",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -134,6 +141,7 @@ export function useModelState({
|
||||
const [defaultFableModelName, setDefaultFableModelName] = useState(
|
||||
initial.fableName,
|
||||
);
|
||||
const [subagentModel, setSubagentModel] = useState(initial.subagent);
|
||||
|
||||
const isUserEditingRef = useRef(false);
|
||||
const lastConfigRef = useRef(settingsConfig);
|
||||
@@ -164,6 +172,7 @@ export function useModelState({
|
||||
setDefaultOpusModelName(parsed.opusName);
|
||||
setDefaultFableModel(parsed.fable);
|
||||
setDefaultFableModelName(parsed.fableName);
|
||||
setSubagentModel(parsed.subagent);
|
||||
}, [settingsConfig]);
|
||||
|
||||
const handleModelChange = useCallback(
|
||||
@@ -186,6 +195,7 @@ export function useModelState({
|
||||
setDefaultFableModel(value);
|
||||
if (field === "ANTHROPIC_DEFAULT_FABLE_MODEL_NAME")
|
||||
setDefaultFableModelName(value);
|
||||
if (field === "CLAUDE_CODE_SUBAGENT_MODEL") setSubagentModel(value);
|
||||
|
||||
try {
|
||||
const currentConfig = latestConfigRef.current
|
||||
@@ -233,6 +243,8 @@ export function useModelState({
|
||||
setDefaultFableModel,
|
||||
defaultFableModelName,
|
||||
setDefaultFableModelName,
|
||||
subagentModel,
|
||||
setSubagentModel,
|
||||
handleModelChange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1124,8 +1124,10 @@
|
||||
"modelRoleOpus": "Opus",
|
||||
"modelRoleFable": "Fable",
|
||||
"modelRoleHaiku": "Haiku",
|
||||
"modelRoleSubagent": "Subagent",
|
||||
"modelDisplayNameLabel": "Display name",
|
||||
"modelDisplayNamePlaceholder": "e.g. DeepSeek V4 Pro",
|
||||
"modelNoDisplayName": "Not shown in /model",
|
||||
"requestModelLabel": "Requested model",
|
||||
"modelOneMHeader": "Declare 1M",
|
||||
"modelOneMLabel": "1M",
|
||||
|
||||
@@ -1124,8 +1124,10 @@
|
||||
"modelRoleOpus": "Opus",
|
||||
"modelRoleFable": "Fable",
|
||||
"modelRoleHaiku": "Haiku",
|
||||
"modelRoleSubagent": "Subagent",
|
||||
"modelDisplayNameLabel": "表示名",
|
||||
"modelDisplayNamePlaceholder": "例: DeepSeek V4 Pro",
|
||||
"modelNoDisplayName": "/model には表示されません",
|
||||
"requestModelLabel": "リクエストモデル",
|
||||
"modelOneMHeader": "1M 対応を宣言",
|
||||
"modelOneMLabel": "1M",
|
||||
|
||||
@@ -1096,8 +1096,10 @@
|
||||
"modelRoleOpus": "Opus",
|
||||
"modelRoleFable": "Fable",
|
||||
"modelRoleHaiku": "Haiku",
|
||||
"modelRoleSubagent": "Subagent",
|
||||
"modelDisplayNameLabel": "顯示名稱",
|
||||
"modelDisplayNamePlaceholder": "例如 DeepSeek V4 Pro",
|
||||
"modelNoDisplayName": "不顯示於 /model 選單",
|
||||
"requestModelLabel": "實際請求模型",
|
||||
"modelOneMHeader": "聲明支援 1M",
|
||||
"modelOneMLabel": "1M",
|
||||
|
||||
@@ -1124,8 +1124,10 @@
|
||||
"modelRoleOpus": "Opus",
|
||||
"modelRoleFable": "Fable",
|
||||
"modelRoleHaiku": "Haiku",
|
||||
"modelRoleSubagent": "Subagent",
|
||||
"modelDisplayNameLabel": "显示名称",
|
||||
"modelDisplayNamePlaceholder": "例如 DeepSeek V4 Pro",
|
||||
"modelNoDisplayName": "不显示在 /model 菜单",
|
||||
"requestModelLabel": "实际请求模型",
|
||||
"modelOneMHeader": "声明支持 1M",
|
||||
"modelOneMLabel": "1M",
|
||||
|
||||
Reference in New Issue
Block a user