sunxy 1 year ago
commit
cb019e218b

+ 34 - 0
.gitignore

@@ -0,0 +1,34 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+.DS_Store
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 26 - 0
README.md

@@ -0,0 +1,26 @@
+# 封装阿里云日志服务sdk
+## 使用方法
+1 引入pom
+```xml
+<dependency>
+    <groupId>com.tzld.commons</groupId>
+    <artifactId>aliyun-log-spring-boot-starter</artifactId>
+    <version>3.0.0</version>
+</dependency>
+```
+2 设置endpoint,accessKeyId,accessKeySecret参数
+```yaml
+aliyun:
+  log:
+    endpoint: 
+    accessKeyId: 
+    accessKeySecret: 
+```
+3 引入aliyunLogManager
+```java
+@Autowired
+AliyunLogManager aliyunLogManager;
+```
+4 调用sendLog发送日志,
+
+5 调用queryLog查询日志,注意:查询日志性能消耗较大,请不要在高并发业务中使用

+ 105 - 0
pom.xml

@@ -0,0 +1,105 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.tzld.commons</groupId>
+	<artifactId>llm-repository-spring-boot-starter</artifactId>
+	<version>3.0.0</version>
+	<packaging>jar</packaging>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-autoconfigure</artifactId>
+			<version>2.4.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter</artifactId>
+			<version>2.4.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+			<version>2.4.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+			<version>1.18.26</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+			<version>1.2.40</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+			<version>3.7</version>
+		</dependency>
+	</dependencies>
+
+	<!-- 配置远程发布到私服,mvn deploy -->
+	<distributionManagement>
+		<repository>
+			<id>stuuudy</id>
+			<name>stuuudy Repository</name>
+			<url>http://nexus.stuuudy.com:9580/nexus/content/repositories/stuuudy/</url>
+		</repository>
+		<snapshotRepository>
+			<id>snapshots</id>
+			<name>Snapshots</name>
+			<url>http://nexus.stuuudy.com:9580/nexus/content/repositories/snapshots/</url>
+		</snapshotRepository>
+	</distributionManagement>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>2.3.2</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+					<encoding>utf8</encoding>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>2.4</version>
+				<configuration>
+					<testFailureIgnore>true</testFailureIgnore>
+					<skip>true</skip>
+					<skipTests>true</skipTests>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-archetype-plugin</artifactId>
+				<version>2.2</version>
+			</plugin>
+			<plugin>
+				<artifactId>maven-source-plugin</artifactId>
+				<version>2.2.1</version>
+				<configuration>
+					<attach>true</attach>
+				</configuration>
+				<executions>
+					<execution>
+						<phase>compile</phase>
+						<goals>
+							<goal>jar</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

+ 70 - 0
src/main/java/com/tzld/commons/llm/azure/gpt/AzureGptManager.java

@@ -0,0 +1,70 @@
+package com.tzld.commons.llm.azure.gpt;
+
+import com.alibaba.fastjson.JSONObject;
+import com.tzld.commons.llm.common.Constant;
+import com.tzld.commons.llm.common.ConversationManager;
+import com.tzld.commons.llm.gpt.param.OpenAiGptParam;
+import com.tzld.commons.llm.gpt.util.AigcUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.util.Objects;
+
+/**
+ * @author sunxy
+ */
+@Slf4j
+@Component(value = Constant.AZURE_GPT_CONVERSATION_MANAGER)
+public class AzureGptManager implements ConversationManager {
+
+    @Resource
+    private RestTemplate restTemplate;
+    @Value("${azure.openai.conversation.url:}")
+    private String conversationUrl;
+
+    @Override
+    public JSONObject conversation(OpenAiGptParam openAiGptParam, String auth) {
+        StopWatch st = StopWatch.createStarted();
+        if (StringUtils.isBlank(auth)) {
+            return null;
+        }
+        openAiGptParam.setModel(null);
+        String requestParams = JSONObject.toJSONString(openAiGptParam);
+        HttpEntity<String> entity = new HttpEntity<>(requestParams,
+                AigcUtils.getAigcHeaders(auth));
+        JSONObject result = doRequestAzureOpenai(requestParams, entity, openAiGptParam.getModel());
+        log.info("request auth {} param {} result {}  cost {}", auth, JSONObject.toJSONString(openAiGptParam),
+                Objects.isNull(result) ? null : result.toJSONString(), st.getTime());
+        return result;
+    }
+
+    private JSONObject doRequestAzureOpenai(String requestParams, HttpEntity<String> entity, String model) {
+        log.info("azure openai chat request param {}", requestParams);
+        JSONObject jsonObject = null;
+        String requestUrl = String.format(conversationUrl, model);
+        for (int i = 0; i < 3; i++) {
+            try {
+                jsonObject = restTemplate.exchange(requestUrl, HttpMethod.POST,
+                        entity, JSONObject.class).getBody();
+                if (jsonObject == null || Objects.nonNull(jsonObject.get("choices"))) {
+                    break;
+                }
+            } catch (Exception e) {
+                log.error("request openAi error ", e);
+            }
+        }
+        return jsonObject;
+    }
+
+    @Override
+    public String chooseAuth() {
+        return null;
+    }
+}

