feat(chat): load model options from backend
This commit is contained in:
@@ -28,6 +28,7 @@ import BoltRounded from "@mui/icons-material/BoltRounded";
|
||||
import AutoAwesomeRounded from "@mui/icons-material/AutoAwesomeRounded";
|
||||
import VerifiedUserRounded from "@mui/icons-material/VerifiedUserRounded";
|
||||
import AdminPanelSettingsRounded from "@mui/icons-material/AdminPanelSettingsRounded";
|
||||
import type { AgentModelOption } from "@/lib/chatModels";
|
||||
import type { AgentApprovalMode, AgentModel } from "@/lib/chatStream";
|
||||
|
||||
export type AgentComposerHandle = {
|
||||
@@ -48,12 +49,23 @@ type AgentComposerProps = {
|
||||
onAbort: () => void;
|
||||
onStartListening: () => void;
|
||||
onStopListening: () => void;
|
||||
selectedModel: AgentModel;
|
||||
modelOptions: AgentModelOption[];
|
||||
selectedModel?: AgentModel;
|
||||
onModelChange: (model: AgentModel) => void;
|
||||
approvalMode: AgentApprovalMode;
|
||||
onApprovalModeChange: (mode: AgentApprovalMode) => void;
|
||||
};
|
||||
|
||||
const renderModelIcon = (
|
||||
icon: AgentModelOption["icon"] | undefined,
|
||||
props?: React.ComponentProps<typeof BoltRounded>,
|
||||
) =>
|
||||
icon === "bolt" ? (
|
||||
<BoltRounded {...props} />
|
||||
) : (
|
||||
<AutoAwesomeRounded {...props} />
|
||||
);
|
||||
|
||||
export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposerProps>(function AgentComposer({
|
||||
isHydrating = false,
|
||||
isStreaming,
|
||||
@@ -64,6 +76,7 @@ export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposer
|
||||
onAbort,
|
||||
onStartListening,
|
||||
onStopListening,
|
||||
modelOptions,
|
||||
selectedModel,
|
||||
onModelChange,
|
||||
approvalMode,
|
||||
@@ -74,6 +87,7 @@ export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposer
|
||||
const [input, setInput] = React.useState("");
|
||||
const [isPresetOpen, setIsPresetOpen] = React.useState(false);
|
||||
const canSend = input.trim().length > 0 && !isStreaming && !isHydrating;
|
||||
const selectedModelOption = modelOptions.find((model) => model.id === selectedModel);
|
||||
|
||||
React.useImperativeHandle(
|
||||
ref,
|
||||
@@ -347,19 +361,21 @@ export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposer
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<FormControl size="small" sx={{ minWidth: 80 }}>
|
||||
<Select
|
||||
value={selectedModel}
|
||||
value={selectedModel ?? ""}
|
||||
onChange={(event) => onModelChange(event.target.value as AgentModel)}
|
||||
disabled={isHydrating || isStreaming}
|
||||
disabled={isHydrating || isStreaming || modelOptions.length === 0}
|
||||
aria-label="模型选择"
|
||||
renderValue={(val) => (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
{val === "deepseek/deepseek-v4-flash" ? (
|
||||
<BoltRounded sx={{ fontSize: 18, color: "inherit", transition: "color 0.2s" }} />
|
||||
) : (
|
||||
<AutoAwesomeRounded sx={{ fontSize: 16, color: "inherit", transition: "color 0.2s" }} />
|
||||
)}
|
||||
renderValue={() => (
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
|
||||
{renderModelIcon(selectedModelOption?.icon, {
|
||||
sx: {
|
||||
fontSize: selectedModelOption?.icon === "bolt" ? 18 : 16,
|
||||
color: "inherit",
|
||||
transition: "color 0.2s",
|
||||
},
|
||||
})}
|
||||
<Typography sx={{ fontSize: "0.8rem", fontWeight: 600, color: "inherit", transition: "color 0.2s" }}>
|
||||
{val === "deepseek/deepseek-v4-flash" ? "快速" : "专家"}
|
||||
{selectedModelOption?.label ?? "模型"}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
@@ -433,30 +449,25 @@ export const AgentComposer = React.forwardRef<AgentComposerHandle, AgentComposer
|
||||
}}
|
||||
>
|
||||
<Box sx={{ px: 2, py: 1.5, pb: 1, display: "flex", alignItems: "center", gap: 1, pointerEvents: "none" }}>
|
||||
<Box
|
||||
component="img"
|
||||
src="/deepseek-logo.svg"
|
||||
alt="DeepSeek"
|
||||
sx={{ width: 16, height: 16, display: "block", flexShrink: 0 }}
|
||||
/>
|
||||
<AutoAwesomeRounded sx={{ width: 16, height: 16, color: "text.secondary", flexShrink: 0 }} />
|
||||
<Typography sx={{ fontSize: "0.75rem", fontWeight: 700, color: "text.secondary", letterSpacing: 0.5 }}>
|
||||
DEEPSEEK V4
|
||||
模型选择
|
||||
</Typography>
|
||||
</Box>
|
||||
<MenuItem value="deepseek/deepseek-v4-flash">
|
||||
<BoltRounded className="icon" sx={{ mr: 1.5, mt: 0.2, fontSize: 20, color: "text.secondary", transition: "color 0.2s" }} />
|
||||
<Box>
|
||||
<Typography className="title" sx={{ fontSize: "0.85rem", fontWeight: 700, color: "text.primary", mb: 0.2, transition: "color 0.2s" }}>快速</Typography>
|
||||
<Typography sx={{ fontSize: "0.7rem", fontWeight: 500, color: "text.secondary", lineHeight: 1.3 }}>快速回答和任务执行</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
<MenuItem value="deepseek/deepseek-v4-pro">
|
||||
<AutoAwesomeRounded className="icon" sx={{ mr: 1.5, mt: 0.2, fontSize: 18, color: "text.secondary", transition: "color 0.2s" }} />
|
||||
<Box>
|
||||
<Typography className="title" sx={{ fontSize: "0.85rem", fontWeight: 700, color: "text.primary", mb: 0.2, transition: "color 0.2s" }}>专家</Typography>
|
||||
<Typography sx={{ fontSize: "0.7rem", fontWeight: 500, color: "text.secondary", lineHeight: 1.3 }}>探索、解决复杂任务</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
{modelOptions.map((model) => (
|
||||
<MenuItem key={model.id} value={model.id}>
|
||||
{renderModelIcon(model.icon, {
|
||||
className: "icon",
|
||||
sx: { mr: 1.5, mt: 0.2, fontSize: model.icon === "bolt" ? 20 : 18, color: "text.secondary", transition: "color 0.2s" },
|
||||
})}
|
||||
<Box>
|
||||
<Typography className="title" sx={{ fontSize: "0.85rem", fontWeight: 700, color: "text.primary", mb: 0.2, transition: "color 0.2s" }}>{model.label}</Typography>
|
||||
{model.description ? (
|
||||
<Typography sx={{ fontSize: "0.7rem", fontWeight: 500, color: "text.secondary", lineHeight: 1.3 }}>{model.description}</Typography>
|
||||
) : null}
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user