In AgentScope-Java 2.0.1, the ModelCallEndEvent captured by the Middleware's onModelCall hook does not carry complete response information. When the LLM API encounters sensitive content filtering, the response body is often empty. Currently, we can only determine if an error occurred by checking if the content is empty to trigger a fallback retry. However, we are unable to access specific error details such as status_code and err_code (which vary across different LLM providers) from the event.
To Reproduce
Steps to reproduce the behavior:
Implement a custom Middleware that overrides the onModelCall method to intercept ModelCallEndEvent.
Send a request to an LLM API that triggers a sensitive content filter (resulting in an empty response body or an error response).
Inspect the fields within the captured ModelCallEndEvent.
Expected behavior
The ModelCallEndEvent should carry the complete raw response information from the LLM provider, including HTTP status_code, err_code, and the original error message body. This would allow developers to accurately identify the specific type of failure and implement precise fallback/retry strategies, rather than relying on checking if the content is empty.
Error messages
No exception is thrown, but the ModelCallEndEvent lacks necessary error metadata (e.g., status_code, err_code), making it impossible to distinguish between different types of API failures or empty responses.
Environment (please complete the following information):
AgentScope-Java Version: 2.0.1
Java Version: 17
OS: Linux
Additional context
Different LLM providers return different error structures when blocking sensitive content. Without exposing these provider-specific error codes and status codes in the onModelCall middleware, implementing robust error handling and fallback mechanisms is highly limited.
import io.agentscope.core.ReActAgent;
import io.agentscope.core.agent.Agent;
import io.agentscope.core.agent.RuntimeContext;
import io.agentscope.core.event.*;
import io.agentscope.core.middleware.MiddlewareBase;
import io.agentscope.core.middleware.ModelCallInput;
import io.agentscope.core.model.Model;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* 模型调用结果为空时,使用 fallback 模型,一般云端模型在遇到一些敏感词限制时会返回空内容,需要切换本地模型处理
*
* @author qoder
* @date 2026-07-21
*/
@Slf4j
public class EmptyContentFallbackMiddleware implements MiddlewareBase {
public static final String REGISTER_NAME = "empty-content-fallback";
Map<String, Long> fallbackMap = new ConcurrentHashMap<String, Long>();
@Override
public Flux<AgentEvent> onModelCall(Agent agent, RuntimeContext ctx, ModelCallInput input, Function<ModelCallInput, Flux<AgentEvent>> next) {
return next.apply(input).flatMap(event -> {
if (event instanceof ModelCallStartEvent startEvent) {
fallbackMap.put(startEvent.getReplyId(), System.currentTimeMillis());
} else if (event instanceof TextBlockStartEvent textBlockStartEvent) {
fallbackMap.remove(textBlockStartEvent.getReplyId());
} else if (event instanceof ModelCallEndEvent endEvent) {
if (fallbackMap.containsKey(endEvent.getReplyId())) {
fallbackMap.remove(endEvent.getReplyId());
if (agent instanceof ReActAgent reactAgent) {
Model fallbackModel = reactAgent.getModelConfig().fallbackModel() == null ? reactAgent.getModel() : reactAgent.getModelConfig().fallbackModel();
log.warn("EmptyContentFallbackMiddleware:Agent {} 请求没有文本内容响应,使用 fallback 模型 {} 重试", reactAgent.getName(), fallbackModel.getModelName());
return next.apply(new ModelCallInput(input.messages(), input.tools(), input.options(), fallbackModel));
}
}
}
return Flux.just(event);
});
}
}
In AgentScope-Java 2.0.1, the ModelCallEndEvent captured by the Middleware's onModelCall hook does not carry complete response information. When the LLM API encounters sensitive content filtering, the response body is often empty. Currently, we can only determine if an error occurred by checking if the content is empty to trigger a fallback retry. However, we are unable to access specific error details such as status_code and err_code (which vary across different LLM providers) from the event.
To Reproduce
Steps to reproduce the behavior:
Implement a custom Middleware that overrides the onModelCall method to intercept ModelCallEndEvent.
Send a request to an LLM API that triggers a sensitive content filter (resulting in an empty response body or an error response).
Inspect the fields within the captured ModelCallEndEvent.
Expected behavior
The ModelCallEndEvent should carry the complete raw response information from the LLM provider, including HTTP status_code, err_code, and the original error message body. This would allow developers to accurately identify the specific type of failure and implement precise fallback/retry strategies, rather than relying on checking if the content is empty.
Error messages
No exception is thrown, but the ModelCallEndEvent lacks necessary error metadata (e.g., status_code, err_code), making it impossible to distinguish between different types of API failures or empty responses.
Environment (please complete the following information):
AgentScope-Java Version: 2.0.1
Java Version: 17
OS: Linux
Additional context
Different LLM providers return different error structures when blocking sensitive content. Without exposing these provider-specific error codes and status codes in the onModelCall middleware, implementing robust error handling and fallback mechanisms is highly limited.