diff --git a/openai_fdddf/README.md b/openai_fdddf/README.md new file mode 100644 index 000000000..f3ccd68a6 --- /dev/null +++ b/openai_fdddf/README.md @@ -0,0 +1,51 @@ +# OpenAI连接器 +代码实现支持代理配置的OpenAI连接器 + +## 逻辑详情 + +### listModels + +获取模型列表 + +入参: 无 +出参: List + +### getChatCompletions + +获取聊天 completions + +入参: `List`, String modelId +出参: ChatResponse + + +### getChatCompletionsWithPrompt + +获取聊天 completions + +入参: String prompt, String modelId, +出参: ChatResponse + + +## 使用步骤说明 + +1. 应用集成中导入连接库 +2. 添加连接器并配置参数 + * secretKey String OpenAI密钥 + * customEndpoint String 自定义endpoint,空填写为null + * proxyHost String 代理地址,空填写为null + * proxyPort Integer 代理端口,空填写为0 + * proxyUsername String 代理用户名,空填写为null + * proxyPassword String 代理密码,空填写为null + * proxyType Integer 代理类型 0:http 1:socks +3. 逻辑调用示例截图 + +![img](Snipaste_2024-09-28_21-50-04.jpg) + +## 应用演示链接 + +[使用了本依赖库的制品应用链接] + +https://dev-testdify-qa.app.codewave.163.com/testopenai + + + diff --git a/openai_fdddf/Snipaste_2024-09-28_21-50-04.jpg b/openai_fdddf/Snipaste_2024-09-28_21-50-04.jpg new file mode 100644 index 000000000..2c75c0da6 Binary files /dev/null and b/openai_fdddf/Snipaste_2024-09-28_21-50-04.jpg differ diff --git a/openai_fdddf/jar/nasl-metadata-collector-0.8.0.jar b/openai_fdddf/jar/nasl-metadata-collector-0.8.0.jar new file mode 100644 index 000000000..fffde156d Binary files /dev/null and b/openai_fdddf/jar/nasl-metadata-collector-0.8.0.jar differ diff --git a/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/install.bat b/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/install.bat new file mode 100644 index 000000000..a01a75d94 --- /dev/null +++ b/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/install.bat @@ -0,0 +1 @@ +mvn install:install-file -Dfile="nasl-metadata-maven-plugin-1.3.0.jar" -DpomFile="pom.xml" \ No newline at end of file diff --git a/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/install.sh b/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/install.sh new file mode 100644 index 000000000..a01a75d94 --- /dev/null +++ b/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/install.sh @@ -0,0 +1 @@ +mvn install:install-file -Dfile="nasl-metadata-maven-plugin-1.3.0.jar" -DpomFile="pom.xml" \ No newline at end of file diff --git a/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/nasl-metadata-maven-plugin-1.3.0.jar b/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/nasl-metadata-maven-plugin-1.3.0.jar new file mode 100644 index 000000000..0eaf1ec67 Binary files /dev/null and b/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/nasl-metadata-maven-plugin-1.3.0.jar differ diff --git a/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/pom.xml b/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/pom.xml new file mode 100644 index 000000000..642d8f6b1 --- /dev/null +++ b/openai_fdddf/jar/nasl-metadata-maven-plugin-1.3.0/pom.xml @@ -0,0 +1,74 @@ + + 4.0.0 + + com.netease.lowcode + nasl-metadata-maven-plugin + 1.3.0 + maven-plugin + + Nasl Metadata Maven Plugin + + UTF-8 + + + + + org.apache.maven + maven-plugin-api + 2.0 + + + org.apache.maven + maven-project + 2.0.10 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.2 + provided + + + org.codehaus.plexus + plexus-compiler-manager + 2.8.4 + + + org.codehaus.plexus + plexus-utils + 3.0.8 + + + org.apache.maven.shared + maven-common-artifact-filters + 3.0.1 + + + + org.apache.maven + maven-aether-provider + 3.3.9 + + + + org.eclipse.aether + aether-api + 1.1.0 + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.2 + + nasl-metadata-maven-plugin + true + + + + + diff --git a/openai_fdddf/pom.xml b/openai_fdddf/pom.xml new file mode 100644 index 000000000..67a991dc2 --- /dev/null +++ b/openai_fdddf/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.9.RELEASE + + + + com.fdddf + openai + 1.0.4 + OpenAI连接器 + 支持配置代理的OpenAI连接器 + + + 8 + 8 + UTF-8 + 3.3 + + + + + nasl-metadata-collector + com.netease.lowcode + 0.8.0 + true + system + ${project.basedir}/jar/nasl-metadata-collector-0.8.0.jar + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + org.slf4j + slf4j-api + provided + + + + com.unfbx + chatgpt-java + 1.1.5 + + + junit + junit + + + + + + com.netease.lowcode + nasl-metadata-maven-plugin + 1.3.0 + + false + + + + + archive + + + + + + + \ No newline at end of file diff --git a/openai_fdddf/src/main/java/com/fdddf/openai/ChatMessage.java b/openai_fdddf/src/main/java/com/fdddf/openai/ChatMessage.java new file mode 100644 index 000000000..14d467bcb --- /dev/null +++ b/openai_fdddf/src/main/java/com/fdddf/openai/ChatMessage.java @@ -0,0 +1,29 @@ +package com.fdddf.openai; + +import com.netease.lowcode.core.annotation.NaslStructure; + +@NaslStructure +public class ChatMessage { + + /** + * 角色 user: 用户,system:系统,assistant:Assistant + */ + public String roleName; + + public String content; + + public ChatMessage() {} + + public ChatMessage(String roleName, String content) { + this.content = content; + this.roleName = roleName; + } + + @Override + public String toString() { + return "ChatMessage{" + + "roleName='" + roleName + '\'' + + ", content='" + content + '\'' + + '}'; + } +} diff --git a/openai_fdddf/src/main/java/com/fdddf/openai/ChatResponse.java b/openai_fdddf/src/main/java/com/fdddf/openai/ChatResponse.java new file mode 100644 index 000000000..a5c5368da --- /dev/null +++ b/openai_fdddf/src/main/java/com/fdddf/openai/ChatResponse.java @@ -0,0 +1,30 @@ +package com.fdddf.openai; + +import com.netease.lowcode.core.annotation.NaslStructure; + +import java.util.List; + +@NaslStructure +public class ChatResponse { + public List messages; + public String model; + public Long promptTokens; + public Long completionTokens; + public Long totalTokens; + + public Integer errorCode; + public String errorMessage; + + @Override + public String toString() { + return "ChatResponse{" + + "messages=" + messages + + ", model='" + model + + ", promptTokens=" + promptTokens + + ", completionTokens=" + completionTokens + + ", totalTokens=" + totalTokens + + ", errorCode=" + errorCode + + ", errorMessage='" + errorMessage + + '}'; + } +} diff --git a/openai_fdddf/src/main/java/com/fdddf/openai/ConsoleEventSourceListener.java b/openai_fdddf/src/main/java/com/fdddf/openai/ConsoleEventSourceListener.java new file mode 100644 index 000000000..ba3b47a01 --- /dev/null +++ b/openai_fdddf/src/main/java/com/fdddf/openai/ConsoleEventSourceListener.java @@ -0,0 +1,68 @@ +package com.fdddf.openai; + +import lombok.SneakyThrows; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.sse.EventSourceListener; + +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.function.Function; + +public class ConsoleEventSourceListener extends EventSourceListener { + + public Function onMessageHandler; + public Function onFailureHandler; + public Function onCompleteHandler; + + public CountDownLatch countDownLatch; + + @Override + public void onOpen(okhttp3.sse.EventSource eventSource, Response response) { + System.out.println("EventSource opened!"); + } + + @Override + public void onClosed(okhttp3.sse.EventSource eventSource) { + System.out.println("EventSource closed!"); + countDownLatch.countDown(); + if (onCompleteHandler != null) { + onCompleteHandler.apply(""); + } + } + + @Override + public void onEvent(okhttp3.sse.EventSource eventSource, String id, String type, String data) { +// System.out.println("EventSource received: " + data); + if (data.equals("[DONE]")) { + countDownLatch.countDown(); + return; + } + if (onMessageHandler != null) { + onMessageHandler.apply(data); + } + } + + @SneakyThrows + @Override + public void onFailure(okhttp3.sse.EventSource eventSource, Throwable t, Response response) { + System.out.println("EventSource failed!"); + if (onFailureHandler != null) { + onFailureHandler.apply(t.toString()); + } + if (Objects.isNull(response)) { + countDownLatch.countDown(); + return; + } + ResponseBody body = response.body(); + if (Objects.nonNull(body)) { + System.out.println("Response none null: " + body.string()); + countDownLatch.countDown(); + return; + } else { + System.out.println("Response error: " + t.toString()); + } + eventSource.cancel(); + countDownLatch.countDown(); + } +} diff --git a/openai_fdddf/src/main/java/com/fdddf/openai/OpenAIConnector.java b/openai_fdddf/src/main/java/com/fdddf/openai/OpenAIConnector.java new file mode 100644 index 000000000..0531a0cec --- /dev/null +++ b/openai_fdddf/src/main/java/com/fdddf/openai/OpenAIConnector.java @@ -0,0 +1,150 @@ +package com.fdddf.openai; + +import com.netease.lowcode.core.annotation.NaslConnector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +@NaslConnector(connectorKind = "openAIConnector") +public class OpenAIConnector { + + private static final Logger LCAP_LOGGER = LoggerFactory.getLogger("LCAP_EXTENSION_LOGGER"); + + /** + * 密钥 + */ + private String secretKey; + + /** + * 自定义endpoint + */ + private String customEndpoint; + + /** + * 代理 + */ + private String proxyHost; + + /** + * 代理端口 + */ + private Integer proxyPort = 80; + + /** + * 代理用户名 + */ + private String proxyUsername; + + /** + * 代理用户密码 + */ + private String proxyPassword; + + /** + * 代理类型 0:http 1:socks + */ + private Integer proxyType = 0; + + /** + * 初始化 + * @param secretKey String OpenAI密钥 + * @param customEndpoint String 自定义endpoint,空填写为null + * @param proxyHost String 代理地址,空填写为null + * @param proxyPort Integer 代理端口,空填写为0 + * @param proxyUsername String 代理用户名,空填写为null + * @param proxyPassword String 代理密码,空填写为null + * @param proxyType Integer 代理类型 0:http 1:socks + * @return OpenAIConnector + */ + @NaslConnector.Creator + public OpenAIConnector initBean(String secretKey, String customEndpoint, + String proxyHost, Integer proxyPort, + String proxyUsername, String proxyPassword, Integer proxyType) { + OpenAIConnector openAIConnector = new OpenAIConnector(); + openAIConnector.secretKey = secretKey; + openAIConnector.customEndpoint = customEndpoint.equals("null") ? null : customEndpoint; + openAIConnector.proxyHost = proxyHost.equals("null") ? null : proxyHost; + openAIConnector.proxyPort = proxyPort; + openAIConnector.proxyUsername = proxyUsername.equals("null") ? null : proxyUsername; + openAIConnector.proxyPassword = proxyPassword.equals("null") ? null : proxyPassword; + openAIConnector.proxyType = proxyType; + return openAIConnector; + } + + private OpenAiChat getOpenAiChat() { + return new OpenAiChat(secretKey, customEndpoint, proxyHost, proxyPort, proxyUsername, proxyPassword, proxyType); + } + + /** + * 获取聊天 completions + * @param chatMessages List + * @param modelId String + * @return ChatResponse + */ + @NaslConnector.Logic + public ChatResponse getChatCompletions(List chatMessages, String modelId) { + OpenAiChat openAiChat = getOpenAiChat(); + return openAiChat.getChatCompletions(chatMessages, modelId); + } + + /** + * 获取模型列表 + * @return List + */ + @NaslConnector.Logic + public List listModels() { + try { + OpenAiChat openAiChat = getOpenAiChat(); + return openAiChat.listModelsName(); + } catch (Exception e) { + LCAP_LOGGER.error("listModels error:{}", e.getMessage()); + return null; + } + } + + /** + * 获取 completions + * @param prompt String + * @param modelId String + * @return ChatResponse + */ + @NaslConnector.Logic + public ChatResponse getChatCompletionsWithPrompt(String prompt, String modelId) { + OpenAiChat openAiChat = getOpenAiChat(); + return openAiChat.getChatCompletionsWithPrompt(prompt, modelId); + } + + /** + * 流式获取 completions + * @param prompt String + * @param modelId String + * @param onMessageHandler Function + * @param onFailureHandler Function + * @param onCompleteHandler Function + * @return Boolean + */ + public Boolean getStreamChatCompletions(String prompt, String modelId, + Function onMessageHandler, + Function onFailureHandler, + Function onCompleteHandler) { + OpenAiChat openAiChat = getOpenAiChat(); + openAiChat.getStreamChatCompletions(prompt, modelId, onMessageHandler, onFailureHandler, onCompleteHandler); + return true; + } + + @NaslConnector.Tester + public Boolean test(String secretKey, String customEndpoint, + String proxyHost, Integer proxyPort, + String proxyUsername, String proxyPassword, Integer proxyType) { +// OpenAIConnector openAIConnector = new OpenAIConnector(); +// openAIConnector = openAIConnector.initBean(secretKey, customEndpoint, proxyHost, proxyPort, proxyUsername, proxyPassword, proxyType); +// List models = openAIConnector.listModels(); +// return !models.isEmpty(); + + return Objects.nonNull(secretKey) && !secretKey.isEmpty(); + } + +} diff --git a/openai_fdddf/src/main/java/com/fdddf/openai/OpenAiChat.java b/openai_fdddf/src/main/java/com/fdddf/openai/OpenAiChat.java new file mode 100644 index 000000000..2973297b1 --- /dev/null +++ b/openai_fdddf/src/main/java/com/fdddf/openai/OpenAiChat.java @@ -0,0 +1,237 @@ +package com.fdddf.openai; + +import com.unfbx.chatgpt.OpenAiClient; +import com.unfbx.chatgpt.OpenAiStreamClient; +import com.unfbx.chatgpt.entity.chat.*; +import com.unfbx.chatgpt.entity.common.Usage; +import com.unfbx.chatgpt.entity.models.Model; +import okhttp3.*; +import org.jetbrains.annotations.NotNull; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class OpenAiChat { + private String secretKey; + + /** + * 自定义endpoint + */ + private String customEndpoint; + + /** + * 代理 + */ + private String proxyHost; + + /** + * 代理端口 + */ + private Integer proxyPort; + + /** + * 代理用户名 + */ + private String proxyUsername; + + /** + * 代理用户密码 + */ + private String proxyPassword; + + /** + * 代理类型 0:http 1:socks + */ + private Integer proxyType = 0; + + public OpenAiChat(String secretKey, String customEndpoint, String proxyHost, + Integer proxyPort, String proxyUsername, String proxyPassword, + Integer proxyType ) { + this.secretKey = secretKey; + this.customEndpoint = customEndpoint; + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.proxyUsername = proxyUsername; + this.proxyPassword = proxyPassword; + this.proxyType = proxyType; + } + + public OpenAiClient getClient(String secretKey, String customEndpoint) { + OkHttpClient okHttpClient = getOkHttpClient(); + + OpenAiClient.Builder clientBuilder = OpenAiClient.builder() + .apiKey(Collections.singletonList(secretKey)) + .okHttpClient(okHttpClient); + if (customEndpoint != null && !customEndpoint.isEmpty()) { + clientBuilder.apiHost(customEndpoint); + } + return clientBuilder.build(); + } + + private OkHttpClient getOkHttpClient() { + OkHttpClient.Builder builder = getBuilder(); + return builder.connectTimeout(30, TimeUnit.SECONDS)//自定义超时时间 + .writeTimeout(30, TimeUnit.SECONDS)//自定义超时时间 + .readTimeout(30, TimeUnit.SECONDS)//自定义超时时间 + .build(); + } + + public OpenAiStreamClient getStreamClient(String secretKey, String customEndpoint) { + OkHttpClient okHttpClient = getOkHttpClient(); + + OpenAiStreamClient.Builder clientBuilder = OpenAiStreamClient.builder() + .apiKey(Collections.singletonList(secretKey)) + .okHttpClient(okHttpClient); + if (customEndpoint != null && !customEndpoint.isEmpty()) { + clientBuilder.apiHost(customEndpoint); + } + return clientBuilder.build(); + } + + @NotNull + private OkHttpClient.Builder getBuilder() { + Proxy proxy = getProxy(); + + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + if (proxy != null) { + builder.proxy(proxy); + + if (proxyUsername != null && !proxyUsername.isEmpty()) { + Authenticator proxyAuthenticator = (route, response) -> { + String credential = Credentials.basic(proxyUsername, proxyPassword); + return response.request().newBuilder().header("Proxy-Authorization", credential).build(); + }; + builder.proxyAuthenticator(proxyAuthenticator); + } + + + } + return builder; + } + + private Proxy getProxy() { + Proxy proxy = null; + Proxy.Type type = Proxy.Type.DIRECT; + if (proxyHost != null && !proxyHost.isEmpty()) { + if (proxyType == 0) { + type = Proxy.Type.HTTP; + } else if (proxyType == 1) { + type = Proxy.Type.SOCKS; + } + proxy = new Proxy(type, new InetSocketAddress(proxyHost, proxyPort)); + } + return proxy; + } + + public ChatResponse getChatCompletions(List chatMessages, String modelId) { + OpenAiClient client = getClient(secretKey, customEndpoint); + + List messages = toChatMessages(chatMessages); + ChatCompletion chatCompletion = ChatCompletion + .builder() + .messages(messages) + .model(modelId) + .build(); + + ChatResponse response = new ChatResponse(); + + try { + ChatCompletionResponse chatCompletionResponse = client.chatCompletion(chatCompletion); + System.out.printf("Model ID=%s is created at %s.%n", chatCompletionResponse.getId(), chatCompletionResponse.getCreated()); + + response.messages = new ArrayList<>(); + for (ChatChoice choice : chatCompletionResponse.getChoices()) { + Message message = choice.getMessage(); + response.messages.add(fromOpenAIChatResponseMessage(message)); + } + + Usage usage = chatCompletionResponse.getUsage(); + System.out.printf("Usage: number of prompt token is %d, " + + "number of completion token is %d, and number of total tokens in request and response is %d.%n", + usage.getPromptTokens(), usage.getCompletionTokens(), usage.getTotalTokens()); + + response.model = chatCompletionResponse.getModel(); + response.promptTokens = usage.getPromptTokens(); + response.completionTokens = usage.getCompletionTokens(); + response.totalTokens = usage.getTotalTokens(); + } catch (Exception e) { + e.printStackTrace(); + response.errorCode = 1; + response.errorMessage = e.getMessage(); + } + + return response; + } + + public static Message toOpenAIChatRequestMessage(ChatMessage message) + { + Message.Role role = Message.Role.USER; + if (message.roleName.equals("assistant")) + role = Message.Role.ASSISTANT; + if (message.roleName.equals("system")) + role = Message.Role.SYSTEM; + + return Message.builder().role(role).content(message.content).build(); + } + + public ChatMessage fromOpenAIChatResponseMessage(Message message) + { + String role = message.getRole(); + return new ChatMessage(role, message.getContent()); + } + + public List toChatMessages(List chatMessages) { + return chatMessages.stream().map(OpenAiChat::toOpenAIChatRequestMessage).collect(Collectors.toList()); + } + + + public List listModels() { + OpenAiClient openAiChat = getClient(secretKey, customEndpoint); + return openAiChat.models(); + } + + public List listModelsName() { + return listModels().stream().map(Model::getId).collect(Collectors.toList()); + } + + public ChatResponse getChatCompletionsWithPrompt(String prompt, String modelId) { + return getChatCompletions(Collections.singletonList(new ChatMessage("user", prompt)), modelId); + } + + public void getStreamChatCompletions(String prompt, String modelId, + Function onMessageHandler, + Function onFailureHandler, + Function onCompleteHandler) { + OpenAiStreamClient openAiStreamClient = getStreamClient(secretKey, customEndpoint); + + ConsoleEventSourceListener listener = new ConsoleEventSourceListener(); + CountDownLatch countDownLatch = new CountDownLatch(1); + listener.onMessageHandler = onMessageHandler; + listener.onFailureHandler = onFailureHandler; + listener.onCompleteHandler = onCompleteHandler; + listener.countDownLatch = countDownLatch; + + Message message = Message.builder().role(Message.Role.USER).content(prompt).build(); + ChatCompletion chatCompletion = ChatCompletion.builder(). + messages(Collections.singletonList(message)) + .model(modelId) + .build(); + + openAiStreamClient.streamChatCompletion(chatCompletion, listener); + + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Stream chat completed."); + } + +} diff --git a/openai_fdddf/src/main/java/com/fdddf/openai/TestChat.java b/openai_fdddf/src/main/java/com/fdddf/openai/TestChat.java new file mode 100644 index 000000000..ea8b40250 --- /dev/null +++ b/openai_fdddf/src/main/java/com/fdddf/openai/TestChat.java @@ -0,0 +1,67 @@ +package com.fdddf.openai; + +import org.jetbrains.annotations.TestOnly; +import org.junit.Test; + +@TestOnly +public class TestChat { + + private static final String OPENAI_API_KEY = "sk-xxxxxxxxxxxxxxxx"; + + @Test + public void testProxy() { + OpenAiChat openAiChat = new OpenAiChat( + OPENAI_API_KEY, + null, + "192.168.1.50", + 1089, + null, + null, + 0 + ); + openAiChat.listModelsName().forEach(System.out::println); + } + + @Test + public void testCompletions() { + OpenAiChat openAiChat = new OpenAiChat( + OPENAI_API_KEY, + "https://api.apiyi.com/", + null, //null, "127.0.0.1" + 0, + null, + null, + 0 + ); + ChatResponse response = openAiChat.getChatCompletionsWithPrompt("How to say thank you in French", "gpt-3.5-turbo"); + System.out.println(response); + response.messages.forEach(System.out::println); + } + + @Test + public void testCompletionsWithCallback() { + OpenAiChat openAiChat = new OpenAiChat( + OPENAI_API_KEY, + "https://api.apiyi.com/", + null, //null, "127.0.0.1" + 0, + null, + null, + 0 + ); + openAiChat.getStreamChatCompletions("How to say thank you in French", "gpt-3.5-turbo", + (String message) -> { + System.out.println(message); + return null; + }, null, null); + } + + @Test + public void testConnection() { + OpenAIConnector connector = new OpenAIConnector(); + Boolean result = connector.test(OPENAI_API_KEY, "https://api.apiyi.com/", "null", 1080, "null", "null", 0); + System.out.println(result); + } +} + + diff --git "a/openai_fdddf/\344\276\235\350\265\226\345\272\223\344\275\277\347\224\250\346\226\207\346\241\243\350\257\264\346\230\216.docx" "b/openai_fdddf/\344\276\235\350\265\226\345\272\223\344\275\277\347\224\250\346\226\207\346\241\243\350\257\264\346\230\216.docx" new file mode 100644 index 000000000..604c685d6 Binary files /dev/null and "b/openai_fdddf/\344\276\235\350\265\226\345\272\223\344\275\277\347\224\250\346\226\207\346\241\243\350\257\264\346\230\216.docx" differ