Add streaming interface 64/137564/2
authorkaixiliu <liukaixi@chinamobile.com>
Mon, 6 May 2024 08:56:03 +0000 (16:56 +0800)
committerKaixi LIU <liukaixi@chinamobile.com>
Sat, 11 May 2024 01:31:52 +0000 (01:31 +0000)
Issue-ID: USECASEUI-834
Change-Id: I5ffb3324486d3fcff511883b293a87b0c34b97f9
Signed-off-by: kaixiliu <liukaixi@chinamobile.com>
llm-adaptation/pom.xml
llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/constant/LLMConstant.java [new file with mode: 0644]
llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/controller/LlmController.java
llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/service/LlmService.java [new file with mode: 0644]
llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/service/impl/LlmServiceImpl.java [new file with mode: 0644]
llm-adaptation/src/main/resources/application.yaml

index db03490..8917255 100644 (file)
@@ -42,7 +42,7 @@
     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
         </dependency>
         <dependency>
             <groupId>com.nimbusds</groupId>
diff --git a/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/constant/LLMConstant.java b/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/constant/LLMConstant.java
new file mode 100644 (file)
index 0000000..6a55060
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 CMCC, Inc. and others. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.usecaseui.llmadaptation.constant;
+
+public class LLMConstant {
+    // Jiutian Model Key
+    public static final String API_KEY = "65e82b2fa8a3d22f03679898.kTywdU/witQJlHdwgWAI+1thI2UUWfHN";
+
+    // Jiutian Model API Path
+    public static final String LARGE_MODEL_UIL = "http://jiutian.hq.cmcc/largemodel/api/v1/completions?klAssisId=65e6c42ba8a3d22f0366c84d";
+
+    public static final String RESPONSE = "response";
+}
index 120110f..e2779da 100644 (file)
 
 package org.onap.usecaseui.llmadaptation.controller;
 
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import com.nimbusds.jose.JOSEException;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
-import org.onap.usecaseui.llmadaptation.bean.LargeModelRequestParam;
-import org.onap.usecaseui.llmadaptation.util.TokenUtil;
+import org.onap.usecaseui.llmadaptation.service.LlmService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
-
-import java.util.ArrayList;
+import org.springframework.web.bind.annotation.RequestMapping;
+import reactor.core.publisher.Flux;
 
 @Slf4j
 @RestController
+@RequestMapping("/api/usecaseui-llm-adaptation/v1")
 public class LlmController {
+    @Autowired
+    private LlmService llmService;
 
-    @PostMapping(value = "/getHelper")
-    public String getHelp(@RequestBody String question) {
-        String result = "";
-        String url = "http://jiutian.hq.cmcc/largemodel/api/v1/completions?klAssisId=65e6c42ba8a3d22f0366c84d";
-        String apiKey = "65e82b2fa8a3d22f03679898.kTywdU/witQJlHdwgWAI+1thI2UUWfHN";
-        String token;
-        try {
-            token = TokenUtil.generateToken(apiKey, 200000);
-        } catch (JOSEException e) {
-            log.error("error is {}", e.getMessage());
-            return result;
-        }
-        String authorization = "Bearer " + token;
-
-        LargeModelRequestParam helpRequest = new LargeModelRequestParam();
-        helpRequest.setPrompt(question);
-        helpRequest.setReference(false);
-        helpRequest.setStream(false);
-        helpRequest.setHistory(new ArrayList<>());
-        helpRequest.setTemperature(0.01);
-
-        RequestConfig defaultRequestConfig = RequestConfig.custom()
-                .setConnectTimeout(10000)
-                .setConnectionRequestTimeout(10000)
-                .setSocketTimeout(10000)
-                .build();
-        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
-            HttpPost httpPost = new HttpPost(url);
-
-            httpPost.setHeader("Content-Type", "application/json");
-            httpPost.setHeader("Authorization", authorization);
-            httpPost.setConfig(defaultRequestConfig);
-
-            StringEntity requestEntity = new StringEntity(JSON.toJSONString(helpRequest), ContentType.APPLICATION_JSON);
-            httpPost.setEntity(requestEntity);
-
-            HttpResponse response = httpClient.execute(httpPost);
-
-            HttpEntity responseEntity = response.getEntity();
-
-            if (responseEntity != null) {
-                String responseString = EntityUtils.toString(responseEntity, "utf-8");
-                String json = responseString.replaceAll("^data:", "");
-                JSONObject jsonObject = JSON.parseObject(json);
-                result = jsonObject.getString("response");
-                return result;
-            }
-        } catch (Exception e) {
-            log.error("error is {}", e.getMessage());
-        }
-        return result;
+    @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    public Flux<String> streamData(@RequestBody String question) {
+        return llmService.getStream(question);
     }
 }
