001/* 002 * Copyright (c) 2023-2025, Agents-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.agentsflex.llm.openai; 017 018import com.agentsflex.core.document.Document; 019import com.agentsflex.core.llm.BaseLlm; 020import com.agentsflex.core.llm.ChatOptions; 021import com.agentsflex.core.llm.StreamResponseListener; 022import com.agentsflex.core.llm.client.BaseLlmClientListener; 023import com.agentsflex.core.llm.client.HttpClient; 024import com.agentsflex.core.llm.client.LlmClient; 025import com.agentsflex.core.llm.client.LlmClientListener; 026import com.agentsflex.core.llm.client.impl.SseClient; 027import com.agentsflex.core.llm.embedding.EmbeddingOptions; 028import com.agentsflex.core.llm.response.AiMessageResponse; 029import com.agentsflex.core.parser.AiMessageParser; 030import com.agentsflex.core.prompt.Prompt; 031import com.agentsflex.core.store.VectorData; 032import com.agentsflex.core.util.StringUtil; 033import com.alibaba.fastjson.JSON; 034import com.alibaba.fastjson.JSONObject; 035import com.alibaba.fastjson.JSONPath; 036 037import java.util.HashMap; 038import java.util.Map; 039import java.util.function.Consumer; 040 041public class OpenAILlm extends BaseLlm<OpenAILlmConfig> { 042 043 private final HttpClient httpClient = new HttpClient(); 044 public AiMessageParser aiMessageParser = OpenAILlmUtil.getAiMessageParser(false); 045 public AiMessageParser streamMessageParser = OpenAILlmUtil.getAiMessageParser(true); 046 047 public static OpenAILlm of(String apiKey) { 048 OpenAILlmConfig config = new OpenAILlmConfig(); 049 config.setApiKey(apiKey); 050 return new OpenAILlm(config); 051 } 052 053 public static OpenAILlm of(String apiKey, String endpoint) { 054 OpenAILlmConfig config = new OpenAILlmConfig(); 055 config.setApiKey(apiKey); 056 config.setEndpoint(endpoint); 057 return new OpenAILlm(config); 058 } 059 060 public OpenAILlm(OpenAILlmConfig config) { 061 super(config); 062 } 063 064 @Override 065 public AiMessageResponse chat(Prompt prompt, ChatOptions options) { 066 Map<String, String> headers = new HashMap<>(); 067 headers.put("Content-Type", "application/json"); 068 headers.put("Authorization", "Bearer " + getConfig().getApiKey()); 069 070 Consumer<Map<String, String>> headersConfig = config.getHeadersConfig(); 071 if (headersConfig != null) { 072 headersConfig.accept(headers); 073 } 074 075 String payload = OpenAILlmUtil.promptToPayload(prompt, config, options, false); 076 String endpoint = config.getEndpoint(); 077 String response = httpClient.post(endpoint + "/v1/chat/completions", headers, payload); 078 079 if (config.isDebug()) { 080 System.out.println(">>>>receive payload:" + response); 081 } 082 083 if (StringUtil.noText(response)) { 084 return AiMessageResponse.error(prompt, response, "no content for response."); 085 } 086 087 JSONObject jsonObject = JSON.parseObject(response); 088 JSONObject error = jsonObject.getJSONObject("error"); 089 090 AiMessageResponse messageResponse = new AiMessageResponse(prompt, response, aiMessageParser.parse(jsonObject)); 091 092 if (error != null && !error.isEmpty()) { 093 messageResponse.setError(true); 094 messageResponse.setErrorMessage(error.getString("message")); 095 messageResponse.setErrorType(error.getString("type")); 096 messageResponse.setErrorCode(error.getString("code")); 097 } 098 099 return messageResponse; 100 } 101 102 103 @Override 104 public void chatStream(Prompt prompt, StreamResponseListener listener, ChatOptions options) { 105 LlmClient llmClient = new SseClient(); 106 Map<String, String> headers = new HashMap<>(); 107 headers.put("Content-Type", "application/json"); 108 headers.put("Authorization", "Bearer " + getConfig().getApiKey()); 109 110 String payload = OpenAILlmUtil.promptToPayload(prompt, config, options, true); 111 String endpoint = config.getEndpoint(); 112 LlmClientListener clientListener = new BaseLlmClientListener(this, llmClient, listener, prompt, streamMessageParser); 113 llmClient.start(endpoint + "/v1/chat/completions", headers, payload, clientListener, config); 114 } 115 116 117 @Override 118 public VectorData embed(Document document, EmbeddingOptions options) { 119 Map<String, String> headers = new HashMap<>(); 120 headers.put("Content-Type", "application/json"); 121 headers.put("Authorization", "Bearer " + getConfig().getApiKey()); 122 123 String payload = OpenAILlmUtil.promptToEmbeddingsPayload(document, options, config); 124 String endpoint = config.getEndpoint(); 125 // https://platform.openai.com/docs/api-reference/embeddings/create 126 String response = httpClient.post(endpoint + "/v1/embeddings", headers, payload); 127 128 if (config.isDebug()) { 129 System.out.println(">>>>receive payload:" + response); 130 } 131 132 if (StringUtil.noText(response)) { 133 return null; 134 } 135 136 VectorData vectorData = new VectorData(); 137 double[] embedding = JSONPath.read(response, "$.data[0].embedding", double[].class); 138 vectorData.setVector(embedding); 139 140 return vectorData; 141 } 142 143 144}