+ 12 - 0
src/main/java/com/tzld/commons/llm/common/Constant.java

@@ -0,0 +1,12 @@
+package com.tzld.commons.llm.common;
+
+/**
+ * @author sunxy
+ */
+public interface Constant {
+
+    String OFFICIAL_GPT_CONVERSATION_MANAGER = "gptConversationManager";
+
+    String AZURE_GPT_CONVERSATION_MANAGER = "azureGptManager";
+
+}

+ 26 - 0
src/main/java/com/tzld/commons/llm/common/ConversationManager.java

@@ -0,0 +1,26 @@
+package com.tzld.commons.llm.common;
+
+import com.alibaba.fastjson.JSONObject;
+import com.tzld.commons.llm.gpt.param.OpenAiGptParam;
+
+/**
+ * @author sunxy
+ */
+public interface ConversationManager {
+    /**
+     * 对话接口
+     *
+     * @param openAiGptParam 对话参数
+     * @param auth           api key
+     * @return 对话结果
+     */
+    JSONObject conversation(OpenAiGptParam openAiGptParam, String auth);
+
+    /**
+     * 选择一个 api key
+     *
+     * @return auth
+     */
+    String chooseAuth();
+
+}

+ 60 - 0
src/main/java/com/tzld/commons/llm/common/call/OpenaiCaller.java

