Skip to content

Commit e02da79

Browse files
authored
🐛 generate button in Agent config page returns error: Empty agent ID in creation mode
2 parents 5bb9ff7 + 41cc466 commit e02da79

File tree

4 files changed

+126
-43
lines changed

4 files changed

+126
-43
lines changed

backend/services/prompt_service.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,15 @@ def generate_and_save_system_prompt_impl(agent_id: int,
118118
tenant_id: str,
119119
language: str):
120120
# Get description of tool and agent
121-
tool_info_list = get_enabled_tool_description_for_generate_prompt(
122-
tenant_id=tenant_id, agent_id=agent_id)
123-
sub_agent_info_list = get_enabled_sub_agent_description_for_generate_prompt(
124-
tenant_id=tenant_id, agent_id=agent_id)
121+
# In create mode (agent_id=0), return empty lists
122+
if agent_id == 0:
123+
tool_info_list = []
124+
sub_agent_info_list = []
125+
else:
126+
tool_info_list = get_enabled_tool_description_for_generate_prompt(
127+
tenant_id=tenant_id, agent_id=agent_id)
128+
sub_agent_info_list = get_enabled_sub_agent_description_for_generate_prompt(
129+
tenant_id=tenant_id, agent_id=agent_id)
125130

126131
# 1. Real-time streaming push
127132
final_results = {"duty": "", "constraint": "", "few_shots": "", "agent_var_name": "", "agent_display_name": "",
@@ -132,25 +137,29 @@ def generate_and_save_system_prompt_impl(agent_id: int,
132137
final_results[result_data["type"]] = result_data["content"]
133138
yield result_data
134139

135-
# 2. Update agent with the final result
136-
logger.info("Updating agent with business_description and prompt segments")
137-
agent_info = AgentInfoRequest(
138-
agent_id=agent_id,
139-
business_description=task_description,
140-
duty_prompt=final_results["duty"],
141-
constraint_prompt=final_results["constraint"],
142-
few_shots_prompt=final_results["few_shots"],
143-
name=final_results["agent_var_name"],
144-
display_name=final_results["agent_display_name"],
145-
description=final_results["agent_description"]
146-
)
147-
update_agent(
148-
agent_id=agent_id,
149-
agent_info=agent_info,
150-
tenant_id=tenant_id,
151-
user_id=user_id
152-
)
153-
logger.info("Prompt generation and agent update completed successfully")
140+
# 2. Update agent with the final result (skip in create mode)
141+
if agent_id == 0:
142+
logger.info("Skipping agent update in create mode (agent_id=0)")
143+
else:
144+
logger.info(
145+
"Updating agent with business_description and prompt segments")
146+
agent_info = AgentInfoRequest(
147+
agent_id=agent_id,
148+
business_description=task_description,
149+
duty_prompt=final_results["duty"],
150+
constraint_prompt=final_results["constraint"],
151+
few_shots_prompt=final_results["few_shots"],
152+
name=final_results["agent_var_name"],
153+
display_name=final_results["agent_display_name"],
154+
description=final_results["agent_description"]
155+
)
156+
update_agent(
157+
agent_id=agent_id,
158+
agent_info=agent_info,
159+
tenant_id=tenant_id,
160+
user_id=user_id
161+
)
162+
logger.info("Prompt generation and agent update completed successfully")
154163

155164

156165
def generate_system_prompt(sub_agent_info_list, task_description, tool_info_list, tenant_id: str, model_id: int, language: str = LANGUAGE["ZH"]):

frontend/app/[locale]/setup/agents/components/AgentSetupOrchestrator.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,11 @@ export default function AgentSetupOrchestrator({
868868

869869
// Ensure tool pool won't show loading state
870870
setIsLoadingTools(false);
871+
872+
// Reset unsaved state and baseline when explicitly exiting edit mode
873+
baselineRef.current = null;
874+
setHasUnsavedChanges(false);
875+
onUnsavedChange?.(false);
871876
}
872877
};
873878

frontend/app/[locale]/setup/agents/config.tsx

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,12 @@ export default forwardRef<AgentConfigHandle, AgentConfigProps>(function AgentCon
146146
// Clear error when validation passes
147147
setBusinessLogicError(false);
148148

149+
// In create mode, agent_id should be 0 (backend will handle this case)
150+
// In edit mode, use the current agent id
149151
const currentAgentId = getCurrentAgentId();
150-
if (!currentAgentId) {
152+
const agentIdToUse = isCreatingNewAgent ? 0 : currentAgentId || 0;
153+
154+
if (!isCreatingNewAgent && !currentAgentId) {
151155
message.error(t("businessLogic.config.error.noAgentId"));
152156
return;
153157
}
@@ -160,7 +164,7 @@ export default forwardRef<AgentConfigHandle, AgentConfigProps>(function AgentCon
160164
// Call backend API to generate agent prompt
161165
await generatePromptStream(
162166
{
163-
agent_id: Number(currentAgentId),
167+
agent_id: agentIdToUse,
164168
task_description: businessLogic,
165169
model_id: selectedModel?.id?.toString() || "",
166170
},
@@ -406,29 +410,35 @@ export default forwardRef<AgentConfigHandle, AgentConfigProps>(function AgentCon
406410

407411
// Memoize event handler to avoid recreating listener
408412
const handleGetAgentConfigData = useCallback(() => {
409-
// Check if there is system prompt content
410-
let hasSystemPrompt = false;
413+
// Check if there is system prompt content
414+
let hasSystemPrompt = false;
411415

412-
// If any of the segmented prompts has content, consider it as having system prompt
416+
// If any of the segmented prompts has content, consider it as having system prompt
413417
if (dutyContentRef.current && dutyContentRef.current.trim() !== "") {
414-
hasSystemPrompt = true;
415-
} else if (constraintContentRef.current && constraintContentRef.current.trim() !== "") {
416-
hasSystemPrompt = true;
417-
} else if (fewShotsContentRef.current && fewShotsContentRef.current.trim() !== "") {
418-
hasSystemPrompt = true;
419-
}
418+
hasSystemPrompt = true;
419+
} else if (
420+
constraintContentRef.current &&
421+
constraintContentRef.current.trim() !== ""
422+
) {
423+
hasSystemPrompt = true;
424+
} else if (
425+
fewShotsContentRef.current &&
426+
fewShotsContentRef.current.trim() !== ""
427+
) {
428+
hasSystemPrompt = true;
429+
}
420430

421-
// Send the current configuration data to the main page
422-
const eventData: AgentConfigDataResponse = {
431+
// Send the current configuration data to the main page
432+
const eventData: AgentConfigDataResponse = {
423433
businessLogic: businessLogicRef.current,
424-
systemPrompt: hasSystemPrompt ? "has_content" : "",
425-
};
434+
systemPrompt: hasSystemPrompt ? "has_content" : "",
435+
};
426436

427-
window.dispatchEvent(
428-
new CustomEvent("agentConfigDataResponse", {
429-
detail: eventData,
430-
}) as AgentConfigCustomEvent
431-
);
437+
window.dispatchEvent(
438+
new CustomEvent("agentConfigDataResponse", {
439+
detail: eventData,
440+
}) as AgentConfigCustomEvent
441+
);
432442
}, []); // Empty deps - handler uses refs for latest values
433443

434444
// Add event listener to respond to the data request from the main page
@@ -462,9 +472,15 @@ export default forwardRef<AgentConfigHandle, AgentConfigProps>(function AgentCon
462472
};
463473

464474
const getCurrentAgentId = () => {
475+
// In edit mode, always use the currently editing agent's id
465476
if (isEditingAgent && editingAgent) {
466477
return parseInt(editingAgent.id);
467478
}
479+
// In create mode, the agent has not been persisted yet, so there should be no agent_id
480+
if (isCreatingNewAgent) {
481+
return undefined;
482+
}
483+
// Fallback to mainAgentId when not creating and not explicitly editing
468484
return mainAgentId ? parseInt(mainAgentId) : undefined;
469485
};
470486

test/backend/services/test_prompt_service.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,59 @@ def mock_generator(*args, **kwargs):
161161
self.assertEqual(agent_info.agent_id, 123)
162162
self.assertEqual(agent_info.business_description, "Test task")
163163

164+
@patch('backend.services.prompt_service.generate_system_prompt')
165+
@patch('backend.services.prompt_service.get_enabled_sub_agent_description_for_generate_prompt')
166+
@patch('backend.services.prompt_service.get_enabled_tool_description_for_generate_prompt')
167+
@patch('backend.services.prompt_service.update_agent')
168+
def test_generate_and_save_system_prompt_impl_create_mode(self, mock_update_agent, mock_get_tool_desc,
169+
mock_get_agent_desc, mock_generate_system_prompt):
170+
"""Test generate_and_save_system_prompt_impl in create mode (agent_id=0)"""
171+
# Setup - Mock the generator to return the expected data structure
172+
def mock_generator(*args, **kwargs):
173+
yield {"type": "duty", "content": "Generated duty prompt", "is_complete": False}
174+
yield {"type": "constraint", "content": "Generated constraint prompt", "is_complete": False}
175+
yield {"type": "few_shots", "content": "Generated few shots prompt", "is_complete": False}
176+
yield {"type": "agent_var_name", "content": "test_agent", "is_complete": True}
177+
yield {"type": "agent_display_name", "content": "Test Agent", "is_complete": True}
178+
yield {"type": "agent_description", "content": "Test agent description", "is_complete": True}
179+
yield {"type": "duty", "content": "Final duty prompt", "is_complete": True}
180+
yield {"type": "constraint", "content": "Final constraint prompt", "is_complete": True}
181+
yield {"type": "few_shots", "content": "Final few shots prompt", "is_complete": True}
182+
183+
mock_generate_system_prompt.side_effect = mock_generator
184+
185+
# Execute - test as a generator with agent_id=0 (create mode)
186+
result_gen = generate_and_save_system_prompt_impl(
187+
agent_id=0,
188+
model_id=self.test_model_id,
189+
task_description="Test task",
190+
user_id="user123",
191+
tenant_id="tenant456",
192+
language="zh"
193+
)
194+
result = list(result_gen) # Convert generator to list for assertion
195+
196+
# Assert
197+
self.assertGreater(len(result), 0)
198+
199+
# In create mode, should NOT call get_enabled_tool_description_for_generate_prompt
200+
# and get_enabled_sub_agent_description_for_generate_prompt
201+
mock_get_tool_desc.assert_not_called()
202+
mock_get_agent_desc.assert_not_called()
203+
204+
# Should call generate_system_prompt with empty lists for tools and sub-agents
205+
mock_generate_system_prompt.assert_called_once_with(
206+
[], # Empty sub_agent_info_list
207+
"Test task",
208+
[], # Empty tool_info_list
209+
"tenant456",
210+
self.test_model_id,
211+
"zh"
212+
)
213+
214+
# In create mode, should NOT call update_agent
215+
mock_update_agent.assert_not_called()
216+
164217
@patch('backend.services.prompt_service.generate_and_save_system_prompt_impl')
165218
def test_gen_system_prompt_streamable(self, mock_generate_impl):
166219
"""Test gen_system_prompt_streamable function"""

0 commit comments

Comments
 (0)