diff options
author | kaixiliu <liukaixi@chinamobile.com> | 2024-05-06 16:56:03 +0800 |
---|---|---|
committer | Kaixi LIU <liukaixi@chinamobile.com> | 2024-05-11 01:31:52 +0000 |
commit | 8b1c6bb2a063b0c4294f8ea2a54420b28da5df4d (patch) | |
tree | 5154c871db3768224bf0ad1bc2f060456587982b | |
parent | 826f025a08a831a53c5e290fedb0911931c1b680 (diff) |
Add streaming interface
Issue-ID: USECASEUI-834
Change-Id: I5ffb3324486d3fcff511883b293a87b0c34b97f9
Signed-off-by: kaixiliu <liukaixi@chinamobile.com>
6 files changed, 158 insertions, 71 deletions
diff --git a/llm-adaptation/pom.xml b/llm-adaptation/pom.xml index db03490..8917255 100644 --- a/llm-adaptation/pom.xml +++ b/llm-adaptation/pom.xml @@ -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 index 0000000..6a55060 --- /dev/null +++ b/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/constant/LLMConstant.java @@ -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"; +} diff --git a/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/controller/LlmController.java b/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/controller/LlmController.java index 120110f..e2779da 100644 --- a/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/controller/LlmController.java +++ b/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/controller/LlmController.java @@ -16,81 +16,25 @@ 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 index 0000000..7e18463 --- /dev/null +++ b/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/service/LlmService.java @@ -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 index 0000000..bf751e2 --- /dev/null +++ b/llm-adaptation/src/main/java/org/onap/usecaseui/llmadaptation/service/impl/LlmServiceImpl.java @@ -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); + } +} diff --git a/llm-adaptation/src/main/resources/application.yaml b/llm-adaptation/src/main/resources/application.yaml index e8e1727..f0c46ab 100644 --- a/llm-adaptation/src/main/resources/application.yaml +++ b/llm-adaptation/src/main/resources/application.yaml @@ -1,4 +1,2 @@ server: - port: 8084 - servlet: - context-path: /api/usecaseui-llm-adaptation/v1 + port: 8084
\ No newline at end of file |