package com.master.chat.llm.openai;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.master.chat.llm.openai.constant.OpenAIConst;
import com.master.chat.llm.openai.entity.Tts.TextToSpeech;
import com.master.chat.llm.openai.entity.assistant.message.MessageFileResponse;
import com.master.chat.llm.openai.entity.assistant.message.MessageResponse;
import com.master.chat.llm.openai.entity.assistant.message.ModifyMessage;
import com.master.chat.llm.openai.entity.assistant.thread.ModifyThread;
import com.master.chat.llm.openai.entity.assistant.thread.Thread;
import com.master.chat.llm.openai.entity.assistant.thread.ThreadMessage;
import com.master.chat.llm.openai.entity.assistant.thread.ThreadResponse;
import com.master.chat.llm.openai.entity.billing.BillingUsage;
import com.master.chat.llm.openai.entity.billing.CreditGrantsResponse;
import com.master.chat.llm.openai.entity.billing.Subscription;
import com.master.chat.llm.openai.entity.chat.ChatChoice;
import com.master.chat.llm.openai.entity.chat.ChatCompletion;
import com.master.chat.llm.openai.entity.chat.Message;
import com.master.chat.llm.openai.entity.common.DeleteResponse;
import com.master.chat.llm.openai.entity.common.OpenAiResponse;
import com.master.chat.llm.openai.entity.common.PageRequest;
import com.master.chat.llm.openai.entity.completions.Completion;
import com.master.chat.llm.openai.entity.completions.CompletionResponse;
import com.master.chat.llm.openai.entity.edits.Edit;
import com.master.chat.llm.openai.entity.edits.EditResponse;
import com.master.chat.llm.openai.entity.embeddings.Embedding;
import com.master.chat.llm.openai.entity.embeddings.EmbeddingResponse;
import com.master.chat.llm.openai.entity.engines.Engine;
import com.master.chat.llm.openai.entity.files.File;
import com.master.chat.llm.openai.entity.files.UploadFileResponse;
import com.master.chat.llm.openai.entity.fineTune.Event;
import com.master.chat.llm.openai.entity.fineTune.FineTune;
import com.master.chat.llm.openai.entity.fineTune.FineTuneDeleteResponse;
import com.master.chat.llm.openai.entity.fineTune.FineTuneResponse;
import com.master.chat.llm.openai.entity.fineTune.job.FineTuneJob;
import com.master.chat.llm.openai.entity.fineTune.job.FineTuneJobEvent;
import com.master.chat.llm.openai.entity.fineTune.job.FineTuneJobListResponse;
import com.master.chat.llm.openai.entity.fineTune.job.FineTuneJobResponse;
import com.master.chat.llm.openai.entity.images.Image;
import com.master.chat.llm.openai.entity.images.ImageResponse;
import com.master.chat.llm.openai.entity.images.Item;
import com.master.chat.llm.openai.entity.models.Model;
import com.master.chat.llm.openai.entity.models.ModelResponse;
import com.master.chat.llm.openai.entity.moderations.Moderation;
import com.master.chat.llm.openai.entity.moderations.ModerationResponse;
import com.master.chat.llm.openai.entity.whisper.Transcriptions;
import com.master.chat.llm.openai.entity.whisper.Translations;
import com.master.chat.llm.openai.entity.whisper.WhisperResponse;
import com.master.chat.llm.openai.exception.BaseException;
import com.master.chat.llm.openai.exception.CommonError;
import com.master.chat.llm.openai.function.KeyRandomStrategy;
import com.master.chat.llm.openai.function.KeyStrategyFunction;
import com.master.chat.llm.openai.interceptor.DefaultOpenAiAuthInterceptor;
import com.master.chat.llm.openai.interceptor.DynamicKeyOpenAiAuthInterceptor;
import com.master.chat.llm.openai.interceptor.OpenAiAuthInterceptor;
import com.master.chat.llm.openai.plugin.PluginAbstract;
import com.master.chat.llm.openai.plugin.PluginParam;
import com.master.chat.llm.openai.utils.SSEUtil;
import io.reactivex.Single;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okhttp3.sse.EventSourceListener;
import org.jetbrains.annotations.NotNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* 描述: open ai 客户端
*/
@Slf4j
public class OpenAiClient {
/**
* keys
*/
@Getter
@NotNull
private List<String> apiKey;
/**
* 自定义api host使用builder的方式构造client
*/
@Getter
private String apiHost;
@Getter
private OpenAiApi openAiApi;
/**
* 自定义的okHttpClient
* 如果不自定义 ,就是用sdk默认的OkHttpClient实例
*/
@Getter
private OkHttpClient okHttpClient;
/**
* api key的获取策略
*/
@Getter
private KeyStrategyFunction<List<String>, String> keyStrategy;
/**
* 自定义鉴权处理拦截器<br/>
* 可以不设置,默认实现:DefaultOpenAiAuthInterceptor <br/>
* 如需自定义实现参考:DealKeyWithOpenAiAuthInterceptor
*
* @see DynamicKeyOpenAiAuthInterceptor
* @see DefaultOpenAiAuthInterceptor
*/
@Getter
private OpenAiAuthInterceptor authInterceptor;
/**
* 默认的分页参数
*/
@Getter
private PageRequest pageRequest = PageRequest.builder().build();
@Getter
private static final Headers assistantsHeader = Headers.of("OpenAI-Beta", "assistants=v1");
/**
* 构造器
*
* @return OpenAiClient.Builder
*/
public static Builder builder() {
return new Builder();
}
public OpenAiClient() {
}
/**
* 构造
*
* @param builder
*/
private OpenAiClient(Builder builder) {
if (CollectionUtil.isEmpty(builder.apiKey)) {
throw new BaseException(CommonError.API_KEYS_NOT_NUL);
}
apiKey = builder.apiKey;
if (StrUtil.isBlank(builder.apiHost)) {
builder.apiHost = OpenAIConst.OPENAI_HOST;
}
apiHost = builder.apiHost;
if (Objects.isNull(builder.keyStrategy)) {
builder.keyStrategy = new KeyRandomStrategy();
}
keyStrategy = builder.keyStrategy;
if (Objects.isNull(builder.authInterceptor)) {
builder.authInterceptor = new DefaultOpenAiAuthInterceptor();
}
authInterceptor = builder.authInterceptor;
authInterceptor.setApiKey(this.apiKey);
authInterceptor.setKeyStrategy(this.keyStrategy);
if (Objects.isNull(builder.okHttpClient)) {
builder.okHttpClient = this.okHttpClient();
} else {
//自定义的okhttpClient 需要增加api keys
builder.okHttpClient = builder.okHttpClient
.newBuilder()
.addInterceptor(authInterceptor)
.build();
}
okHttpClient = builder.okHttpClient;
this.openAiApi = new Retrofit.Builder()
.baseUrl(apiHost)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(JacksonConverterFactory.create())
.build().create(OpenAiApi.class);
}
/**
* 创建默认OkHttpClient
*
* @return
*/
private OkHttpClient okHttpClient() {
if (Objects.isNull(this.authInterceptor)) {
this.authInterceptor = new DefaultOpenAiAuthInterceptor();
}
this.authInterceptor.setApiKey(this.apiKey);
this.authInterceptor.setKeyStrategy(this.keyStrategy);
return new OkHttpClient
.Builder()
.addInterceptor(this.authInterceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS).build();
}
/**
* openAi模型列表
*
* @return Model list
*/
public List<Model> models() {
Single<ModelRespons