Skip to content

HttpClientStreamableHttpTransport RequestContext Lost #5118

@PsHype

Description

@PsHype

Bug description
AgentToolNode lost ToolCallRequest Context

Environment
1.1.2

Steps to reproduce

  1. RunnableConfig runnableConfig = RunnableConfig.builder()
    .threadId("thread-" + i)
    .addMetadata("Personal-Access-Token", "your-token" + i).build();
  2. node.call(input,runnableConfig)

Expected behavior
Personal-Access-Token can keep in Context

Minimal Complete Reproducible example
RunnableConfig paramter has metadata as context ,when call executeToolxxx method the context is lost



  private ToolCallResponse executeToolCallWithInterceptors(AssistantMessage.ToolCall toolCall, OverAllState state, RunnableConfig config, Map<String, Object> extraStateFromToolCall) {
        ToolCallRequest request = ToolCallRequest.builder().toolCall(toolCall).context((Map)config.metadata().orElse(new HashMap())).build();
        ToolCallHandler baseHandler = (req) -> {
            ToolCallback toolCallback = this.resolve(req.getToolName());
            if (toolCallback == null) {
                logger.warn("LLM may have adapted the tool name '{}', especially if the name was truncated due to length limits. If this is the case, you can customize the prefixing and processing logic using McpToolNamePrefixGenerator", req.getToolName());
                throw new IllegalStateException("No ToolCallback found for tool name: " + req.getToolName());
            } else {
                if (this.enableActingLog) {
                    logger.info("[ThreadId {}] Agent {} acting, executing tool {}.", new Object[]{config.threadId().orElse("$default"), this.agentName, req.getToolName()});
                }

                String result;
                try {
                    if (!(toolCallback instanceof FunctionToolCallback) && !(toolCallback instanceof MethodToolCallback)) {
                        result = toolCallback.call(req.getArguments(), new ToolContext(this.toolContext));
                    } else {
                        Map<String, Object> toolContextMap = new HashMap(this.toolContext);
                        toolContextMap.putAll(Map.of("_AGENT_STATE_", state, "_AGENT_CONFIG_", config, "_AGENT_STATE_FOR_UPDATE_", extraStateFromToolCall));
                        result = toolCallback.call(req.getArguments(), new ToolContext(toolContextMap));
                    }

                    if (this.enableActingLog) {
                        logger.info("[ThreadId {}] Agent {} acting, tool {} finished", new Object[]{config.threadId().orElse("$default"), this.agentName, req.getToolName()});
                        if (logger.isDebugEnabled()) {
                            logger.debug("Tool {} returned: {}", req.getToolName(), result);
                        }
                    }
                } catch (ToolExecutionException var8) {
                    ToolExecutionException e = var8;
                    logger.error("[ThreadId {}] Agent {} acting, tool {} execution failed, handle to {} processor to decide the next move (terminate or continue). ", new Object[]{config.threadId().orElse("$default"), this.agentName, req.getToolName(), this.toolExecutionExceptionProcessor.getClass().getName(), e});
                    result = this.toolExecutionExceptionProcessor.process(e);
                }

                return ToolCallResponse.of(req.getToolCallId(), req.getToolName(), result);
            }
        };

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions