Skip to content

Commit 7cb29ae

Browse files
committed
✨Agent configuration page logic optimization #1711
[Specification Detail] 1.Agent errors prevent saving. 2.Complete the test cases.
1 parent 742640d commit 7cb29ae

File tree

2 files changed

+129
-1
lines changed

2 files changed

+129
-1
lines changed

frontend/app/[locale]/agents/components/agent/AgentConfigModal.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,12 +542,20 @@ export default function AgentConfigModal({
542542
const shouldCheckNameStatus = isCreatingNewAgent || currentAgentName !== originalAgentName;
543543
const shouldCheckDisplayNameStatus = isCreatingNewAgent || currentDisplayName !== originalDisplayName;
544544

545+
// Disable save if there are any error indicators from backend (unavailable_reasons)
546+
// These errors should block saving even if names haven't changed
547+
const hasBackendErrors =
548+
shouldShowDuplicateNameReason ||
549+
shouldShowDuplicateDisplayNameReason ||
550+
shouldShowModelUnavailableReason;
551+
545552
const canActuallySave =
546553
canSaveAgent &&
547554
!agentNameError &&
548555
(shouldCheckNameStatus ? agentNameStatus !== NAME_CHECK_STATUS.EXISTS_IN_TENANT : true) &&
549556
!agentDisplayNameError &&
550-
(shouldCheckDisplayNameStatus ? agentDisplayNameStatus !== NAME_CHECK_STATUS.EXISTS_IN_TENANT : true);
557+
(shouldCheckDisplayNameStatus ? agentDisplayNameStatus !== NAME_CHECK_STATUS.EXISTS_IN_TENANT : true) &&
558+
!hasBackendErrors;
551559

552560
// Render individual content sections
553561
const renderAgentInfo = () => (

test/backend/services/test_agent_service.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ def _mock_context():
8282
run_agent_stream,
8383
stop_agent_tasks,
8484
_resolve_user_tenant_language,
85+
_apply_duplicate_name_availability_rules,
86+
_check_single_model_availability,
8587
)
8688
from consts.model import ExportAndImportAgentInfo, ExportAndImportDataFormat, MCPInfo, AgentRequest
8789

@@ -3968,6 +3970,67 @@ async def test_list_all_agent_info_impl_all_disabled_agents():
39683970
mock_check_tools.assert_not_called()
39693971

39703972

3973+
def test_apply_duplicate_name_availability_rules_handles_missing_fields():
3974+
"""
3975+
Ensure duplicate detection gracefully handles agents without name/display_name.
3976+
"""
3977+
enriched_agents = [
3978+
{
3979+
"raw_agent": {
3980+
"agent_id": 1,
3981+
"name": None,
3982+
"display_name": None,
3983+
"create_time": "2024-01-01T00:00:00",
3984+
},
3985+
"unavailable_reasons": [],
3986+
},
3987+
{
3988+
"raw_agent": {
3989+
"agent_id": 2,
3990+
"name": "dup",
3991+
"display_name": None,
3992+
"create_time": "2024-01-01T00:00:00",
3993+
},
3994+
"unavailable_reasons": [],
3995+
},
3996+
{
3997+
"raw_agent": {
3998+
"agent_id": 3,
3999+
"name": "dup",
4000+
"display_name": None,
4001+
"create_time": "2024-02-01T00:00:00",
4002+
},
4003+
"unavailable_reasons": [],
4004+
},
4005+
{
4006+
"raw_agent": {
4007+
"agent_id": 4,
4008+
"name": None,
4009+
"display_name": "display-dup",
4010+
"create_time": "2024-01-01T00:00:00",
4011+
},
4012+
"unavailable_reasons": [],
4013+
},
4014+
{
4015+
"raw_agent": {
4016+
"agent_id": 5,
4017+
"name": None,
4018+
"display_name": "display-dup",
4019+
"create_time": "2024-02-01T00:00:00",
4020+
},
4021+
"unavailable_reasons": [],
4022+
},
4023+
]
4024+
4025+
_apply_duplicate_name_availability_rules(enriched_agents)
4026+
4027+
assert enriched_agents[0]["unavailable_reasons"] == []
4028+
assert "duplicate_name" not in enriched_agents[1]["unavailable_reasons"]
4029+
assert "duplicate_name" in enriched_agents[2]["unavailable_reasons"]
4030+
assert "duplicate_display_name" not in enriched_agents[3]["unavailable_reasons"]
4031+
assert "duplicate_display_name" in enriched_agents[4]["unavailable_reasons"]
4032+
4033+
39714034
# ============================================================================
39724035
# Tests for Agent Export/Import Integration with model_name fields
39734036
# ============================================================================
@@ -5420,3 +5483,60 @@ async def test_resolve_model_quick_config_exception(
54205483
model_label="Model",
54215484
tenant_id="tenant_011"
54225485
)
5486+
5487+
5488+
def test_check_single_model_availability_no_model_id():
5489+
reasons = _check_single_model_availability(
5490+
model_id=None,
5491+
tenant_id="tenant",
5492+
model_cache={},
5493+
reason_key="model_unavailable",
5494+
)
5495+
assert reasons == []
5496+
5497+
5498+
@patch("backend.services.agent_service.get_model_by_model_id")
5499+
def test_check_single_model_availability_fetches_and_handles_missing_model(mock_get_model):
5500+
model_cache = {}
5501+
mock_get_model.return_value = None
5502+
5503+
reasons = _check_single_model_availability(
5504+
model_id=123,
5505+
tenant_id="tenant",
5506+
model_cache=model_cache,
5507+
reason_key="model_unavailable",
5508+
)
5509+
5510+
assert reasons == ["model_unavailable"]
5511+
assert 123 in model_cache
5512+
mock_get_model.assert_called_once_with(123, "tenant")
5513+
5514+
5515+
def test_check_single_model_availability_uses_cached_unavailable_model():
5516+
model_cache = {
5517+
456: {"connect_status": agent_service.ModelConnectStatusEnum.UNAVAILABLE.value}
5518+
}
5519+
5520+
reasons = _check_single_model_availability(
5521+
model_id=456,
5522+
tenant_id="tenant",
5523+
model_cache=model_cache,
5524+
reason_key="model_unavailable",
5525+
)
5526+
5527+
assert reasons == ["model_unavailable"]
5528+
5529+
5530+
def test_check_single_model_availability_returns_empty_for_available_model():
5531+
model_cache = {
5532+
789: {"connect_status": agent_service.ModelConnectStatusEnum.AVAILABLE.value}
5533+
}
5534+
5535+
reasons = _check_single_model_availability(
5536+
model_id=789,
5537+
tenant_id="tenant",
5538+
model_cache=model_cache,
5539+
reason_key="model_unavailable",
5540+
)
5541+
5542+
assert reasons == []

0 commit comments

Comments
 (0)