Skip to content

Commit 4625d29

Browse files
committed
feat: add ReasoningEffortSelector component and integrate reasoning effort into chat functionality
- Introduced ReasoningEffortSelector for selecting reasoning effort levels (high, medium, low). - Updated ChatInput and ChatInterface components to include the new ReasoningEffortSelector. - Enhanced ModelStore to manage reasoning effort state. - Modified sessionManager and CodexConfig to accommodate reasoning effort in session handling. 🤖 Generated with [Claude Code](https://claude.ai/code)
1 parent e5694c0 commit 4625d29

File tree

9 files changed

+82
-4
lines changed

9 files changed

+82
-4
lines changed

src-tauri/src/codex_client/command_builder.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,14 @@ impl CommandBuilder {
199199
cmd.arg("-c").arg(sandbox_config);
200200
}
201201

202+
// Add reasoning effort parameter
203+
if let Some(reasoning_effort) = &config.reasoning_effort {
204+
if !reasoning_effort.is_empty() {
205+
cmd.arg("-c")
206+
.arg(format!("model_reasoning_effort={}", reasoning_effort));
207+
}
208+
}
209+
202210
// Enable streaming by setting show_raw_agent_reasoning=true
203211
// This is required for agent_message_delta events to be generated
204212
cmd.arg("-c").arg("show_raw_agent_reasoning=true");

src-tauri/src/protocol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,5 @@ pub struct CodexConfig {
151151
pub sandbox_mode: String,
152152
pub codex_path: Option<String>,
153153
pub api_key: Option<String>,
154+
pub reasoning_effort: Option<String>,
154155
}

src/components/chat/ChatInput.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Send, Square } from 'lucide-react';
55
import { useChatInputStore } from '@/stores/chatInputStore';
66
import { MediaSelector } from './MediaSelector';
77
import { ModelSelector } from './ModelSelector';
8+
import { ReasoningEffortSelector } from './ReasoningEffortSelector';
89
import { FileReferenceList } from './FileReferenceList';
910
import { MediaAttachmentList } from './MediaAttachmentList';
1011

