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.model.chat.response;
017
018import com.agentsflex.core.message.AiMessage;
019import com.agentsflex.core.message.ToolCall;
020import com.agentsflex.core.message.ToolMessage;
021import com.agentsflex.core.model.chat.ChatContext;
022import com.agentsflex.core.model.chat.tool.Tool;
023import com.agentsflex.core.model.chat.tool.ToolExecutor;
024import com.agentsflex.core.model.chat.tool.ToolInterceptor;
025import com.agentsflex.core.prompt.Prompt;
026import com.agentsflex.core.util.CollectionUtil;
027import com.agentsflex.core.util.StringUtil;
028import com.alibaba.fastjson2.JSON;
029
030import java.util.*;
031
032public class AiMessageResponse extends AbstractBaseMessageResponse<AiMessage> {
033
034    private final ChatContext context;
035    private final String rawText;
036    private final AiMessage message;
037
038    public AiMessageResponse(ChatContext context, String rawText, AiMessage message) {
039        this.context = context;
040        this.rawText = rawText;
041        this.message = message;
042    }
043
044
045    public ChatContext getContext() {
046        return context;
047    }
048
049    public String getRawText() {
050        return rawText;
051    }
052
053    @Override
054    public AiMessage getMessage() {
055        return message;
056    }
057
058    public boolean hasToolCalls() {
059        if (this.message == null) {
060            return false;
061        }
062        return message.hasToolCalls();
063    }
064
065
066    public List<ToolExecutor> getToolExecutors(ToolInterceptor... interceptors) {
067        if (this.message == null) {
068            return Collections.emptyList();
069        }
070
071        List<ToolCall> calls = message.getToolCalls();
072        if (calls == null || calls.isEmpty()) {
073            return Collections.emptyList();
074        }
075
076        Prompt prompt = getContext().getPrompt();
077        Map<String, Tool> toolsMap = prompt.getToolsMap();
078
079        if (toolsMap == null || toolsMap.isEmpty()) {
080            return Collections.emptyList();
081        }
082
083        List<ToolExecutor> toolExecutors = new ArrayList<>(calls.size());
084        for (ToolCall toolCall : calls) {
085            Tool tool = toolsMap.get(toolCall.getName());
086            if (tool != null) {
087                ToolExecutor executor = new ToolExecutor(tool, toolCall);
088                if (interceptors != null && interceptors.length > 0) {
089                    executor.addInterceptors(Arrays.asList(interceptors));
090                }
091                toolExecutors.add(executor);
092            }
093        }
094        return toolExecutors;
095    }
096
097
098    public List<Object> executeToolCallsAndGetResults(ToolInterceptor... interceptors) {
099        List<ToolExecutor> toolExecutors = getToolExecutors(interceptors);
100
101        for (ToolExecutor toolExecutor : toolExecutors) {
102            toolExecutor.addInterceptors(Arrays.asList(interceptors));
103        }
104
105        List<Object> results = new ArrayList<>();
106        for (ToolExecutor toolExecutor : toolExecutors) {
107            results.add(toolExecutor.execute());
108        }
109        return results;
110    }
111
112
113    public List<ToolMessage> executeToolCallsAndGetToolMessages(ToolInterceptor... interceptors) {
114        List<ToolExecutor> toolExecutors = getToolExecutors(interceptors);
115
116        if (CollectionUtil.noItems(toolExecutors)) {
117            return Collections.emptyList();
118        }
119
120        List<ToolMessage> toolMessages = new ArrayList<>(toolExecutors.size());
121        for (ToolExecutor toolExecutor : toolExecutors) {
122            ToolMessage toolMessage = new ToolMessage();
123            String callId = toolExecutor.getToolCall().getId();
124            if (StringUtil.hasText(callId)) {
125                toolMessage.setToolCallId(callId);
126            } else {
127                toolMessage.setToolCallId(toolExecutor.getToolCall().getName());
128            }
129            Object result = toolExecutor.execute();
130            if (result instanceof CharSequence || result instanceof Number) {
131                toolMessage.setContent(result.toString());
132            } else {
133                toolMessage.setContent(JSON.toJSONString(result));
134            }
135            toolMessages.add(toolMessage);
136        }
137        return toolMessages;
138    }
139
140
141    public static AiMessageResponse error(ChatContext context, String rawText, String errorMessage) {
142        AiMessageResponse errorResp = new AiMessageResponse(context, rawText, null);
143        errorResp.setError(true);
144        errorResp.setErrorMessage(errorMessage);
145        return errorResp;
146    }
147
148
149    @Override
150    public String toString() {
151        return "AiMessageResponse{" +
152            "context=" + context +
153            ", rawText='" + rawText + '\'' +
154            ", message=" + message +
155            ", error=" + error +
156            ", errorMessage='" + errorMessage + '\'' +
157            ", errorType='" + errorType + '\'' +
158            ", errorCode='" + errorCode + '\'' +
159            '}';
160    }
161}