Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 5 additions & 51 deletions frontend/src/components/ModelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
ChevronLeft,
Sparkles,
Zap,
Brain,
Code,
Image
} from "lucide-react";
Expand Down Expand Up @@ -68,13 +67,6 @@ export const MODEL_CONFIG: Record<string, ModelCfg> = {
requiresPro: true,
tokenLimit: 130000
},
"kimi-k2-thinking": {
displayName: "Kimi K2 Thinking",
shortName: "Kimi K2",
badges: ["Pro", "Reasoning"],
requiresPro: true,
tokenLimit: 256000
},
"kimi-k2-5": {
displayName: "Kimi K2.5",
shortName: "Kimi K2.5",
Expand Down Expand Up @@ -108,13 +100,11 @@ export function getModelTokenLimit(modelId: string): number {
}

// Model categories for simplified UI
type ModelCategory = "free" | "quick" | "reasoning" | "math" | "image" | "advanced";
type ModelCategory = "free" | "quick" | "math" | "image" | "advanced";

export const CATEGORY_MODELS = {
free: "llama-3.3-70b",
quick: "gpt-oss-120b",
reasoning_on: "kimi-k2-thinking", // Kimi K2 with thinking
reasoning_off: "deepseek-r1-0528", // DeepSeek R1 without thinking
math: "kimi-k2-5",
image: "qwen3-vl-30b" // Qwen3-VL for image analysis
};
Expand All @@ -130,11 +120,6 @@ const CATEGORY_INFO = {
icon: Zap,
description: "Fastest responses"
},
reasoning: {
label: "Reasoning",
icon: Brain,
description: "Deep analysis"
},
math: {
label: "Math/Coding",
icon: Code,
Expand All @@ -154,9 +139,7 @@ export function ModelSelector({ hasImages = false }: { hasImages?: boolean }) {
availableModels,
setAvailableModels,
billingStatus,
setHasWhisperModel,
thinkingEnabled,
setThinkingEnabled
setHasWhisperModel
} = useLocalState();
const os = useOpenSecret();
const isFetching = useRef(false);
Expand Down Expand Up @@ -243,16 +226,6 @@ export function ModelSelector({ hasImages = false }: { hasImages?: boolean }) {
}
}, [os, setAvailableModels, setHasWhisperModel]);