@@ -127,9 +128,10 @@ export const ChatInput: React.FC<ChatInputProps> = ({
127128
<MediaSelector />
128129
</div>
129130

130-
{/* Model Selector and Send Button - bottom right inside textarea */}
131+
{/* Reasoning Effort, Model Selector and Send Button - bottom right inside textarea */}
131132
<div className="absolute right-2 bottom-2 flex items-center gap-1">
132133
<ModelSelector />
134+
<ReasoningEffortSelector />
133135
{isLoading ? (
134136
<Button
135137
onClick={handleStopStreaming}

src/components/chat/ChatInterface.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
3434
selectedConversation = null,
3535
}) => {
3636
const { inputValue, setInputValue } = useChatInputStore();
37-
const { currentModel, currentProvider } = useModelStore();
37+
const { currentModel, currentProvider, reasoningEffort } = useModelStore();
3838
const [pendingApproval, setPendingApproval] =
3939
useState<ApprovalRequest | null>(null);
4040
const [isConnected, setIsConnected] = useState(false);
@@ -152,6 +152,7 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
152152
model: currentModel,
153153
provider: currentProvider,
154154
useOss: currentProvider.toLowerCase() === "ollama",
155+
reasoningEffort,
155156
};
156157
await sessionManager.ensureSessionRunning(
157158
sessionId,
@@ -265,6 +266,7 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
265266
model: currentModel,
266267
provider: currentProvider,
267268
useOss: currentProvider.toLowerCase() === "ollama",
269+
reasoningEffort,
268270
};
269271
await sessionManager.ensureSessionRunning(
270272
actualSessionId,

src/components/chat/ModelSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export const ModelSelector: React.FC = () => {
106106
}, [modelsByProvider, searchTerm]);
107107

108108
return (
109-
<div className="flex items-center justify-between mt-2 pt-2 border-t border-gray-100">
109+
<div className="flex items-center justify-between border-t border-gray-100">
110110
<Popover open={isModelPopoverOpen} onOpenChange={(open) => {
111111
setIsModelPopoverOpen(open);
112112
if (!open) {
@@ -115,7 +115,7 @@ export const ModelSelector: React.FC = () => {
115115
}}>
116116
<PopoverTrigger asChild>
117117
<div className="flex items-center gap-2 text-xs text-gray-500">
118-
<Badge variant="outline" className="text-xs cursor-pointer hover:bg-gray-100">
118+
<Badge variant="outline" className="text-xs cursor-pointer hover:bg-gray-100 h-6 px-2">
119119
{currentProvider}/{currentModel}
120120
</Badge>
121121
</div>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React, { useState } from 'react';
2+
import { Badge } from '../ui/badge';
3+
import { Button } from '../ui/button';
4+
import { ChevronDown } from 'lucide-react';
5+
import {
6+
Popover,
7+
PopoverContent,
8+
PopoverTrigger,
9+
} from '../ui/popover';
10+
import { useModelStore } from '@/stores/ModelStore';
11+
12+
export const ReasoningEffortSelector: React.FC = () => {
13+
const { reasoningEffort, setReasoningEffort } = useModelStore();
14+
const [isOpen, setIsOpen] = useState(false);
15+
16+
const efforts = [
17+
{ value: 'high', label: 'High' },
18+
{ value: 'medium', label: 'Medium' },
19+
{ value: 'low', label: 'Low' },
20+
] as const;
21+
22+
return (
23+
<Popover open={isOpen} onOpenChange={setIsOpen}>
24+
<PopoverTrigger asChild>
25+
<Badge
26+
variant="outline"
27+
className="text-xs cursor-pointer hover:bg-gray-100 h-6 px-2 gap-1"
28+
>
29+
<span className="capitalize">{reasoningEffort}</span>
30+
<ChevronDown className="w-3 h-3" />
31+
</Badge>
32+
</PopoverTrigger>
33+
<PopoverContent className="w-36 p-2" align="start">
34+
<div className="space-y-1">
35+
<div className="text-xs font-medium text-gray-700 px-2 py-1">
36+
Reasoning Effort
37+
</div>
38+
{efforts.map((effort) => (
39+
<Button
40+
key={effort.value}
41+
variant={reasoningEffort === effort.value ? "default" : "ghost"}
42+
size="sm"
43+
className="w-full justify-start text-left p-2"
44+
onClick={() => {
45+
setReasoningEffort(effort.value);
46+
setIsOpen(false);
47+
}}
48+
>
49+
{effort.label}
50+
</Button>
51+
))}
52+
</div>
53+
</PopoverContent>
54+
</Popover>
55+
);
56+
};

src/services/sessionManager.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class SessionManager {
5353
approval_policy: config.approvalPolicy,
5454
sandbox_mode: config.sandboxMode,
5555
api_key: apiKey,
56+
reasoning_effort: config.reasoningEffort,
5657
},
5758
});
5859

src/stores/ModelStore.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
import { create } from "zustand";
22
import { persist } from "zustand/middleware";
33

4+
type ReasoningEffort = 'high' | 'medium' | 'low';
5+
46
interface ModelStore {
57
currentModel: string;
68
currentProvider: string;
9+
reasoningEffort: ReasoningEffort;
710
setCurrentModel: (model: string, provider: string) => void;
11+
setReasoningEffort: (effort: ReasoningEffort) => void;
812
}
913

1014
export const useModelStore = create<ModelStore>()(
1115
persist(
1216
(set) => ({
1317
currentModel: 'gpt-5',
1418
currentProvider: 'openai',
19+
reasoningEffort: 'medium',
1520
setCurrentModel: (model: string, provider: string) =>
1621
set({ currentModel: model, currentProvider: provider }),
22+
setReasoningEffort: (effort: ReasoningEffort) =>
23+
set({ reasoningEffort: effort }),
1724
}),
1825
{
1926
name: "model-storage",

src/types/codex.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export interface CodexConfig {
5959
approvalPolicy: 'untrusted' | 'on-failure' | 'on-request' | 'never';
6060
sandboxMode: 'read-only' | 'workspace-write' | 'danger-full-access';
6161
codexPath?: string;
62+
reasoningEffort?: 'high' | 'medium' | 'low';
6263
}
6364

6465
export const DEFAULT_CONFIG: CodexConfig = {

0 commit comments

Comments
 (0)