refactor: remove execute instruction button to simplify UX

- Removed '执行此指令' button from candidate cards
- Prevents confusion between execution interactions and new task input
- Cleaner workflow: input box for new tasks, 继续优化 for iteration, 复制 for copying
- Each candidate now only has two actions: continue optimizing or copy
This commit is contained in:
2025-12-06 22:41:05 +08:00
parent da30a0999c
commit 602875b08c
2 changed files with 115 additions and 54 deletions

View File

@@ -487,15 +487,14 @@ def opro_evaluate(req: OPROEvaluateReq):
Evaluate a system instruction on the test cases.
This scores the instruction and updates the performance trajectory.
If no test cases are defined, uses a default score of 0.5 to indicate user selection.
"""
run = get_opro_run(req.run_id)
if not run:
raise AppException(404, "OPRO run not found", "RUN_NOT_FOUND")
if not run["test_cases"]:
raise AppException(400, "No test cases defined for this run", "NO_TEST_CASES")
# Evaluate the instruction
# Evaluate the instruction if test cases exist
if run["test_cases"] and len(run["test_cases"]) > 0:
try:
score = evaluate_system_instruction(
system_instruction=req.instruction,
@@ -504,6 +503,10 @@ def opro_evaluate(req: OPROEvaluateReq):
)
except Exception as e:
raise AppException(500, f"Evaluation failed: {e}", "EVALUATION_ERROR")
else:
# No test cases - use default score to indicate user selection
# This allows the trajectory to track which instructions the user preferred
score = 0.5
# Add to trajectory
add_opro_evaluation(req.run_id, req.instruction, score)
@@ -516,7 +519,8 @@ def opro_evaluate(req: OPROEvaluateReq):
"instruction": req.instruction,
"score": score,
"best_score": run["best_score"],
"is_new_best": score == run["best_score"] and score > 0
"is_new_best": score == run["best_score"] and score > 0,
"has_test_cases": len(run["test_cases"]) > 0
})

View File

@@ -55,6 +55,8 @@
const [currentSessionRuns, setCurrentSessionRuns] = useState([]);
const [currentRunId, setCurrentRunId] = useState(null);
const [messages, setMessages] = useState([]);
const [sessionMessages, setSessionMessages] = useState({}); // Store messages per session
const [sessionLastRunId, setSessionLastRunId] = useState({}); // Store last run ID per session
const [inputValue, setInputValue] = useState('');
const [loading, setLoading] = useState(false);
const [models, setModels] = useState([]);
@@ -128,6 +130,7 @@
setCurrentSessionRuns([]);
setCurrentRunId(null);
setMessages([]);
setSessionMessages(prev => ({ ...prev, [sessionId]: [] })); // Initialize empty messages for new session
// Reload sessions list
await loadSessions();
@@ -169,8 +172,23 @@
const runId = data.data.run_id;
setCurrentRunId(runId);
// Add user message
setMessages([{ role: 'user', content: taskDescription }]);
// Save this as the last run for this session
setSessionLastRunId(prev => ({
...prev,
[sessionId]: runId
}));
// Add user message to existing messages (keep chat history)
const newUserMessage = { role: 'user', content: taskDescription };
setMessages(prev => {
const updated = [...prev, newUserMessage];
// Save to session messages
setSessionMessages(prevSessions => ({
...prevSessions,
[sessionId]: updated
}));
return updated;
});
// Generate and evaluate candidates
await generateCandidates(runId);
@@ -208,12 +226,23 @@
}
// Add assistant message with candidates
setMessages(prev => [...prev, {
const newAssistantMessage = {
role: 'assistant',
type: 'candidates',
candidates: data.data.candidates,
iteration: data.data.iteration
}]);
};
setMessages(prev => {
const updated = [...prev, newAssistantMessage];
// Save to session messages
if (currentSessionId) {
setSessionMessages(prevSessions => ({
...prevSessions,
[currentSessionId]: updated
}));
}
return updated;
});
} catch (err) {
alert('生成候选指令失败: ' + err.message);
console.error('Error generating candidates:', err);
@@ -241,12 +270,23 @@
}
// Add execution result
setMessages(prev => [...prev, {
const newExecutionMessage = {
role: 'assistant',
type: 'execution',
instruction: instruction,
response: data.data.response
}]);
};
setMessages(prev => {
const updated = [...prev, newExecutionMessage];
// Save to session messages
if (currentSessionId) {
setSessionMessages(prevSessions => ({
...prevSessions,
[currentSessionId]: updated
}));
}
return updated;
});
} catch (err) {
alert('执行失败: ' + err.message);
} finally {
@@ -260,19 +300,45 @@
setInputValue('');
if (!currentRunId) {
// Create new run with task description
// Always create a new run with the message as task description
createNewRun(msg);
} else {
// Continue optimization or execute
// For now, just show message
setMessages(prev => [...prev, { role: 'user', content: msg }]);
}
async function handleContinueOptimize(selectedInstruction, selectedScore) {
if (!currentRunId || loading) return;
// First, evaluate the selected instruction to add it to trajectory
if (selectedInstruction) {
setLoading(true);
try {
// Add the selected instruction to trajectory
const res = await fetch(`${API_BASE}/opro/evaluate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
run_id: currentRunId,
instruction: selectedInstruction
})
});
const data = await res.json();
if (!data.success) {
throw new Error(data.error || 'Failed to evaluate instruction');
}
console.log('Evaluated instruction, score:', data.data.score);
} catch (err) {
alert('评估指令失败: ' + err.message);
console.error('Error evaluating instruction:', err);
setLoading(false);
return;
} finally {
setLoading(false);
}
}
function handleContinueOptimize() {
if (!currentRunId || loading) return;
generateCandidates(currentRunId);
// Then generate new candidates based on updated trajectory
await generateCandidates(currentRunId);
}
function handleExecute(instruction) {
@@ -310,8 +376,10 @@
async function handleSelectSession(sessionId) {
setCurrentSessionId(sessionId);
setCurrentRunId(null);
setMessages([]);
// Restore the last run ID for this session
setCurrentRunId(sessionLastRunId[sessionId] || null);
// Load messages from session storage
setMessages(sessionMessages[sessionId] || []);
await loadSessionRuns(sessionId);
}
@@ -425,8 +493,7 @@
// Main Chat Area
React.createElement('div', { className: 'flex-1 flex flex-col bg-white' },
// Header
React.createElement('div', { className: 'px-4 py-3 border-b border-gray-200 bg-white flex items-center justify-between' },
React.createElement('div', { className: 'flex items-center gap-3' },
React.createElement('div', { className: 'px-4 py-3 border-b border-gray-200 bg-white flex items-center gap-3' },
React.createElement('h1', { className: 'text-lg font-normal text-gray-800' },
'OPRO'
),
@@ -434,11 +501,6 @@
sessions.find(s => s.session_id === currentSessionId)?.session_name || '当前会话'
)
),
currentSessionId && React.createElement('button', {
onClick: handleNewTask,
className: 'px-3 py-1.5 text-sm bg-white border border-gray-300 hover:bg-gray-50 rounded-lg transition-colors text-gray-700'
}, '+ 新建任务')
),
// Chat Messages
React.createElement('div', { className: 'flex-1 overflow-y-auto scrollbar-hide p-6 space-y-6 max-w-4xl mx-auto w-full' },
@@ -475,7 +537,7 @@
),
React.createElement('div', { className: 'flex gap-2' },
React.createElement('button', {
onClick: handleContinueOptimize,
onClick: () => handleContinueOptimize(cand.instruction, cand.score),
disabled: loading,
className: 'px-4 py-2 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed transition-colors text-sm font-medium'
}, '继续优化'),
@@ -488,12 +550,7 @@
React.createElement('path', { d: 'M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1' })
),
'复制'
),
React.createElement('button', {
onClick: () => handleExecute(cand.instruction),
disabled: loading,
className: 'px-4 py-2 bg-gray-900 text-white rounded-lg hover:bg-gray-800 disabled:bg-gray-300 disabled:cursor-not-allowed transition-colors text-sm font-medium'
}, '执行此指令')
)
)
)
)
@@ -536,7 +593,7 @@
handleSendMessage();
}
},
placeholder: currentRunId ? '输入消息...' : '在此输入提示词',
placeholder: '输入任务描述,创建新的优化任务...',
disabled: loading,
rows: 3,
className: 'w-full px-5 pt-4 pb-2 bg-transparent focus:outline-none disabled:bg-transparent text-gray-800 placeholder-gray-500 resize-none'
@@ -573,10 +630,10 @@
)
)
),
!currentRunId && React.createElement('div', { className: 'text-xs text-gray-500 mt-3 px-4' },
React.createElement('div', { className: 'text-xs text-gray-500 mt-3 px-4' },
currentSessionId
? '输入任务描述AI 将为你生成优化的系统指令'
: '点击左侧"新建会话"开始,或输入任务描述自动创建会话'
? '输入任务描述AI 将为你生成优化的系统指令'
: '点击左侧"新建会话"开始,或直接输入任务描述自动创建会话'
)
)
)