14f30ad1d8a3d9b0c5739e29b30b4134dfc866f9
[usecase-ui/llm-adaptation.git] /
1 package org.onap.usecaseui.llmadaptation.service.impl;
2
3 import com.alibaba.fastjson2.JSONArray;
4 import com.alibaba.fastjson2.JSONObject;
5 import com.fasterxml.jackson.databind.ObjectMapper;
6 import lombok.extern.slf4j.Slf4j;
7 import org.onap.usecaseui.llmadaptation.bean.Application;
8 import org.onap.usecaseui.llmadaptation.bean.ResultHeader;
9 import org.onap.usecaseui.llmadaptation.bean.ServiceResult;
10 import org.onap.usecaseui.llmadaptation.bean.fastgpt.dataset.CreateDataSetResponse;
11 import org.onap.usecaseui.llmadaptation.bean.fastgpt.application.*;
12 import org.onap.usecaseui.llmadaptation.constant.CommonConstant;
13 import org.onap.usecaseui.llmadaptation.constant.FastGptConstant;
14 import org.onap.usecaseui.llmadaptation.constant.ServerConstant;
15 import org.onap.usecaseui.llmadaptation.mapper.ApplicationMapper;
16 import org.onap.usecaseui.llmadaptation.service.FastGptApplicationService;
17 import org.onap.usecaseui.llmadaptation.util.TimeUtil;
18 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.core.io.ResourceLoader;
20 import org.springframework.stereotype.Service;
21 import org.springframework.web.reactive.function.client.WebClient;
22 import reactor.core.publisher.Flux;
23 import reactor.core.publisher.Mono;
24
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.UUID;
30 import java.util.concurrent.atomic.AtomicBoolean;
31
32 import static org.springframework.http.MediaType.APPLICATION_JSON;
33
34 @Slf4j
35 @Service
36 public class FastGptApplicationServiceImpl implements FastGptApplicationService {
37     @Autowired
38     private ResourceLoader resourceLoader;
39
40     @Autowired
41     private ApplicationMapper applicationMapper;
42
43     @Autowired
44     private WebClient webClient;
45
46     @Autowired
47     private ServerConstant serverConstant;
48
49     private final ObjectMapper objectMapper = new ObjectMapper();
50
51     @Override
52     public Mono<ServiceResult> createApplication(Application application) {
53         try (InputStream inputStream = resourceLoader.getResource(FastGptConstant.CREATE_APP_PARAM_FILE_URL).getInputStream()) {
54             CreateApplicationParam createApplicationParam = objectMapper.readValue(inputStream, CreateApplicationParam.class);
55             createApplicationParam.setName(application.getApplicationName());
56
57             return createApplication(createApplicationParam, application)
58                     .onErrorResume(e -> {
59                         log.error("Error occurred while creating application: {}", e.getMessage());
60                         return Mono.just(new ServiceResult(new ResultHeader(500, "Application creation failed")));
61                     });
62
63         } catch (IOException e) {
64             log.error("Error occurred while reading input file: {}", e.getMessage());
65             return Mono.just(new ServiceResult(new ResultHeader(500, "Failed to read input file")));
66         }
67     }
68
69     private Mono<ServiceResult> createApplication(CreateApplicationParam createApplicationParam, Application application) {
70         return webClient.post()
71                 .uri(serverConstant.getFastGptServer() + FastGptConstant.CREATE_APPLICATION)
72                 .contentType(APPLICATION_JSON)
73                 .header(CommonConstant.COOKIE, FastGptConstant.COOKIE_VALUE)
74                 .bodyValue(createApplicationParam)
75                 .retrieve()
76                 .bodyToMono(CreateDataSetResponse.class)
77                 .flatMap(response -> {
78                     if (response.getCode() == 200) {
79                         return handleApplicationResponse(response, application);
80                     }
81                     return Mono.just(new ServiceResult(new ResultHeader(500, response.getStatusText())));
82                 });
83     }
84
85     private Mono<ServiceResult> handleApplicationResponse(CreateDataSetResponse createDataSetResponse, Application application) {
86         String data = String.valueOf(createDataSetResponse.getData());
87         application.setApplicationId(data);
88         String url = serverConstant.getFastGptServer() + FastGptConstant.UPDATE_APPLICATION + data;
89         UpdateApplicationParam updateApplicationParam = new UpdateApplicationParam();
90         updateApplicationParam.setAvatar("/imgs/app/avatar/simple.svg");
91         updateApplicationParam.setDefaultPermission(0);
92         updateApplicationParam.setName(application.getApplicationName());
93         updateApplicationParam.setIntro(application.getApplicationDescription());
94
95         return webClient.put()
96                 .uri(url)
97                 .contentType(APPLICATION_JSON)
98                 .header(CommonConstant.COOKIE, FastGptConstant.COOKIE_VALUE)
99                 .bodyValue(updateApplicationParam)
100                 .retrieve()
101                 .bodyToMono(CreateDataSetResponse.class)
102                 .flatMap(response -> {
103                     if (response.getCode() == 200) {
104                         return publishApplication(application, data);
105                     }
106                     return Mono.just(new ServiceResult(new ResultHeader(500, response.getStatusText())));
107                 });
108     }
109
110     private Mono<ServiceResult> publishApplication(Application application, String data) {
111         try (InputStream inputStream = resourceLoader.getResource(FastGptConstant.PUBLISH_APP_PARAM_FILE_URL).getInputStream()) {
112             PublishApplicationParam publishApplicationParam = objectMapper.readValue(inputStream, PublishApplicationParam.class);
113             publishApplicationParam.setVersionName(TimeUtil.getNowTime());
114             publishApplicationParam.getChatConfig().setWelcomeText(application.getOpeningRemarks());
115             setApplicationParameters(application, publishApplicationParam);
116             String publishUrl = serverConstant.getFastGptServer() + FastGptConstant.PUBLISH_APPLICATION + data;
117
118             return webClient.post()
119                     .uri(publishUrl)
120                     .contentType(APPLICATION_JSON)
121                     .header(CommonConstant.COOKIE, FastGptConstant.COOKIE_VALUE)
122                     .bodyValue(publishApplicationParam)
123                     .retrieve()
124                     .bodyToMono(CreateDataSetResponse.class)
125                     .flatMap(response -> {
126                         if (response.getCode() == 200) {
127                             applicationMapper.insertApplication(application);
128                             return Mono.just(new ServiceResult(new ResultHeader(200, "Application created successfully")));
129                         }
130                         return Mono.just(new ServiceResult(new ResultHeader(500, response.getStatusText())));
131                     });
132         } catch (IOException e) {
133             log.error("Error occurred while reading publish parameters: {}", e.getMessage());
134             return Mono.just(new ServiceResult(new ResultHeader(500, "Failed to read publish parameters")));
135         }
136     }
137
138     private void setApplicationParameters(Application application, PublishApplicationParam publishApplicationParam) {
139         publishApplicationParam.getNodes().forEach(node -> {
140             if ("chatNode".equals(node.getFlowNodeType())) {
141                 node.getInputs().forEach(input -> {
142                     switch (input.getKey()) {
143                         case "temperature":
144                             input.setValue(application.getTemperature());
145                             break;
146                         case "systemPrompt":
147                             input.setValue(application.getPrompt());
148                             break;
149                         case "model":
150                             log.info(application.getLargeModelName());
151                             input.setValue(application.getLargeModelName());
152                             break;
153                     }
154                 });
155             } else if ("datasetSearchNode".equals(node.getFlowNodeType())) {
156                 node.getInputs().forEach(input -> {
157                     if ("datasets".equals(input.getKey())) {
158                         JSONObject jsonObject = new JSONObject();
159                         jsonObject.put("datasetId", application.getKnowledgeBaseId());
160                         List<JSONObject> list = new ArrayList<>();
161                         list.add(jsonObject);
162                         input.setValue(list);
163                     }
164                 });
165             }
166         });
167     }
168
169     @Override
170     public Flux<String> chat(JSONObject question) {
171         ChatParam chatParam = new ChatParam();
172         chatParam.setAppId(question.getString("applicationId"));
173         chatParam.setStream(true);
174         chatParam.setDetail(true);
175         chatParam.setChatId(UUID.randomUUID().toString());
176         chatParam.setResponseChatItemId(UUID.randomUUID().toString());
177         JSONObject time = new JSONObject();
178         time.put("cTime", TimeUtil.getFormattedDateTime());
179         chatParam.setVariables(time);
180         Message message = new Message();
181         message.setContent(question.getString("question"));
182         message.setDataId(UUID.randomUUID().toString());
183         message.setRole("user");
184         List<Message> messages = new ArrayList<>();
185         messages.add(message);
186         chatParam.setMessages(messages);
187         AtomicBoolean isDone = new AtomicBoolean(false);
188         return webClient.post()
189                 .uri(serverConstant.getFastGptServer() + FastGptConstant.APPLICATION_CHAT_URL)
190                 .contentType(APPLICATION_JSON)
191                 .header(CommonConstant.COOKIE, FastGptConstant.COOKIE_VALUE)
192                 .bodyValue(chatParam)
193                 .retrieve()
194                 .bodyToFlux(String.class).flatMap(response -> parseAndTransform(response, isDone))
195                 .onErrorResume(throwable -> {
196                     log.error("An error occurred {}", throwable.getMessage());
197                     return Flux.just("Network Error");
198                 });
199     }
200
201     private Flux<String> parseAndTransform(String param, AtomicBoolean isDone) {
202         if (isDone.get()) {
203             return Flux.empty();
204         }
205         JSONObject jsonObject = JSONObject.parseObject(param);
206         if (!jsonObject.containsKey("choices")) {
207             return Flux.empty();
208         }
209         JSONArray choices = jsonObject.getJSONArray("choices");
210         JSONObject choice = choices.getJSONObject(0);
211         if ("stop".equals(choice.getString("finish_reason"))) {
212             isDone.set(true);
213             return Flux.just("[DONE]");
214         }
215         String string = choice.getJSONObject("delta").getString("content");
216         isDone.set(false);
217         string = string.replace(" ", "__SPACE__");
218         return Flux.just(string);
219     }
220
221     @Override
222     public Mono<ServiceResult> removeApplication(String applicationId) {
223         String url = serverConstant.getFastGptServer() + FastGptConstant.DELETE_APPLICATION + applicationId;
224         return webClient.delete()
225                 .uri(url)
226                 .header(CommonConstant.COOKIE, FastGptConstant.COOKIE_VALUE)
227                 .retrieve()
228                 .bodyToMono(CreateDataSetResponse.class)
229                 .flatMap(response -> {
230                     if (response.getCode() == 200) {
231                         return Mono.fromRunnable(() -> {
232                             try {
233                                 applicationMapper.deleteApplicationById(applicationId);
234                             } catch (Exception dbException) {
235                                 throw new RuntimeException("Database operation failed", dbException);
236                             }
237                         }).then(Mono.just(new ServiceResult(new ResultHeader(200, "delete success"))));
238                     } else {
239                         return Mono.just(new ServiceResult(new ResultHeader(500, response.getStatusText())));
240                     }
241                 })
242                 .onErrorResume(e -> {
243                     log.error("Error occurred while delete dataset: {}", e.getMessage());
244                     return Mono.just(new ServiceResult(new ResultHeader(500, "delete failed")));
245                 });
246     }
247
248     @Override
249     public Mono<ServiceResult> editApplication(Application application) {
250         String url = serverConstant.getFastGptServer() + FastGptConstant.UPDATE_APPLICATION + application.getApplicationId();
251         UpdateApplicationParam updateApplicationParam = new UpdateApplicationParam();
252         updateApplicationParam.setAvatar("/imgs/app/avatar/simple.svg");
253         updateApplicationParam.setName(application.getApplicationName());
254         updateApplicationParam.setIntro(application.getApplicationDescription());
255
256         return webClient.put()
257                 .uri(url)
258                 .contentType(APPLICATION_JSON)
259                 .header(CommonConstant.COOKIE, FastGptConstant.COOKIE_VALUE)
260                 .bodyValue(updateApplicationParam)
261                 .retrieve()
262                 .bodyToMono(CreateDataSetResponse.class)
263                 .flatMap(response -> {
264                     if (response.getCode() == 200) {
265                         return Mono.fromRunnable(() -> {
266                             applicationMapper.updateApplication(application);
267                         }).then(Mono.just(new ServiceResult(new ResultHeader(200, "edit success"))));
268                     } else {
269                         return Mono.just(new ServiceResult(new ResultHeader(500, response.getStatusText())));
270                     }
271                 })
272                 .onErrorResume(e -> {
273                     log.error("Error occurred while delete dataset: {}", e.getMessage());
274                     return Mono.just(new ServiceResult(new ResultHeader(500, "edit failed")));
275                 });
276     }
277
278 }