Skip to content

Human action confirmation #592

@ssu-atl

Description

@ssu-atl

Hello adk-java team.

Q1:
I noticed that boolean and advance action confirmation for tool calls is available in the adk-python, but not quite yet in adk-java. I also stumble across #531 which looks like there are some ongoing work to support this feature.

Was wondering whether this feature will be coming in the next release of adk-java and when that will be?

Q2:
Are there alternative ways currently to implement HITL tool confirmation in adk-java, if so what would be the recommended approach?

I did manage to replicate similar behaviour as the feature in adk-python using a before tool callback hook.

public class ConfirmationToolCallback implements Callbacks.BeforeToolCallback {

    private final String toolName;

    public ConfirmationToolCallback(String functionName) {
        this.toolName = functionName;
    }

    @Override
    public Maybe<Map<String, Object>> call(InvocationContext invocationContext, BaseTool baseTool, Map<String, Object> input, ToolContext toolContext) {
        if (!toolName.equals(baseTool.name())) {
            return Maybe.empty();
        }

        boolean hasApproveFunctionResponse = invocationContext.userContent()
                .map(userContent -> userContent.parts().stream()
                        .flatMap(Collection::stream)
                        .map(Part::functionResponse)
                        .anyMatch(response -> response.flatMap(FunctionResponse::name).orElse("").equals("approved_function_call")))
                .orElse(false);

        if (!hasApproveFunctionResponse) {
            invocationContext.sessionService()
                    .appendEvent(invocationContext.session(),
                            Event.builder().author(invocationContext.agent().name()).content(
                                    Content.fromParts(
                                            Part.builder()
                                                    .functionCall(
                                                            FunctionCall.builder()
                                                                    .id(UUID.randomUUID().toString())
                                                                    .name("approved_function_call")
                                                                    .build()
                                                    )
                                                    .build()
                                    )
                            ).build()
                    );
            return Maybe.just(Map.of("error", "Please approve or reject the tool call"));
        }

        return Maybe.empty();
    }
}

and the user would confirm and resume the execution using

runner.runAsync(USER_ID, session.id(), Content.fromParts(
    Part.builder()
        functionResponse(
             FunctionResponse.builder()
                  .id(functionId)
                  .name("approved_function_call")
                  .response(Map.of("confirmed", true))
                  .build()
          )
    .build()
));

However, I'm not sure if this is heading down the right path or I should use session state to manage the HITL flows.

Thanks in advance!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions