001/*
002 *  Copyright (c) 2023-2026, Agents-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.agentsflex.core.agent.react;
017
018import java.util.ArrayList;
019import java.util.Collections;
020import java.util.List;
021
022public interface ReActStepParser {
023
024    ReActStepParser DEFAULT = content -> {
025        if (content == null || content.trim().isEmpty()) {
026            return Collections.emptyList();
027        }
028
029        List<ReActStep> steps = new ArrayList<>();
030        String[] lines = content.split("\n");
031
032        String currentThought = null;
033        String currentAction = null;
034        String currentRequest = null;
035        StringBuilder currentActionInput = new StringBuilder();
036        boolean inActionInput = false;
037
038        for (String line : lines) {
039            String trimmedLine = line.trim();
040
041            // 如果遇到新的 Thought,且已有完整步骤,先保存
042            if (trimmedLine.startsWith("Thought:")) {
043                if (currentThought != null && currentAction != null) {
044                    // 保存上一个完整的 step(在遇到新 Thought 或 Final Answer 时触发)
045                    steps.add(new ReActStep(currentThought, currentAction, currentActionInput.toString().trim()));
046                    // 重置状态
047                    currentActionInput.setLength(0);
048                    inActionInput = false;
049                }
050                currentThought = trimmedLine.substring("Thought:".length()).trim();
051                currentAction = null;
052            }
053            // 如果遇到 Action
054            else if (trimmedLine.startsWith("Action:")) {
055                if (currentThought == null) {
056                    // 如果 Action 出现在 Thought 之前,视为格式错误,可选择忽略或报错
057                    continue;
058                }
059                currentAction = trimmedLine.substring("Action:".length()).trim();
060            }
061            // 如果遇到 Action Input
062            else if (trimmedLine.startsWith("Action Input:")) {
063                if (currentAction == null) {
064                    // Action Input 出现在 Action 之前,跳过
065                    continue;
066                }
067                String inputPart = trimmedLine.substring("Action Input:".length()).trim();
068                currentActionInput.append(inputPart);
069                inActionInput = true;
070            }
071            // 如果正在读取 Action Input 的后续行(多行 JSON)
072            else if (inActionInput) {
073                // 判断是否是下一个结构的开始:Thought / Action / Final Answer
074                if (trimmedLine.startsWith("Thought:") ||
075                    trimmedLine.startsWith("Action:") ||
076                    trimmedLine.startsWith("Final Answer:")) {
077                    // 实际上这一行属于下一段,应退回处理
078                    // 但我们是在 for 循环里,无法“退回”,所以先保存当前 step
079                    steps.add(new ReActStep(currentThought, currentAction, currentActionInput.toString().trim()));
080                    currentActionInput.setLength(0);
081                    inActionInput = false;
082                    currentThought = null;
083                    currentAction = null;
084                    // 重新处理当前行(递归或标记),但为简化,我们直接继续下一轮
085                    // 因为下一轮会处理 Thought/Action
086                    continue;
087                } else {
088                    // 是 Action Input 的续行,追加(保留原始换行或加空格)
089                    if (currentActionInput.length() > 0) {
090                        currentActionInput.append("\n");
091                    }
092                    currentActionInput.append(line); // 保留原始缩进(可选)
093                }
094            }
095            // 如果遇到 Final Answer,结束当前步骤(如果有)
096            else if (trimmedLine.startsWith("Final Answer:")) {
097                if (currentThought != null && currentAction != null) {
098                    steps.add(new ReActStep(currentThought, currentAction, currentActionInput.toString().trim()));
099                }
100                // Final Answer 本身不作为 ReActStep,通常单独处理
101                break; // 或 continue,视需求而定
102            }
103            // 空行或无关行:如果是 Action Input 多行内容,已在上面处理;否则忽略
104        }
105
106        // 循环结束后,检查是否还有未保存的步骤
107        if (currentThought != null && currentAction != null) {
108            steps.add(new ReActStep(currentThought, currentAction, currentActionInput.toString().trim()));
109        }
110
111        return steps;
112    };
113
114
115    List<ReActStep> parse(String content);
116
117
118    default boolean isFinalAnswer(String content) {
119        return content.contains(getFinalAnswerFlag());
120    }
121
122    default boolean isReActAction(String content) {
123        return content.contains("Action:") && content.contains("Action Input:");
124    }
125
126    default boolean isRequest(String content) {
127        return content.contains("Request:");
128    }
129
130    default String extractRequestQuestion(String content) {
131        return content.trim().substring("Request:".length()).trim();
132    }
133
134    default String getFinalAnswerFlag() {
135        return "Final Answer:";
136    }
137}