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