// Sync thinking toggle when model changes externally
useEffect(() => {
if (model === CATEGORY_MODELS.reasoning_on) {
setThinkingEnabled(true);
} else if (model === CATEGORY_MODELS.reasoning_off) {
setThinkingEnabled(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [model]);

// Auto-switch to image analysis when images are uploaded
useEffect(() => {
if (chatHasImages && hasAccessToModel(CATEGORY_MODELS.image)) {
Expand All @@ -269,9 +242,6 @@ export function ModelSelector({ hasImages = false }: { hasImages?: boolean }) {
const getCurrentCategory = (): string => {
if (model === CATEGORY_MODELS.free) return "General";
if (model === CATEGORY_MODELS.quick) return "Quick";
if (model === CATEGORY_MODELS.reasoning_on || model === CATEGORY_MODELS.reasoning_off) {
return "Reasoning";
}
if (model === CATEGORY_MODELS.math) return "Math/Coding";
if (model === CATEGORY_MODELS.image) return "Image Analysis";
// If in advanced mode, show model name
Expand Down Expand Up @@ -313,15 +283,7 @@ export function ModelSelector({ hasImages = false }: { hasImages?: boolean }) {
return;
}

let targetModel: string;
if (category === "reasoning") {
// Use thinking state to pick R1 vs V3.1
targetModel = thinkingEnabled ? CATEGORY_MODELS.reasoning_on : CATEGORY_MODELS.reasoning_off;
} else if (category === "image") {
targetModel = CATEGORY_MODELS.image;
} else {
targetModel = CATEGORY_MODELS[category as keyof typeof CATEGORY_MODELS];
}
const targetModel = CATEGORY_MODELS[category as keyof typeof CATEGORY_MODELS];

// Prevent switching to non-vision models if chat has images
const targetModelConfig = MODEL_CONFIG[targetModel];
Expand Down Expand Up @@ -441,25 +403,17 @@ export function ModelSelector({ hasImages = false }: { hasImages?: boolean }) {
{!showAdvanced ? (
<div className="p-1 h-[300px] flex flex-col">
{/* Category options */}
{(["free", "quick", "reasoning", "math", "image"] as const).map((category) => {
{(["free", "quick", "math", "image"] as const).map((category) => {
const info = CATEGORY_INFO[category];
const Icon = info.icon;
const isActive =
(category === "free" && model === CATEGORY_MODELS.free) ||
(category === "quick" && model === CATEGORY_MODELS.quick) ||
(category === "reasoning" &&
(model === CATEGORY_MODELS.reasoning_on ||
model === CATEGORY_MODELS.reasoning_off)) ||
(category === "math" && model === CATEGORY_MODELS.math) ||
(category === "image" && model === CATEGORY_MODELS.image);

// Check if user has access to this category's model
const targetModel =
category === "reasoning"
? thinkingEnabled
? CATEGORY_MODELS.reasoning_on
: CATEGORY_MODELS.reasoning_off
: CATEGORY_MODELS[category];
const targetModel = CATEGORY_MODELS[category];
const hasAccess = hasAccessToModel(targetModel);
const targetModelConfig = MODEL_CONFIG[targetModel];
const requiresUpgrade = !hasAccess;
Expand Down
71 changes: 1 addition & 70 deletions frontend/src/components/UnifiedChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {
Search,
Loader2,
Globe,
Brain,
Maximize2,
Minimize2,
Volume2,
Expand All @@ -54,7 +53,7 @@ import { truncateMarkdownPreservingLinks } from "@/utils/markdown";
import { useOpenAI } from "@/ai/useOpenAi";
import { DEFAULT_MODEL_ID } from "@/state/LocalStateContext";
import { Markdown, ThinkingBlock } from "@/components/markdown";
import { ModelSelector, CATEGORY_MODELS } from "@/components/ModelSelector";
import { ModelSelector } from "@/components/ModelSelector";
import { useLocalState } from "@/state/useLocalState";
import { useOpenSecret } from "@opensecret/react";
import { UpgradePromptDialog } from "@/components/UpgradePromptDialog";
Expand Down Expand Up @@ -2919,40 +2918,6 @@ export function UnifiedChat() {
}
/>

{/* Thinking toggle button - visible when reasoning model is selected */}
{(localState.model === CATEGORY_MODELS.reasoning_on ||
localState.model === CATEGORY_MODELS.reasoning_off) && (
<Button
type="button"
variant="ghost"
size="sm"
className="h-8 w-8 p-0"
onClick={() => {
const newThinkingEnabled = !localState.thinkingEnabled;
localState.setThinkingEnabled(newThinkingEnabled);
// Switch between Kimi K2 (with thinking) and DeepSeek R1 (without)
localState.setModel(
newThinkingEnabled
? CATEGORY_MODELS.reasoning_on
: CATEGORY_MODELS.reasoning_off
);
}}
aria-label={
localState.thinkingEnabled
? "Disable thinking mode"
: "Enable thinking mode"
}
>
<Brain
className={`h-4 w-4 ${
localState.thinkingEnabled
? "text-purple-500"
: "text-muted-foreground"
}`}
/>
</Button>
)}

{/* Web search toggle button - always visible */}
<Button
type="button"
Expand Down Expand Up @@ -3196,40 +3161,6 @@ export function UnifiedChat() {
}
/>

{/* Thinking toggle button - visible when reasoning model is selected */}
{(localState.model === CATEGORY_MODELS.reasoning_on ||
localState.model === CATEGORY_MODELS.reasoning_off) && (
<Button
type="button"
variant="ghost"
size="sm"
className="h-8 w-8 p-0"
onClick={() => {
const newThinkingEnabled = !localState.thinkingEnabled;
localState.setThinkingEnabled(newThinkingEnabled);
// Switch between Kimi K2 (with thinking) and DeepSeek R1 (without)
localState.setModel(
newThinkingEnabled
? CATEGORY_MODELS.reasoning_on
: CATEGORY_MODELS.reasoning_off
);
}}
aria-label={
localState.thinkingEnabled
? "Disable thinking mode"
: "Enable thinking mode"
}
>
<Brain
className={`h-4 w-4 ${
localState.thinkingEnabled
? "text-purple-500"
: "text-muted-foreground"
}`}
/>
</Button>
)}

{/* Web search toggle button - always visible */}
<Button
type="button"
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,10 @@ export function aliasModelName(modelName: string | undefined): string {
return "kimi-k2-5";
}

// Backwards compatibility: alias old k2-thinking to k2.5
if (modelName === "kimi-k2-thinking") {
return "kimi-k2-5";
}

return modelName;
}
Loading