Skip to content

Handle Vertex AI tool call JsonProcessingException fallback#4521

Open
cho-thinkfree-com wants to merge 1 commit intospring-projects:mainfrom
cho-thinkfree-com:fix/gh-4230-vertexai-tool-call-fallback
Open

Handle Vertex AI tool call JsonProcessingException fallback#4521
cho-thinkfree-com wants to merge 1 commit intospring-projects:mainfrom
cho-thinkfree-com:fix/gh-4230-vertexai-tool-call-fallback

Conversation

@cho-thinkfree-com
Copy link
Contributor

@cho-thinkfree-com cho-thinkfree-com commented Sep 30, 2025

Summary

  • Use DefaultToolExecutionExceptionProcessor(false) so that tool exceptions are not thrown but are converted into tool responses containing the exception message. This prevents exceptions from bubbling up through the streaming pipeline.
  • If a tool exception were thrown outward, it would be hard to catch and turn into a user-friendly message at the right layer. So we treat the exception message as the tool’s output instead.
  • However, Vertex AI expects tool responses to be valid JSON. If the exception message is plain text (i.e., not JSON), Vertex will throw another error. Therefore, when forwarding exception messages as tool responses, they must be wrapped/normalized into JSON (e.g., { "result": "" }).
  • ensure Vertex AI tool call responses fall back to the raw JSON payload when JsonProcessingException is raised during tool result conversion
  • Fix Gemini Tool Calling for texts returned from MethodToolCallback #3040

Signed-off-by: cho-thinkfree-com <cho@thinkfree.com>
@cho-thinkfree-com cho-thinkfree-com force-pushed the fix/gh-4230-vertexai-tool-call-fallback branch from 960654a to 3ad2acc Compare September 30, 2025 15:19
@cho-thinkfree-com cho-thinkfree-com changed the title GH-4230: Handle Vertex AI tool call JsonProcessingException fallback Handle Vertex AI tool call JsonProcessingException fallback Sep 30, 2025
@cho-thinkfree-com
Copy link
Contributor Author

You can also address this by introducing a VertexAwareToolCallingManager that normalizes tool responses before they reach Vertex. The idea is simple:

  • If the tool returns a valid JSON object, pass it through unchanged.
  • If it returns non-JSON text (e.g., an exception message), wrap it as {"result":""}.

This guarantees Vertex always receives a top-level JSON object.

private static final class VertexAwareToolCallingManager implements ToolCallingManager {

    private final DefaultToolCallingManager peerToolCallingManager;

    public VertexAwareToolCallingManager(DefaultToolCallingManager peerToolCallingManager) {
        this.peerToolCallingManager = peerToolCallingManager;
    }

    @Override
    public ToolExecutionResult executeToolCalls(@NonNull Prompt prompt, @NonNull ChatResponse chatResponse) {
        ToolExecutionResult res = this.peerToolCallingManager.executeToolCalls(prompt, chatResponse);
        ObjectMapper om = safeObjectMapper();

        var newHistory = res.conversationHistory().stream().map(msg -> {
            if (msg instanceof ToolResponseMessage trm) {
                var patched = trm.getResponses().stream().map(r -> {
                    String data = r.responseData();
                    // Normalizes to object JSON: returns {"result": data} when wrapping is required; otherwise returns data as is.
                    String normalized = normalizeToObjectJson(om, data); 
                    return new ToolResponseMessage.ToolResponse(r.id(), r.name(), normalized);
                }).toList();
                return new ToolResponseMessage(patched);
            }
            return msg;
        }).toList();

        return new DefaultToolExecutionResult(newHistory, res.returnDirect());
    }
//...
// ...

@junjiexh
Copy link

junjiexh commented Feb 6, 2026

what prevent this to be merging?
Today I also faced same problem as issue #4230

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants