diff --git a/mateclaw-server/src/main/java/vip/mate/agent/AgentService.java b/mateclaw-server/src/main/java/vip/mate/agent/AgentService.java index 8c59deda..be92acb7 100644 --- a/mateclaw-server/src/main/java/vip/mate/agent/AgentService.java +++ b/mateclaw-server/src/main/java/vip/mate/agent/AgentService.java @@ -146,6 +146,11 @@ public class AgentService { public AgentEntity createAgent(AgentEntity agent) { agent.setEnabled(true); + // Match the runtime fallback before insert; older schemas default to 10. + // Explicit budgets (including 0 for no soft cap) remain user-controlled. + if (agent.getMaxIterations() == null) { + agent.setMaxIterations(100); + } if (agent.getAgentType() == null) { agent.setAgentType("react"); } diff --git a/mateclaw-server/src/test/java/vip/mate/agent/AgentServiceIterationBudgetTest.java b/mateclaw-server/src/test/java/vip/mate/agent/AgentServiceIterationBudgetTest.java new file mode 100644 index 00000000..fe21d893 --- /dev/null +++ b/mateclaw-server/src/test/java/vip/mate/agent/AgentServiceIterationBudgetTest.java @@ -0,0 +1,54 @@ +package vip.mate.agent; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import vip.mate.agent.model.AgentEntity; +import vip.mate.agent.repository.AgentMapper; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +class AgentServiceIterationBudgetTest { + private final AgentMapper mapper = mock(AgentMapper.class); + private final AgentService service = new AgentService(mapper, null, null, null, null, null, null); + + @Test + void omittedBudgetIsStoredAsOneHundredInsteadOfUsingDatabaseDefault() { + AgentEntity agent = new AgentEntity(); + agent.setName("Research assistant"); + doAnswer(invocation -> { + AgentEntity inserted = invocation.getArgument(0); + assertEquals(100, inserted.getMaxIterations()); + return 1; + }).when(mapper).insert(any(AgentEntity.class)); + + assertEquals(100, service.createAgent(agent).getMaxIterations()); + verify(mapper).insert(agent); + } + + @ParameterizedTest + @ValueSource(ints = {0, 10, 150}) + void explicitCreationBudgetIsPreserved(int budget) { + AgentEntity agent = new AgentEntity(); + agent.setName("Custom assistant"); + agent.setMaxIterations(budget); + assertEquals(budget, service.createAgent(agent).getMaxIterations()); + verify(mapper).insert(agent); + } + + @Test + void editingExistingAgentRetainsItsSmallBudget() { + AgentEntity agent = new AgentEntity(); + agent.setId(1L); + agent.setName("Existing assistant"); + agent.setMaxIterations(10); + agent.setDescription("Updated description"); + when(mapper.selectById(1L)).thenReturn(agent); + + service.updateAgent(agent); + + assertEquals(10, agent.getMaxIterations()); + verify(mapper).updateById(agent); + } +} diff --git a/mateclaw-ui/src/components/chat/MessageBubble.vue b/mateclaw-ui/src/components/chat/MessageBubble.vue index 5a2a8f41..b416e6f4 100644 --- a/mateclaw-ui/src/components/chat/MessageBubble.vue +++ b/mateclaw-ui/src/components/chat/MessageBubble.vue @@ -263,6 +263,16 @@ + +
{{ $t('chat.iterationLimitDescription') }}
+