diff --git a/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/service/LlmService.java b/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/service/LlmService.java
new file mode 100644 (file)
index 0000000..7e18463
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 CMCC, Inc. and others. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.usecaseui.llmadaptation.service;
+
+import reactor.core.publisher.Flux;
+
+public interface LlmService {
+    Flux<String> getStream(String question);
+}
diff --git a/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/service/impl/LlmServiceImpl.java b/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/service/impl/LlmServiceImpl.java
new file mode 100644 (file)
index 0000000..bf751e2
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 CMCC, Inc. and others. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.usecaseui.llmadaptation.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.nimbusds.jose.JOSEException;
+import io.netty.channel.ChannelOption;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.usecaseui.llmadaptation.bean.LargeModelRequestParam;
+import org.onap.usecaseui.llmadaptation.service.LlmService;
+import org.onap.usecaseui.llmadaptation.util.TokenUtil;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Flux;
+import reactor.netty.http.client.HttpClient;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static org.onap.usecaseui.llmadaptation.constant.LLMConstant.RESPONSE;
+import static org.onap.usecaseui.llmadaptation.constant.LLMConstant.API_KEY;
+import static org.onap.usecaseui.llmadaptation.constant.LLMConstant.LARGE_MODEL_UIL;
+
+@Slf4j
+@Service
+public class LlmServiceImpl implements LlmService {
+    @Override
+    public Flux<String> getStream(String question) {
+        LargeModelRequestParam helpRequest = new LargeModelRequestParam();
+        helpRequest.setPrompt(question);
+        helpRequest.setReference(false);
+        helpRequest.setStream(true);
+        helpRequest.setHistory(new ArrayList<>());
+        helpRequest.setTemperature(0.01);
+        Optional<String> token = getToken();
+        if (token.isEmpty()) {
+            return Flux.just("Token Error");
+        }
+        HttpClient httpClient = HttpClient.create()
+                .tcpConfiguration(tcpClient -> tcpClient
+                        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000))
+                .responseTimeout(Duration.ofSeconds(10));
+        WebClient webClient = WebClient.builder()
+                .clientConnector(new ReactorClientHttpConnector(httpClient))
+                .build();
+        return webClient.post()
+                .uri(LARGE_MODEL_UIL)
+                .bodyValue(helpRequest)
+                .header("Authorization", "Bearer " + token.get())
+                .header("Content-Type", "application/json")
+                .retrieve()
+                .bodyToFlux(String.class).flatMap(this::parseAndTransform)
+                .onErrorResume(throwable -> {
+                    log.error("An error occurred", throwable);
+                    return Flux.just("Network Error");
+                });
+    }
+
+    private Optional<String> getToken() {
+        try {
+            String token = TokenUtil.generateToken(API_KEY, 200000);
+            return Optional.of(token);
+        } catch (JOSEException e) {
+            log.error("get token is error,error is {}", e.getMessage());
+        }
+        return Optional.empty();
+    }
+
+    private Flux<String> parseAndTransform(String param) {
+        JSONObject jsonObject = JSON.parseObject(param);
+        if (!jsonObject.containsKey(RESPONSE)) {
+            return Flux.just("Response Error");
+        }
+        String response = jsonObject.getString(RESPONSE);
+        String replace = response.replace("\n", "\\x0A");
+        return Flux.just(replace);
+    }
+}
index e8e1727..f0c46ab 100644 (file)
@@ -1,4 +1,2 @@
 server:
-  port: 8084
-  servlet:
-    context-path: /api/usecaseui-llm-adaptation/v1
+  port: 8084
\ No newline at end of file