feat: remove tool chips above input, add Auto model option

- Remove selected-tool chip/tag area above the input textarea; tool
  selection state is now shown only via button highlight in the tools panel
- Add "Auto" as a third model option between Flash and Pro (default)
- Auto maps to "flash" when sent to the backend (which only accepts
  flash|pro)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gongzhiyong
2026-04-08 18:13:03 +08:00
co-authored by Claude Sonnet 4.6
parent 8c7fe84a3d
commit a1ae32713d
2 changed files with 22 additions and 41 deletions
+8 -5
View File
@@ -72,13 +72,16 @@ export function GeminiChat() {
const [extensions, setExtensions] = useState<Extension[]>(INITIAL_EXTENSIONS);
const [ticketSummary, setTicketSummary] = useState<TicketSummaryData | null>(null);
const [activeTools, setActiveTools] = useState<Set<string>>(new Set());
const [selectedModel, setSelectedModel] = useState<"flash" | "pro">("pro");
const [selectedModel, setSelectedModel] = useState<"flash" | "auto" | "pro">("auto");
const messagesEndRef = useRef<HTMLDivElement>(null);
const abortRef = useRef<AbortController | null>(null);
const activeConversation = conversations.find((c) => c.id === activeConvId) ?? null;
const messages = activeConversation?.messages ?? [];
// Map UI model to backend model ("auto" -> "flash")
const apiModel: "flash" | "pro" = selectedModel === "pro" ? "pro" : "flash";
// 检查工单系统是否已连接
const ticketSystemConnected = extensions.find((e) => e.id === "ticket")?.connected ?? false;
@@ -200,7 +203,7 @@ export function GeminiChat() {
text,
streamConvId,
Array.from(activeTools),
selectedModel,
apiModel,
(event) => {
if (event.type === "token" && event.content) {
setConversations((prev) =>
@@ -244,7 +247,7 @@ export function GeminiChat() {
setIsLoading(false);
}
);
}, [inputValue, isLoading, activeConvId, activeTools, selectedModel]);
}, [inputValue, isLoading, activeConvId, activeTools, apiModel]);
const handleRegenerate = useCallback(
async (msgId: string) => {
@@ -283,7 +286,7 @@ export function GeminiChat() {
userMsg.content,
regenConvId,
Array.from(activeTools),
selectedModel,
apiModel,
(event) => {
if (event.type === "token" && event.content) {
setConversations((prev) =>
@@ -309,7 +312,7 @@ export function GeminiChat() {
}
);
},
[activeConvId, conversations, isLoading, activeTools, selectedModel]
[activeConvId, conversations, isLoading, activeTools, apiModel]
);
const handleSuggestionClick = useCallback((text: string) => {
+14 -36
View File
@@ -10,7 +10,6 @@ import {
Database,
FileText,
Code2,
X,
} from "lucide-react";
import { cn } from "@/lib/utils";
@@ -27,8 +26,8 @@ interface GeminiInputProps {
isLoading?: boolean;
activeTools: Set<string>;
onActiveToolsChange: (tools: Set<string>) => void;
selectedModel: "flash" | "pro";
onSelectedModelChange: (model: "flash" | "pro") => void;
selectedModel: "flash" | "auto" | "pro";
onSelectedModelChange: (model: "flash" | "auto" | "pro") => void;
}
const TOOLS: Tool[] = [
@@ -77,12 +76,6 @@ export function GeminiInput({
onActiveToolsChange(next);
};
const removeTool = (id: string) => {
const next = new Set(activeTools);
next.delete(id);
onActiveToolsChange(next);
};
const canSend = value.trim().length > 0 && !isLoading;
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -98,33 +91,6 @@ export function GeminiInput({
<div className="max-w-3xl mx-auto px-4 pointer-events-auto">
{/* Input container */}
<div className="bg-[var(--gem-surface)] border border-[var(--gem-border)] rounded-3xl shadow-lg focus-within:border-[var(--gem-border-hover)] transition-colors duration-150">
{/* Selected tools chips (auto show when has active tools) */}
{activeTools.size > 0 && (
<div className="flex items-center gap-2 px-4 pt-3 pb-1 flex-wrap">
{Array.from(activeTools).map((toolId) => {
const tool = TOOLS.find((t) => t.id === toolId);
if (!tool) return null;
const Icon = tool.icon;
return (
<span
key={tool.id}
className="flex items-center gap-2 px-2.5 py-1.5 rounded-lg text-xs font-medium bg-[var(--gem-blue)] text-white border border-[var(--gem-blue)]"
>
<Icon size={14} />
<span>{tool.label}</span>
<button
onClick={() => removeTool(tool.id)}
className="ml-1 hover:opacity-80 transition-opacity cursor-pointer"
aria-label={`移除${tool.label}`}
>
<X size={12} />
</button>
</span>
);
})}
</div>
)}
{/* Main input row */}
<div className="flex items-end gap-2 px-4 py-3">
{/* Left: File upload button */}
@@ -218,6 +184,18 @@ export function GeminiInput({
>
Flash
</button>
<button
onClick={() => onSelectedModelChange("auto")}
className={cn(
"px-3 py-1.5 rounded-full text-xs font-medium transition-all duration-150 cursor-pointer",
selectedModel === "auto"
? "bg-[var(--gem-blue)] text-white"
: "bg-[var(--gem-surface-2)] text-[var(--gem-text-muted)] hover:bg-[var(--gem-surface-3)] hover:text-[var(--gem-text)]"
)}
aria-pressed={selectedModel === "auto"}
>
Auto
</button>
<button
onClick={() => onSelectedModelChange("pro")}
className={cn(