feat: Enhance error handling with AgentErrorType and Exception tracking
This commit is contained in:
@@ -22,7 +22,9 @@ public final class AgentContext {
|
||||
|
||||
private volatile boolean streaming;
|
||||
|
||||
private volatile boolean error;
|
||||
private volatile AgentErrorType errorType;
|
||||
|
||||
private volatile Exception errorException;
|
||||
|
||||
private volatile boolean skipNextCompletion;
|
||||
|
||||
@@ -74,7 +76,8 @@ public final class AgentContext {
|
||||
this.requestStatistic = other.requestStatistic;
|
||||
this.lastCompletion = other.lastCompletion;
|
||||
this.agentContextUpdateConsumer = other.agentContextUpdateConsumer;
|
||||
this.error = other.error;
|
||||
this.errorType = other.errorType;
|
||||
this.errorException = other.errorException;
|
||||
this.skipNextCompletion = other.skipNextCompletion;
|
||||
this.skipCache = other.skipCache;
|
||||
this.streaming = other.streaming;
|
||||
@@ -190,11 +193,29 @@ public final class AgentContext {
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return this.error;
|
||||
return this.errorType != null;
|
||||
}
|
||||
|
||||
public void setError(final boolean error) {
|
||||
this.error = error;
|
||||
public AgentErrorType getErrorType() {
|
||||
return this.errorType;
|
||||
}
|
||||
|
||||
public Exception getErrorException() {
|
||||
return this.errorException;
|
||||
}
|
||||
|
||||
public void setError(final AgentErrorType errorType, final Exception errorException) {
|
||||
if (errorType == null) {
|
||||
this.clearError();
|
||||
return;
|
||||
}
|
||||
this.errorType = errorType;
|
||||
this.errorException = errorException;
|
||||
}
|
||||
|
||||
public void clearError() {
|
||||
this.errorType = null;
|
||||
this.errorException = null;
|
||||
}
|
||||
|
||||
public boolean isSkipNextCompletion() {
|
||||
|
||||
6
src/main/java/de/extio/lmlib/agent/AgentErrorType.java
Normal file
6
src/main/java/de/extio/lmlib/agent/AgentErrorType.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package de.extio.lmlib.agent;
|
||||
|
||||
public enum AgentErrorType {
|
||||
PARSING,
|
||||
GENERAL
|
||||
}
|
||||
@@ -111,6 +111,8 @@ public class AgentExecutorService implements InitializingBean, DisposableBean {
|
||||
}
|
||||
catch (final Exception e) {
|
||||
LOGGER.error("Error executing agent", e);
|
||||
branchContext.setError(AgentErrorType.GENERAL, e);
|
||||
responses.add(branchContext);
|
||||
}
|
||||
}, this.branchExecutorService));
|
||||
}
|
||||
|
||||
@@ -80,10 +80,18 @@ public interface BaseAgent {
|
||||
final var splits = new ArrayList<Split>();
|
||||
|
||||
context.setNextAgent(null);
|
||||
context.clearError();
|
||||
|
||||
this.preProcess(context);
|
||||
if (context.getAgentContextUpdateConsumer() != null) {
|
||||
context.getAgentContextUpdateConsumer().accept(context);
|
||||
try {
|
||||
this.preProcess(context);
|
||||
if (context.getAgentContextUpdateConsumer() != null) {
|
||||
context.getAgentContextUpdateConsumer().accept(context);
|
||||
}
|
||||
}
|
||||
catch (final Exception ex) {
|
||||
LOGGER.error("Error during agent execution", ex);
|
||||
context.setError(AgentErrorType.GENERAL, ex);
|
||||
return List.of(context);
|
||||
}
|
||||
|
||||
splits.addAll(this.applyTemplate(context));
|
||||
@@ -91,6 +99,7 @@ public interface BaseAgent {
|
||||
for (final var split : splits) {
|
||||
tasks.add(CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
split.context().clearError();
|
||||
final boolean skipCompletion = split.context().isSkipNextCompletion() || this.agentType(split.context()) == AgentType.PROCESSING_ONLY;
|
||||
if (skipCompletion) {
|
||||
split.context().setSkipNextCompletion(false);
|
||||
@@ -108,6 +117,7 @@ public interface BaseAgent {
|
||||
final var conversation = this.setupConversation(split);
|
||||
|
||||
boolean parseable = false;
|
||||
Exception parseException = null;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
LOGGER.debug("Conversation: {}", conversation);
|
||||
|
||||
@@ -162,6 +172,7 @@ public interface BaseAgent {
|
||||
break;
|
||||
}
|
||||
catch (final Exception e) {
|
||||
parseException = e;
|
||||
split.context().getGraph().add("⚠");
|
||||
LOGGER.warn("{} Cannot parse response: {}", this.name(), completion.response(), e);
|
||||
split.context().setSkipCache(true);
|
||||
@@ -171,7 +182,7 @@ public interface BaseAgent {
|
||||
if (!parseable) {
|
||||
LOGGER.warn("{} Response is still not parseable after last attempt", this.name());
|
||||
split.context().getGraph().add("☢");
|
||||
split.context().setError(true);
|
||||
split.context().setError(AgentErrorType.PARSING, parseException);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -183,7 +194,7 @@ public interface BaseAgent {
|
||||
}
|
||||
catch (final Exception ex) {
|
||||
LOGGER.error("Error during agent execution", ex);
|
||||
split.context().setError(true);
|
||||
split.context().setError(AgentErrorType.GENERAL, ex);
|
||||
}
|
||||
}, agentExecutorService));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package de.extio.lmlib.agent;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Map;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import de.extio.lmlib.agent.responsehandler.AgentResponseHandler;
|
||||
import de.extio.lmlib.client.Client;
|
||||
import de.extio.lmlib.client.Completion;
|
||||
import de.extio.lmlib.client.CompletionStatistics;
|
||||
import de.extio.lmlib.profile.ModelCategory;
|
||||
|
||||
class AgentErrorHandlingTest {
|
||||
|
||||
@Test
|
||||
void parsingErrorsKeepTheirTypeWithoutAnExceptionWhenTheHandlerReturnsFalse() {
|
||||
final Client client = mock(Client.class);
|
||||
when(client.conversation(any(ModelCategory.class), any(), anyBoolean())).thenReturn(new Completion("not json", null, null, new CompletionStatistics(1, Duration.ofMillis(1), 1, 0, 1, 0, BigDecimal.ZERO, false)));
|
||||
|
||||
final AgentResponseHandler responseHandler = (completion, context) -> false;
|
||||
final var agent = new Agent("ParseAgent",
|
||||
AgentType.COMPLETION,
|
||||
ModelCategory.MEDIUM,
|
||||
null,
|
||||
null,
|
||||
"prompt",
|
||||
responseHandler,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
AgentNext::end);
|
||||
final var context = new AgentContext(Map.of(agent.name(), agent));
|
||||
|
||||
try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) {
|
||||
final var results = agent.execute(client, executorService, context);
|
||||
final var result = results.getFirst();
|
||||
assertTrue(result.isError());
|
||||
assertEquals(AgentErrorType.PARSING, result.getErrorType());
|
||||
assertNull(result.getErrorException());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void thrownExceptionsAreStoredAsGeneralErrors() {
|
||||
final var exception = new IllegalStateException("boom");
|
||||
final var agent = new Agent("BrokenAgent",
|
||||
AgentType.PROCESSING_ONLY,
|
||||
ModelCategory.MEDIUM,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
context -> {
|
||||
throw exception;
|
||||
},
|
||||
null,
|
||||
null,
|
||||
AgentNext::end);
|
||||
final var context = new AgentContext(Map.of(agent.name(), agent));
|
||||
|
||||
try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) {
|
||||
final var results = agent.execute(null, executorService, context);
|
||||
final var result = results.getFirst();
|
||||
assertTrue(result.isError());
|
||||
assertEquals(AgentErrorType.GENERAL, result.getErrorType());
|
||||
assertInstanceOf(IllegalStateException.class, result.getErrorException());
|
||||
assertEquals("boom", result.getErrorException().getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user