@@ -0,0 +1,60 @@
+package com.tzld.commons.llm.common.call;
+
+import com.alibaba.fastjson.JSONObject;
+import com.tzld.commons.llm.common.Constant;
+import com.tzld.commons.llm.common.ConversationManager;
+import com.tzld.commons.llm.common.enums.LLMPlatformTypeEnum;
+import com.tzld.commons.llm.gpt.param.OpenAiGptParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * @author sunxy
+ */
+@Component
+public class OpenaiCaller {
+
+    @Autowired
+    private Map<String, ConversationManager> conversationManagerMap;
+
+
+    public JSONObject call(OpenAiGptParam param) {
+        // 随机排序, 防止每次都是同一个平台, 取第一个元素
+        Optional<LLMPlatformTypeEnum> platformTypeEnum = Arrays.stream(LLMPlatformTypeEnum.values())
+                .min((o1, o2) -> ThreadLocalRandom.current().nextInt(-1, 2));
+        return platformTypeEnum.map(llmPlatformTypeEnum -> {
+            try {
+                return call(param, llmPlatformTypeEnum);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }).orElse(null);
+    }
+
+    public JSONObject call(OpenAiGptParam param, LLMPlatformTypeEnum platform) throws IllegalAccessException {
+        if (platform == null) {
+            throw new IllegalAccessException("platform can't be null");
+        }
+        ConversationManager conversationManager = null;
+        switch (platform) {
+            case OPENAI_OFFICIAL:
+                conversationManager = conversationManagerMap.get(Constant.OFFICIAL_GPT_CONVERSATION_MANAGER);
+                break;
+            case AZURE_OPENAI:
+                conversationManager = conversationManagerMap.get(Constant.AZURE_GPT_CONVERSATION_MANAGER);
+                break;
+            default:
+                break;
+        }
+        if (conversationManager == null) {
+            return null;
+        }
+        return conversationManager.conversation(param, conversationManager.chooseAuth());
+    }
+
+}

+ 28 - 0
src/main/java/com/tzld/commons/llm/common/enums/LLMPlatformTypeEnum.java

@@ -0,0 +1,28 @@
+package com.tzld.commons.llm.common.enums;
+
+import lombok.Getter;
+
+/**
+ * 大语言模型类型
+ *
+ * @author sunxy
+ */
+@Getter
+public enum LLMPlatformTypeEnum {
+
+    /**
+     * 官方
+     */
+    OPENAI_OFFICIAL("官方"),
+    /**
+     * Azure
+     */
+    AZURE_OPENAI("Azure"),
+    ;
+
+    private final String name;
+
+    LLMPlatformTypeEnum(String name) {
+        this.name = name;
+    }
+}

+ 85 - 0
src/main/java/com/tzld/commons/llm/gpt/GptConversationManager.java

@@ -0,0 +1,85 @@
+package com.tzld.commons.llm.gpt;
+
+import com.alibaba.fastjson.JSONObject;
+import com.tzld.commons.llm.common.Constant;
+import com.tzld.commons.llm.common.ConversationManager;
+import com.tzld.commons.llm.gpt.param.OpenAiGptParam;
+import com.tzld.commons.llm.gpt.util.AigcUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * @author sunxy
+ */
+@Slf4j
+@Component(value = Constant.OFFICIAL_GPT_CONVERSATION_MANAGER)
+public class GptConversationManager implements ConversationManager {
+    @Resource
+    private RestTemplate restTemplate;
+    @Value("${official.openai.conversation.url:}")
+    private String conversationUrl;
+    @Value("${official.openai.auths:")
+    private String auths;
+
+    @Override
+    public JSONObject conversation(OpenAiGptParam openAiGptParam, String auth) {
+        if (openAiGptParam == null || StringUtils.isBlank(openAiGptParam.getModel())
+                || CollectionUtils.isEmpty(openAiGptParam.getMessages())) {
+            return null;
+        }
+        if (StringUtils.isBlank(auth)) {
+            return null;
+        }
+        StopWatch st = StopWatch.createStarted();
+        String requestParams = JSONObject.toJSONString(openAiGptParam);
+        HttpEntity<String> entity = new HttpEntity<>(requestParams,
+                AigcUtils.getAigcHeaders(auth));
+        JSONObject result = doRequestOpenai(requestParams, entity);
+        log.info("request auth {} param {} result {}  cost {}", auth, JSONObject.toJSONString(openAiGptParam),
+                Objects.isNull(result) ? null : result.toJSONString(), st.getTime());
+        return result;
+    }
+
+    private JSONObject doRequestOpenai(String requestParams, HttpEntity<String> entity) {
+        log.info("openai chat request param {}", requestParams);
+        JSONObject jsonObject = null;
+        for (int i = 0; i < 3; i++) {
+            try {
+                jsonObject = restTemplate.exchange(conversationUrl, HttpMethod.POST,
+                        entity, JSONObject.class).getBody();
+                if (jsonObject == null || Objects.nonNull(jsonObject.get("choices"))) {
+                    break;
+                }
+            } catch (Exception e) {
+                log.error("request openAi error ", e);
+            }
+        }
+        return jsonObject;
+    }
+
+    @Override
+    public String chooseAuth() {
+        if (StringUtils.isBlank(auths)) {
+            return null;
+        }
+        String[] authArray = auths.split(",");
+        if (authArray.length == 1) {
+            return authArray[0];
+        }
+        return authArray[ThreadLocalRandom.current().nextInt(authArray.length)];
+    }
+
+
+
+}

+ 16 - 0
src/main/java/com/tzld/commons/llm/gpt/param/AigcGptParam.java

@@ -0,0 +1,16 @@
+package com.tzld.commons.llm.gpt.param;
+
+import lombok.Data;
+
+/**
+ * @author sunxy
+ */
+@Data
+public class AigcGptParam {
+
+    private String auth;
+
+    private OpenAiGptParam openAiGptParam;
+
+
+}

+ 14 - 0
src/main/java/com/tzld/commons/llm/gpt/param/ContentParam.java

@@ -0,0 +1,14 @@
+package com.tzld.commons.llm.gpt.param;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class ContentParam {
+
+    private String role;
+
+    private String content;
+
+}

+ 18 - 0
src/main/java/com/tzld/commons/llm/gpt/param/OpenAiGptParam.java

@@ -0,0 +1,18 @@
+package com.tzld.commons.llm.gpt.param;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Builder
+public class OpenAiGptParam {
+
+    private String model;
+
+    private List<ContentParam> messages;
+
+    private Double temperature;
+
+}

+ 17 - 0
src/main/java/com/tzld/commons/llm/gpt/util/AigcUtils.java

@@ -0,0 +1,17 @@
+package com.tzld.commons.llm.gpt.util;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+/**
+ * @author sunxy
+ */
+public class AigcUtils {
+
+    public static HttpHeaders getAigcHeaders(String authorization) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.add("Authorization", "Bearer " + authorization);
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        return headers;
+    }
+}