| import React from "react"; |
| import { Button } from "@/components/ui/button"; |
| import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; |
| import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; |
| import { Check, Brain } from "lucide-react"; |
| import { ChatCompletionReasoningEffort } from "openai/resources/chat/completions"; |
| import { PROVIDERS } from "@/lib/config/types"; |
|
|
| interface ReasoningEffortSelectorProps { |
| selectedReasoningEffort: ChatCompletionReasoningEffort | null | undefined; |
| provider: PROVIDERS; |
| isReasoning: boolean; |
| onReasoningEffortChange: (effort: ChatCompletionReasoningEffort) => void; |
| } |
|
|
| interface ReasoningOption { |
| value: ChatCompletionReasoningEffort; |
| label: string; |
| description: string; |
| } |
|
|
| |
| const REASONING_EFFORT_OPTIONS: Record<string, ReasoningOption[]> = { |
| [PROVIDERS.openai]: [ |
| { value: "low" as ChatCompletionReasoningEffort, label: "Low", description: "Minimal reasoning, faster responses" }, |
| { value: "medium" as ChatCompletionReasoningEffort, label: "Medium", description: "Balanced reasoning and speed" }, |
| { value: "high" as ChatCompletionReasoningEffort, label: "High", description: "Thorough reasoning, slower responses" } |
| ], |
| [PROVIDERS.anthropic]: [ |
| { value: "disabled" as ChatCompletionReasoningEffort, label: "Disabled", description: "No explicit reasoning" }, |
| { value: "low" as ChatCompletionReasoningEffort, label: "Low", description: "Minimal reasoning (~8K tokens)" }, |
| { value: "medium" as ChatCompletionReasoningEffort, label: "Medium", description: "Balanced reasoning (~16K tokens)" }, |
| { value: "high" as ChatCompletionReasoningEffort, label: "High", description: "Thorough reasoning (~32K tokens)" } |
| ] |
| }; |
|
|
| export const ReasoningEffortSelector = React.memo(({ |
| selectedReasoningEffort, |
| provider, |
| isReasoning, |
| onReasoningEffortChange |
| }: ReasoningEffortSelectorProps) => { |
| if (!isReasoning) return null; |
| |
| const options = REASONING_EFFORT_OPTIONS[provider] || REASONING_EFFORT_OPTIONS[PROVIDERS.openai]; |
| const selectedOption = options.find((opt: ReasoningOption) => opt.value === selectedReasoningEffort) || options[0]; |
|
|
| return ( |
| <DropdownMenu> |
| <DropdownMenuTrigger asChild> |
| <Button |
| variant="ghost" |
| className="h-8 p-1 justify-start font-normal flex items-center gap-1" |
| title="Reasoning Effort" |
| > |
| <Brain className="h-4 w-4" /> |
| <span className="truncate">{selectedOption.label}</span> |
| </Button> |
| </DropdownMenuTrigger> |
| <DropdownMenuContent className="w-[250px]"> |
| {options.map((option: ReasoningOption) => ( |
| <TooltipProvider key={option.value}> |
| <Tooltip> |
| <TooltipTrigger asChild> |
| <DropdownMenuItem |
| className="p-3" |
| onSelect={() => onReasoningEffortChange(option.value)} |
| > |
| <div className="flex items-center gap-2 w-full"> |
| {option.value === selectedReasoningEffort && ( |
| <Check className="h-4 w-4 shrink-0" /> |
| )} |
| <span className="flex-grow">{option.label}</span> |
| </div> |
| </DropdownMenuItem> |
| </TooltipTrigger> |
| <TooltipContent side="right" align="start" className="max-w-[300px]"> |
| <p>{option.description}</p> |
| </TooltipContent> |
| </Tooltip> |
| </TooltipProvider> |
| ))} |
| </DropdownMenuContent> |
| </DropdownMenu> |
| ); |
| }); |
|
|
| ReasoningEffortSelector.displayName = "ReasoningEffortSelector"; |