-
Couldn't load subscription status.
- Fork 167
Move the planning_re_act_planner python implementation to java version. #503
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,166 @@ | ||||||
| package com.google.adk.flows.llmflows; | ||||||
|
|
||||||
| import com.google.adk.agents.CallbackContext; | ||||||
| import com.google.adk.agents.InvocationContext; | ||||||
| import com.google.adk.agents.LlmAgent; | ||||||
| import com.google.adk.agents.ReadonlyContext; | ||||||
| import com.google.adk.events.Event; | ||||||
| import com.google.adk.models.LlmRequest; | ||||||
| import com.google.adk.models.LlmResponse; | ||||||
| import com.google.adk.planners.BasePlanner; | ||||||
| import com.google.adk.planners.BuiltInPlanner; | ||||||
| import com.google.common.collect.ImmutableList; | ||||||
| import com.google.genai.types.Content; | ||||||
| import com.google.genai.types.Part; | ||||||
| import io.reactivex.rxjava3.core.Single; | ||||||
|
|
||||||
| import java.util.List; | ||||||
| import java.util.Optional; | ||||||
| import java.util.stream.Collectors; | ||||||
|
|
||||||
| public class NLPlanning { | ||||||
|
|
||||||
| static class NlPlanningRequestProcessor implements RequestProcessor { | ||||||
|
|
||||||
| @Override | ||||||
| public Single<RequestProcessingResult> processRequest( | ||||||
| InvocationContext context, LlmRequest llmRequest) { | ||||||
|
|
||||||
| if (!(context.agent() instanceof LlmAgent)) { | ||||||
| throw new IllegalArgumentException( | ||||||
| "Agent in InvocationContext is not an instance of LlmAgent."); | ||||||
| } | ||||||
|
|
||||||
| Optional<BasePlanner> plannerOpt = getPlanner(context); | ||||||
| if (plannerOpt.isEmpty()) { | ||||||
| return Single.just(RequestProcessor.RequestProcessingResult.create(llmRequest, ImmutableList.of())); | ||||||
| } | ||||||
|
|
||||||
| BasePlanner planner = plannerOpt.get(); | ||||||
|
|
||||||
| // Apply thinking configuration for built-in planners | ||||||
| if (planner instanceof BuiltInPlanner) { | ||||||
| llmRequest = ((BuiltInPlanner) planner).applyThinkingConfig(llmRequest); | ||||||
| } | ||||||
|
|
||||||
| // Build and append planning instruction | ||||||
| Optional<String> planningInstruction = | ||||||
| planner.generatePlanningInstruction(new ReadonlyContext(context), llmRequest); | ||||||
|
|
||||||
| LlmRequest.Builder b = llmRequest.toBuilder(); | ||||||
| planningInstruction.ifPresent(s -> b.appendInstructions(ImmutableList.of(s))); | ||||||
| llmRequest = b.build(); | ||||||
|
|
||||||
| // Remove thought annotations from request | ||||||
| llmRequest = removeThoughtFromRequest(llmRequest); | ||||||
|
|
||||||
| return Single.just(RequestProcessor.RequestProcessingResult.create(llmRequest, ImmutableList.of())); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| static class NlPlanningResponseProcessor implements ResponseProcessor { | ||||||
|
|
||||||
| @Override | ||||||
| public Single<ResponseProcessingResult> processResponse( | ||||||
| InvocationContext context, LlmResponse llmResponse) { | ||||||
|
|
||||||
| if (!(context.agent() instanceof LlmAgent)) { | ||||||
| throw new IllegalArgumentException( | ||||||
| "Agent in InvocationContext is not an instance of LlmAgent."); | ||||||
| } | ||||||
|
Comment on lines
+67
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the |
||||||
|
|
||||||
| // Validate response structure | ||||||
| if (llmResponse == null || llmResponse.content().isEmpty()) { | ||||||
| return Single.just( | ||||||
| ResponseProcessor.ResponseProcessingResult.create( | ||||||
| llmResponse, ImmutableList.of(), Optional.empty())); | ||||||
| } | ||||||
|
|
||||||
| Optional<BasePlanner> plannerOpt = getPlanner(context); | ||||||
| if (plannerOpt.isEmpty()) { | ||||||
| return Single.just( | ||||||
| ResponseProcessor.ResponseProcessingResult.create( | ||||||
| llmResponse, ImmutableList.of(), Optional.empty())); | ||||||
| } | ||||||
|
|
||||||
| BasePlanner planner = plannerOpt.get(); | ||||||
| LlmResponse.Builder responseBuilder = llmResponse.toBuilder(); | ||||||
|
|
||||||
| // Process the planning response | ||||||
| CallbackContext callbackContext = new CallbackContext(context, null); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Passing
Suggested change
|
||||||
| Optional<List<Part>> processedParts = | ||||||
| planner.processPlanningResponse( | ||||||
| callbackContext, llmResponse.content().get().parts().orElse(List.of())); | ||||||
|
|
||||||
| // Update response with processed parts | ||||||
| if (processedParts.isPresent()) { | ||||||
| Content.Builder contentBuilder = llmResponse.content().get().toBuilder(); | ||||||
| contentBuilder.parts(processedParts.get()); | ||||||
| responseBuilder.content(contentBuilder.build()); | ||||||
| } | ||||||
|
|
||||||
| ImmutableList.Builder<Event> eventsBuilder = ImmutableList.builder(); | ||||||
|
|
||||||
| // Generate state update event if there are deltas | ||||||
| if (callbackContext.state().hasDelta()) { | ||||||
| Event stateUpdateEvent = | ||||||
| Event.builder() | ||||||
| .invocationId(context.invocationId()) | ||||||
| .author(context.agent().name()) | ||||||
| .branch(context.branch()) | ||||||
| .actions(callbackContext.eventActions()) | ||||||
| .build(); | ||||||
|
|
||||||
| eventsBuilder.add(stateUpdateEvent); | ||||||
| } | ||||||
|
|
||||||
| return Single.just( | ||||||
| ResponseProcessor.ResponseProcessingResult.create( | ||||||
| responseBuilder.build(), eventsBuilder.build(), Optional.empty())); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Retrieves the planner from the invocation context. | ||||||
| * | ||||||
| * @param invocationContext the current invocation context | ||||||
| * @return optional planner instance, or empty if none available | ||||||
| */ | ||||||
| private static Optional<BasePlanner> getPlanner(InvocationContext invocationContext) { | ||||||
| if (!(invocationContext.agent() instanceof LlmAgent agent)) { | ||||||
| return Optional.empty(); | ||||||
| } | ||||||
|
|
||||||
| return agent.planner(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Removes thought annotations from all parts in the LLM request. | ||||||
| * | ||||||
| * <p>This method iterates through all content parts and sets the thought field to false, | ||||||
| * effectively removing thought markings from the request. | ||||||
| * | ||||||
| * @param llmRequest the LLM request to process | ||||||
| */ | ||||||
| private static LlmRequest removeThoughtFromRequest(LlmRequest llmRequest) { | ||||||
| if (llmRequest.contents() == null || llmRequest.contents().isEmpty()) { | ||||||
| return llmRequest; | ||||||
| } | ||||||
|
|
||||||
| // Process each content and update its parts | ||||||
| List<Content> updatedContents = llmRequest.contents().stream().map(content -> { | ||||||
| if (content.parts().isEmpty()) { | ||||||
| return content; | ||||||
| } | ||||||
|
|
||||||
| // Update all parts to set thought to false | ||||||
| List<Part> updatedParts = content.parts().get().stream().map(part -> part.toBuilder().thought(false).build()).collect(Collectors.toList()); | ||||||
|
|
||||||
| // Return updated content with modified parts | ||||||
| return content.toBuilder().parts(updatedParts).build(); | ||||||
| }).collect(Collectors.toList()); | ||||||
|
|
||||||
| // Return updated LlmRequest with modified contents | ||||||
| return llmRequest.toBuilder().contents(updatedContents).build(); | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.google.adk.planners; | ||
|
|
||
| import com.google.adk.agents.CallbackContext; | ||
| import com.google.adk.agents.ReadonlyContext; | ||
| import com.google.adk.models.LlmRequest; | ||
| import com.google.genai.types.Part; | ||
| import java.util.List; | ||
| import java.util.Optional; | ||
|
|
||
| public interface BasePlanner { | ||
| /** | ||
| * Generates system instruction text for LLM planning requests. | ||
| * | ||
| * @param context readonly invocation context | ||
| * @param request the LLM request being prepared | ||
| * @return planning instruction text, or empty if no instruction needed | ||
| */ | ||
| Optional<String> generatePlanningInstruction(ReadonlyContext context, LlmRequest request); | ||
|
|
||
| /** | ||
| * Processes and transforms LLM response parts for planning workflow. | ||
| * | ||
| * @param context callback context for the current invocation | ||
| * @param responseParts list of response parts from the LLM | ||
| * @return processed response parts, or empty if no processing required | ||
| */ | ||
| Optional<List<Part>> processPlanningResponse(CallbackContext context, List<Part> responseParts); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| package com.google.adk.planners; | ||
|
|
||
| import com.google.adk.agents.CallbackContext; | ||
| import com.google.adk.agents.ReadonlyContext; | ||
| import com.google.adk.models.LlmRequest; | ||
| import com.google.genai.types.GenerateContentConfig; | ||
| import com.google.genai.types.Part; | ||
| import com.google.genai.types.ThinkingConfig; | ||
| import java.util.List; | ||
| import java.util.Optional; | ||
|
|
||
| public class BuiltInPlanner implements BasePlanner { | ||
| private ThinkingConfig cognitiveConfig; | ||
|
|
||
| private BuiltInPlanner() {} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| private BuiltInPlanner(ThinkingConfig cognitiveConfig) { | ||
| this.cognitiveConfig = cognitiveConfig; | ||
| } | ||
|
|
||
| public static BuiltInPlanner buildPlanner(ThinkingConfig cognitiveConfig) { | ||
| return new BuiltInPlanner(cognitiveConfig); | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<String> generatePlanningInstruction(ReadonlyContext context, LlmRequest request) { | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<List<Part>> processPlanningResponse( | ||
| CallbackContext context, List<Part> responseParts) { | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| /** | ||
| * Configures the LLM request with thinking capabilities. This method modifies the request to | ||
| * include the thinking configuration, enabling the model's native cognitive processing features. | ||
| * | ||
| * @param request the LLM request to configure | ||
| */ | ||
| public LlmRequest applyThinkingConfig(LlmRequest request) { | ||
| if (this.cognitiveConfig != null) { | ||
| // Ensure config exists | ||
| GenerateContentConfig.Builder configBuilder = | ||
| request.config().map(GenerateContentConfig::toBuilder).orElse(GenerateContentConfig.builder()); | ||
|
|
||
| // Apply thinking configuration | ||
| request = | ||
| request.toBuilder() | ||
| .config(configBuilder.thinkingConfig(this.cognitiveConfig).build()) | ||
| .build(); | ||
| } | ||
| return request; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
instanceofcheck forLlmAgentis redundant. ThegetPlannermethod called on line 34 already performs this check and returns an emptyOptionalif the agent is not anLlmAgent. The subsequent checkif (plannerOpt.isEmpty())on line 35 is sufficient to handle this case. Removing these lines will simplify the code and avoid duplication, as the same pattern is used inNlPlanningResponseProcessor.